summaryrefslogtreecommitdiff
path: root/fs/adfs
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2019-12-09 14:11:02 +0300
committerAl Viro <viro@zeniv.linux.org.uk>2020-01-21 04:12:42 +0300
commitaa3d4e015298fd523617c2bea392d02ea19eaa1a (patch)
treecc6c82379a3c9bc18d1668f0a14ed61a4975cf76 /fs/adfs
parent6674ecab9004dcc4d8a65744f581b9ccf1f17504 (diff)
downloadlinux-aa3d4e015298fd523617c2bea392d02ea19eaa1a.tar.xz
fs/adfs: bigdir: directory validation strengthening
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>
Diffstat (limited to 'fs/adfs')
-rw-r--r--fs/adfs/dir_fplus.c29
1 files changed, 28 insertions, 1 deletions
diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c
index b83a74e9ff6d..a2fa416fbb6d 100644
--- a/fs/adfs/dir_fplus.c
+++ b/fs/adfs/dir_fplus.c
@@ -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;