]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
isofs: fix Y2038 and Y2156 issues in Rock Ridge TF entry
authorJonas 'Sortie' Termansen <sortie@maxsi.org>
Fri, 11 Apr 2025 14:50:21 +0000 (16:50 +0200)
committerJan Kara <jack@suse.cz>
Tue, 15 Apr 2025 09:56:57 +0000 (11:56 +0200)
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
fs/isofs/inode.c
fs/isofs/isofs.h
fs/isofs/rock.c
fs/isofs/rock.h
fs/isofs/util.c

index 47038e6608123c517239bdbdd4c451842939291a..d5da9817df9b36a56482ff9279d463ed125e9da6 100644 (file)
@@ -1275,6 +1275,7 @@ static int isofs_read_inode(struct inode *inode, int relocated)
        unsigned long offset;
        struct iso_inode_info *ei = ISOFS_I(inode);
        int ret = -EIO;
+       struct timespec64 ts;
 
        block = ei->i_iget5_block;
        bh = sb_bread(inode->i_sb, block);
@@ -1387,8 +1388,10 @@ static int isofs_read_inode(struct inode *inode, int relocated)
                        inode->i_ino, de->flags[-high_sierra]);
        }
 #endif
-       inode_set_mtime_to_ts(inode,
-                             inode_set_atime_to_ts(inode, inode_set_ctime(inode, iso_date(de->date, high_sierra), 0)));
+       ts = iso_date(de->date, high_sierra ? ISO_DATE_HIGH_SIERRA : 0);
+       inode_set_ctime_to_ts(inode, ts);
+       inode_set_atime_to_ts(inode, ts);
+       inode_set_mtime_to_ts(inode, ts);
 
        ei->i_first_extent = (isonum_733(de->extent) +
                        isonum_711(de->ext_attr_length));
index 2d55207c9a9902e6f04578a69883ea3f444f67b5..50655583753334e19e430a53939d6b55fed36a73 100644 (file)
@@ -106,7 +106,9 @@ static inline unsigned int isonum_733(u8 *p)
        /* Ignore bigendian datum due to broken mastering programs */
        return get_unaligned_le32(p);
 }
-extern int iso_date(u8 *, int);
+#define ISO_DATE_HIGH_SIERRA (1 << 0)
+#define ISO_DATE_LONG_FORM (1 << 1)
+struct timespec64 iso_date(u8 *p, int flags);
 
 struct inode;          /* To make gcc happy */
 
index dbf911126e610e4985c4bf0e762e69704d0c4e5a..576498245b9d7c7378ef0c1555ec63440daa51e7 100644 (file)
@@ -412,7 +412,12 @@ repeat:
                                }
                        }
                        break;
