]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
metadump: pathname obfuscation overruns symlink buffer
authorDave Chinner <dchinner@redhat.com>
Mon, 3 Mar 2014 01:29:32 +0000 (12:29 +1100)
committerDave Chinner <david@fromorbit.com>
Mon, 3 Mar 2014 01:29:32 +0000 (12:29 +1100)
In testing the previous metadump changes, fsstress generated an
inline symlink of 336 bytes in length. This caused corruption of the
restored filesystem that wasn't present in the original filesystem -
it corrupted the magic number of the next inode in the chunk. The
reason being that the symlink data is not null terminated in the
inode literal area, and hence when the symlink consumes the entire
literal area like so:

xfs_db> daddr 0x42679
xfs_db> p
000: 494ea1ff 03010000 00000000 00000000 00000001 00000000 00000000 00000000
020: 53101af9 1678d2a8 53101af9 15fec0a8 53101af9 15fec0a8 00000000 00000150
040: 00000000 00000000 00000000 00000000 00000002 00000000 00000000 d868b52d
060: ffffffff 0ce5477a 00000000 00000002 00000002 0000041c 00000000 00000000
080: 00000000 00000000 00000000 00000000 53101af9 15fec0a8 00000000 00042679
0a0: 6c4e9d4e 84944986 a074cffd 0ea042a8 78787878 78787878 78782f78 78787878
0c0: 78787878 2f787878 78787878 78782f78 78787878 78787878 2f787878 78787878
0e0: 78782f78 78787878 78787878 2f787878 78787878 78782f78 78787878 78787878
100: 2f787878 78787878 78782f78 78787878 78787878 2f787878 78787878 78782f78
120: 78787878 78787878 2f787878 78787878 78782f78 78787878 78787878 2f787878
140: 78787878 78782f78 78787878 78787878 2f787878 78787878 78782f78 78787878
160: 78787878 2f787878 78787878 78782f78 78787878 78787878 2f787878 78787878
180: 78782f78 78787878 78787878 2f787878 78787878 78782f78 78787878 78787878
1a0: 2f787878 78787878 78782f78 78787878 78787878 2f787878 78787878 78782f78
1c0: 78787878 78787878 2f787878 78787878 78782f78 78787878 78787878 2f787878
1e0: 78787878 78782f78 78787878 78787878 2f787878 78787878 78782f78 78787878

the symlink data butts right up agains the magic number of the next
inode in the chunk. And then, when obfuscation gets to the final
pathname component, it gets it's length via:

/* last (or single) component */
namelen = strlen((char *)comp);
hash = libxfs_da_hashname(comp, namelen);
obfuscate_name(hash, namelen, comp);

strlen(), which looks for a null terminator and finds it several
bytes into the next inode. It then proceeds to obfuscate that
length, including the inode magic number of the next inode....

Fix this by ensuring we can't overrun the symlink buffer length
by assuming that the symlink is not null terminated. Tested against
the filesystem image that triggered the problem in the first place.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
db/metadump.c

index 324800942a209b7de782a546c28b4e8cdfd71154..09bb85ac014db53e76664b819c051292a7378a16 100644 (file)
@@ -1015,16 +1015,23 @@ obfuscate_sf_dir(
        }
 }
 
+/*
+ * The pathname may not be null terminated. It may be terminated by the end of
+ * a buffer or inode literal area, and the start of the next region contains
+ * unknown data. Therefore, when we get to the last component of the symlink, we
+ * cannot assume that strlen() will give us the right result. Hence we need to
+ * track the remaining pathname length and use that instead.
+ */
 static void
 obfuscate_path_components(
        char                    *buf,
        __uint64_t              len)
 {
-       uchar_t                 *comp;
+       uchar_t                 *comp = (uchar_t *)buf;
+       uchar_t                 *end = comp + len;
        xfs_dahash_t            hash;
 
-       comp = (uchar_t *)buf;
-       while (comp < (uchar_t *)buf + len) {
+       while (comp < end) {
                char    *slash;
                int     namelen;
 
@@ -1032,7 +1039,7 @@ obfuscate_path_components(
                slash = strchr((char *)comp, '/');
                if (!slash) {
                        /* last (or single) component */
-                       namelen = strlen((char *)comp);
+                       namelen = strnlen((char *)comp, len);
                        hash = libxfs_da_hashname(comp, namelen);
                        obfuscate_name(hash, namelen, comp);
                        break;
@@ -1041,11 +1048,13 @@ obfuscate_path_components(
                /* handle leading or consecutive slashes */
                if (!namelen) {
                        comp++;
+                       len--;
                        continue;
                }
                hash = libxfs_da_hashname(comp, namelen);
                obfuscate_name(hash, namelen, comp);
                comp += namelen + 1;
+               len -= namelen + 1;
        }
 }