Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

isofs: fix Y2038 and Y2156 issues in Rock Ridge TF entry

This change implements the Rock Ridge TF entry LONG_FORM bit, which uses
the ISO 9660 17-byte date format (up to year 9999, with 10ms precision)
instead of the 7-byte date format (up to year 2155, with 1s precision).

Previously the LONG_FORM bit was ignored; and isofs would entirely
misinterpret the date as the wrong format, resulting in garbage
timestamps on the filesystem.

The Y2038 issue in iso_date() is fixed by returning a struct timespec64
instead of an int.

parse_rock_ridge_inode_internal() is fixed so it does proper bounds
checks of the TF entry timestamps.

Signed-off-by: Jonas 'Sortie' Termansen <sortie@maxsi.org>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20250411145022.2292255-1-sortie@maxsi.org

authored by

Jonas 'Sortie' Termansen and committed by
Jan Kara
5ea45f54 0405d4b6

+65 -43
+5 -2
fs/isofs/inode.c
··· 1275 1275 unsigned long offset; 1276 1276 struct iso_inode_info *ei = ISOFS_I(inode); 1277 1277 int ret = -EIO; 1278 + struct timespec64 ts; 1278 1279 1279 1280 block = ei->i_iget5_block; 1280 1281 bh = sb_bread(inode->i_sb, block); ··· 1388 1387 inode->i_ino, de->flags[-high_sierra]); 1389 1388 } 1390 1389 #endif 1391 - inode_set_mtime_to_ts(inode, 1392 - inode_set_atime_to_ts(inode, inode_set_ctime(inode, iso_date(de->date, high_sierra), 0))); 1390 + ts = iso_date(de->date, high_sierra ? ISO_DATE_HIGH_SIERRA : 0); 1391 + inode_set_ctime_to_ts(inode, ts); 1392 + inode_set_atime_to_ts(inode, ts); 1393 + inode_set_mtime_to_ts(inode, ts); 1393 1394 1394 1395 ei->i_first_extent = (isonum_733(de->extent) + 1395 1396 isonum_711(de->ext_attr_length));
+3 -1
fs/isofs/isofs.h
··· 106 106 /* Ignore bigendian datum due to broken mastering programs */ 107 107 return get_unaligned_le32(p); 108 108 } 109 - extern int iso_date(u8 *, int); 109 + #define ISO_DATE_HIGH_SIERRA (1 << 0) 110 + #define ISO_DATE_LONG_FORM (1 << 1) 111 + struct timespec64 iso_date(u8 *p, int flags); 110 112 111 113 struct inode; /* To make gcc happy */ 112 114
+23 -17
fs/isofs/rock.c
··· 412 412 } 413 413 } 414 414 break; 415 - case SIG('T', 'F'): 415 + case SIG('T', 'F'): { 416 + int flags, size, slen; 417 + 418 + flags = rr->u.TF.flags & TF_LONG_FORM ? ISO_DATE_LONG_FORM : 0; 419 + size = rr->u.TF.flags & TF_LONG_FORM ? 17 : 7; 420 + slen = rr->len - 5; 416 421 /* 417 422 * Some RRIP writers incorrectly place ctime in the 418 423 * TF_CREATE field. Try to handle this correctly for ··· 425 420 */ 426 421 /* Rock ridge never appears on a High Sierra disk */ 427 422 cnt = 0; 428 - if (rr->u.TF.flags & TF_CREATE) { 429 - inode_set_ctime(inode, 430 - iso_date(rr->u.TF.times[cnt++].time, 0), 431 - 0); 423 + if ((rr->u.TF.flags & TF_CREATE) && size <= slen) { 424 + inode_set_ctime_to_ts(inode, 425 + iso_date(rr->u.TF.data + size * cnt++, flags)); 426 + slen -= size; 432 427 } 433 - if (rr->u.TF.flags & TF_MODIFY) { 434 - inode_set_mtime(inode, 435 - iso_date(rr->u.TF.times[cnt++].time, 0), 436 - 0); 428 + if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) { 429 + inode_set_mtime_to_ts(inode, 430 + iso_date(rr->u.TF.data + size * cnt++, flags)); 431 + slen -= size; 437 432 } 438 - if (rr->u.TF.flags & TF_ACCESS) { 439 - inode_set_atime(inode, 440 - iso_date(rr->u.TF.times[cnt++].time, 0), 441 - 0); 433 + if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) { 434 + inode_set_atime_to_ts(inode, 435 + iso_date(rr->u.TF.data + size * cnt++, flags)); 436 + slen -= size; 442 437 } 443 - if (rr->u.TF.flags & TF_ATTRIBUTES) { 444 - inode_set_ctime(inode, 445 - iso_date(rr->u.TF.times[cnt++].time, 0), 446 - 0); 438 + if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) { 439 + inode_set_ctime_to_ts(inode, 440 + iso_date(rr->u.TF.data + size * cnt++, flags)); 441 + slen -= size; 447 442 } 448 443 break; 444 + } 449 445 case SIG('S', 'L'): 450 446 { 451 447 int slen;
+1 -5
fs/isofs/rock.h
··· 65 65 __u8 location[8]; 66 66 }; 67 67 68 - struct stamp { 69 - __u8 time[7]; /* actually 6 unsigned, 1 signed */ 70 - } __attribute__ ((packed)); 71 - 72 68 struct RR_TF_s { 73 69 __u8 flags; 74 - struct stamp times[]; /* Variable number of these beasts */ 70 + __u8 data[]; 75 71 } __attribute__ ((packed)); 76 72 77 73 /* Linux-specific extension for transparent decompression */
+33 -18
fs/isofs/util.c
··· 16 16 * to GMT. Thus we should always be correct. 17 17 */ 18 18 19 - int iso_date(u8 *p, int flag) 19 + struct timespec64 iso_date(u8 *p, int flags) 20 20 { 21 21 int year, month, day, hour, minute, second, tz; 22 - int crtime; 22 + struct timespec64 ts; 23 23 24 - year = p[0]; 25 - month = p[1]; 26 - day = p[2]; 27 - hour = p[3]; 28 - minute = p[4]; 29 - second = p[5]; 30 - if (flag == 0) tz = p[6]; /* High sierra has no time zone */ 31 - else tz = 0; 32 - 33 - if (year < 0) { 34 - crtime = 0; 24 + if (flags & ISO_DATE_LONG_FORM) { 25 + year = (p[0] - '0') * 1000 + 26 + (p[1] - '0') * 100 + 27 + (p[2] - '0') * 10 + 28 + (p[3] - '0') - 1900; 29 + month = ((p[4] - '0') * 10 + (p[5] - '0')); 30 + day = ((p[6] - '0') * 10 + (p[7] - '0')); 31 + hour = ((p[8] - '0') * 10 + (p[9] - '0')); 32 + minute = ((p[10] - '0') * 10 + (p[11] - '0')); 33 + second = ((p[12] - '0') * 10 + (p[13] - '0')); 34 + ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000; 35 + tz = p[16]; 35 36 } else { 36 - crtime = mktime64(year+1900, month, day, hour, minute, second); 37 + year = p[0]; 38 + month = p[1]; 39 + day = p[2]; 40 + hour = p[3]; 41 + minute = p[4]; 42 + second = p[5]; 43 + ts.tv_nsec = 0; 44 + /* High sierra has no time zone */ 45 + tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6]; 46 + } 47 + 48 + if (year < 0) { 49 + ts.tv_sec = 0; 50 + } else { 51 + ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second); 37 52 38 53 /* sign extend */ 39 54 if (tz & 0x80) 40 55 tz |= (-1 << 8); 41 - 56 + 42 57 /* 43 58 * The timezone offset is unreliable on some disks, 44 59 * so we make a sanity check. In no case is it ever ··· 80 65 * for pointing out the sign error. 81 66 */ 82 67 if (-52 <= tz && tz <= 52) 83 - crtime -= tz * 15 * 60; 68 + ts.tv_sec -= tz * 15 * 60; 84 69 } 85 - return crtime; 86 - } 70 + return ts; 71 + }