-               case SIG('T', 'F'):
+               case SIG('T', 'F'): {
+                       int flags, size, slen;
+
+                       flags = rr->u.TF.flags & TF_LONG_FORM ? ISO_DATE_LONG_FORM : 0;
+                       size = rr->u.TF.flags & TF_LONG_FORM ? 17 : 7;
+                       slen = rr->len - 5;
                        /*
                         * Some RRIP writers incorrectly place ctime in the
                         * TF_CREATE field. Try to handle this correctly for
@@ -420,27 +425,28 @@ repeat:
                         */
                        /* Rock ridge never appears on a High Sierra disk */
                        cnt = 0;
-                       if (rr->u.TF.flags & TF_CREATE) {
-                               inode_set_ctime(inode,
-                                               iso_date(rr->u.TF.times[cnt++].time, 0),
-                                               0);
+                       if ((rr->u.TF.flags & TF_CREATE) && size <= slen) {
+                               inode_set_ctime_to_ts(inode,
+                                               iso_date(rr->u.TF.data + size * cnt++, flags));
+                               slen -= size;
                        }
-                       if (rr->u.TF.flags & TF_MODIFY) {
-                               inode_set_mtime(inode,
-                                               iso_date(rr->u.TF.times[cnt++].time, 0),
-                                               0);
+                       if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) {
+                               inode_set_mtime_to_ts(inode,
+                                               iso_date(rr->u.TF.data + size * cnt++, flags));
+                               slen -= size;
                        }
-                       if (rr->u.TF.flags & TF_ACCESS) {
-                               inode_set_atime(inode,
-                                               iso_date(rr->u.TF.times[cnt++].time, 0),
-                                               0);
+                       if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) {
+                               inode_set_atime_to_ts(inode,
+                                               iso_date(rr->u.TF.data + size * cnt++, flags));
+                               slen -= size;
                        }
-                       if (rr->u.TF.flags & TF_ATTRIBUTES) {
-                               inode_set_ctime(inode,
-                                               iso_date(rr->u.TF.times[cnt++].time, 0),
-                                               0);
+                       if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) {
+                               inode_set_ctime_to_ts(inode,
+                                               iso_date(rr->u.TF.data + size * cnt++, flags));
+                               slen -= size;
                        }
                        break;
+               }
                case SIG('S', 'L'):
                        {
                                int slen;
index 7755e587f77850caee00e16c4d807452b9e8dd32..c0856fa9bb6a4e111596cbbbe9d111049a31c3db 100644 (file)
@@ -65,13 +65,9 @@ struct RR_PL_s {
        __u8 location[8];
 };
 
-struct stamp {
-       __u8 time[7];           /* actually 6 unsigned, 1 signed */
-} __attribute__ ((packed));
-
 struct RR_TF_s {
        __u8 flags;
-       struct stamp times[];   /* Variable number of these beasts */
+       __u8 data[];
 } __attribute__ ((packed));
 
 /* Linux-specific extension for transparent decompression */
index e88dba72166187a410c09902dcd78404d10a5a05..42f479da0b282c3ec96c85582ca2124204b5bdb3 100644 (file)
  * to GMT.  Thus  we should always be correct.
  */
 
-int iso_date(u8 *p, int flag)
+struct timespec64 iso_date(u8 *p, int flags)
 {
        int year, month, day, hour, minute, second, tz;
-       int crtime;
+       struct timespec64 ts;
+
+       if (flags & ISO_DATE_LONG_FORM) {
+               year = (p[0] - '0') * 1000 +
+                      (p[1] - '0') * 100 +
+                      (p[2] - '0') * 10 +
+                      (p[3] - '0') - 1900;
+               month = ((p[4] - '0') * 10 + (p[5] - '0'));
+               day = ((p[6] - '0') * 10 + (p[7] - '0'));
+               hour = ((p[8] - '0') * 10 + (p[9] - '0'));
+               minute = ((p[10] - '0') * 10 + (p[11] - '0'));
+               second = ((p[12] - '0') * 10 + (p[13] - '0'));
+               ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000;
+               tz = p[16];
+       } else {
+               year = p[0];
+               month = p[1];
+               day = p[2];
+               hour = p[3];
+               minute = p[4];
+               second = p[5];
+               ts.tv_nsec = 0;
+               /* High sierra has no time zone */
+               tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6];
+       }
 
-       year = p[0];
-       month = p[1];
-       day = p[2];
-       hour = p[3];
-       minute = p[4];
-       second = p[5];
-       if (flag == 0) tz = p[6]; /* High sierra has no time zone */
-       else tz = 0;
-       
        if (year < 0) {
-               crtime = 0;
+               ts.tv_sec = 0;
        } else {
-               crtime = mktime64(year+1900, month, day, hour, minute, second);
+               ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second);
 
                /* sign extend */
                if (tz & 0x80)
                        tz |= (-1 << 8);
-               
+
                /* 
                 * The timezone offset is unreliable on some disks,
                 * so we make a sanity check.  In no case is it ever
@@ -65,7 +80,7 @@ int iso_date(u8 *p, int flag)
                 * for pointing out the sign error.
                 */
                if (-52 <= tz && tz <= 52)
-                       crtime -= tz * 15 * 60;
+                       ts.tv_sec -= tz * 15 * 60;
        }
-       return crtime;
-}              
+       return ts;
+}