]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
fs/adfs: bigdir: directory validation strengthening
authorRussell King <rmk+kernel@armlinux.org.uk>
Mon, 9 Dec 2019 11:11:02 +0000 (11:11 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 21 Jan 2020 01:12:42 +0000 (20:12 -0500)
Strengthen the directory validation by ensuring that the header fields
contain sensible values that fit inside the directory, and limit the
directory size to 4MB as per RISC OS requirements.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/adfs/dir_fplus.c

index b83a74e9ff6d713f9a23e73086d75087963212f7..a2fa416fbb6d79ce25f63af73e61d9103437cbdf 100644 (file)
@@ -19,11 +19,38 @@ static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
 static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
 {
        unsigned int size = le32_to_cpu(h->bigdirsize);
+       unsigned int len;
 
        if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
            h->bigdirversion[2] != 0 ||
            h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
-           size & 2047)
+           !size || size & 2047 || size > SZ_4M)
+               return -EIO;
+
+       size -= sizeof(struct adfs_bigdirtail) +
+               offsetof(struct adfs_bigdirheader, bigdirname);
+
+       /* Check that bigdirnamelen fits within the directory */
+       len = ALIGN(le32_to_cpu(h->bigdirnamelen), 4);
+       if (len > size)
+               return -EIO;
+
+       size -= len;
+
+       /* Check that bigdirnamesize fits within the directory */
+       len = le32_to_cpu(h->bigdirnamesize);
+       if (len > size)
+               return -EIO;
+
+       size -= len;
+
+       /*
+        * Avoid division, we know that absolute maximum number of entries
+        * can not be so large to cause overflow of the multiplication below.
+        */
+       len = le32_to_cpu(h->bigdirentries);
+       if (len > SZ_4M / sizeof(struct adfs_bigdirentry) ||
+           len * sizeof(struct adfs_bigdirentry) > size)
                return -EIO;
 
        return 0;