diff options
| author | Phillip Lougher <phillip@lougher.demon.co.uk> | 2011-03-15 18:09:55 -0400 |
|---|---|---|
| committer | Phillip Lougher <phillip@lougher.demon.co.uk> | 2011-03-15 21:04:18 -0400 |
| commit | 44cff8a9ee8a974f9e931df910688e7fc1f0b0f9 (patch) | |
| tree | 4bb7011f0f5be9047d601c889a8bf02da096cefd | |
| parent | 003a3194d36dc22c29cacda4d0c6fede2753c9d0 (diff) | |
Squashfs: handle corruption of directory structure
Handle the rare case where a directory metadata block is uncompressed and
corrupted, leading to a kernel oops in directory scanning (memcpy).
Normally corruption is detected at the decompression stage and dealt with
then, however, this will not happen if:
- metadata isn't compressed (users can optionally request no metadata
compression), or
- the compressed metadata block was larger than the original, in which
case the uncompressed version was used, or
- the data was corrupt after decompression
This patch fixes this by adding some sanity checks against known maximum
values.
Signed-off-by: Phillip Lougher <phillip@lougher.demon.co.uk>
| -rw-r--r-- | fs/squashfs/dir.c | 9 | ||||
| -rw-r--r-- | fs/squashfs/namei.c | 12 |
2 files changed, 21 insertions, 0 deletions
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 0dc340aa2be9..3f79cd1d0c19 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c | |||
| @@ -172,6 +172,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) | |||
| 172 | length += sizeof(dirh); | 172 | length += sizeof(dirh); |
| 173 | 173 | ||
| 174 | dir_count = le32_to_cpu(dirh.count) + 1; | 174 | dir_count = le32_to_cpu(dirh.count) + 1; |
| 175 | |||
| 176 | /* dir_count should never be larger than 256 */ | ||
| 177 | if (dir_count > 256) | ||
| 178 | goto failed_read; | ||
| 179 | |||
| 175 | while (dir_count--) { | 180 | while (dir_count--) { |
| 176 | /* | 181 | /* |
| 177 | * Read directory entry. | 182 | * Read directory entry. |
| @@ -183,6 +188,10 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) | |||
| 183 | 188 | ||
| 184 | size = le16_to_cpu(dire->size) + 1; | 189 | size = le16_to_cpu(dire->size) + 1; |
| 185 | 190 | ||
| 191 | /* size should never be larger than SQUASHFS_NAME_LEN */ | ||
| 192 | if (size > SQUASHFS_NAME_LEN) | ||
| 193 | goto failed_read; | ||
| 194 | |||
| 186 | err = squashfs_read_metadata(inode->i_sb, dire->name, | 195 | err = squashfs_read_metadata(inode->i_sb, dire->name, |
| 187 | &block, &offset, size); | 196 | &block, &offset, size); |
| 188 | if (err < 0) | 197 | if (err < 0) |
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c index 7a9464d08cf6..5d922a6701ab 100644 --- a/fs/squashfs/namei.c +++ b/fs/squashfs/namei.c | |||
| @@ -176,6 +176,11 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, | |||
| 176 | length += sizeof(dirh); | 176 | length += sizeof(dirh); |
| 177 | 177 | ||
| 178 | dir_count = le32_to_cpu(dirh.count) + 1; | 178 | dir_count = le32_to_cpu(dirh.count) + 1; |
| 179 | |||
| 180 | /* dir_count should never be larger than 256 */ | ||
| 181 | if (dir_count > 256) | ||
| 182 | goto data_error; | ||
| 183 | |||
| 179 | while (dir_count--) { | 184 | while (dir_count--) { |
| 180 | /* | 185 | /* |
| 181 | * Read directory entry. | 186 | * Read directory entry. |
| @@ -187,6 +192,10 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, | |||
| 187 | 192 | ||
| 188 | size = le16_to_cpu(dire->size) + 1; | 193 | size = le16_to_cpu(dire->size) + 1; |
| 189 | 194 | ||
| 195 | /* size should never be larger than SQUASHFS_NAME_LEN */ | ||
| 196 | if (size > SQUASHFS_NAME_LEN) | ||
| 197 | goto data_error; | ||
| 198 | |||
| 190 | err = squashfs_read_metadata(dir->i_sb, dire->name, | 199 | err = squashfs_read_metadata(dir->i_sb, dire->name, |
| 191 | &block, &offset, size); | 200 | &block, &offset, size); |
| 192 | if (err < 0) | 201 | if (err < 0) |
| @@ -228,6 +237,9 @@ exit_lookup: | |||
| 228 | d_add(dentry, inode); | 237 | d_add(dentry, inode); |
| 229 | return ERR_PTR(0); | 238 | return ERR_PTR(0); |
| 230 | 239 | ||
| 240 | data_error: | ||
| 241 | err = -EIO; | ||
| 242 | |||
| 231 | read_failure: | 243 | read_failure: |
| 232 | ERROR("Unable to read directory block [%llx:%x]\n", | 244 | ERROR("Unable to read directory block [%llx:%x]\n", |
| 233 | squashfs_i(dir)->start + msblk->directory_table, | 245 | squashfs_i(dir)->start + msblk->directory_table, |
