]>
Commit | Line | Data |
---|---|---|
0f23eb5b GKH |
1 | From 54dd0e0a1b255f115f8647fc6fb93273251b01b9 Mon Sep 17 00:00:00 2001 |
2 | From: Theodore Ts'o <tytso@mit.edu> | |
3 | Date: Fri, 30 Mar 2018 20:04:11 -0400 | |
4 | Subject: ext4: add extra checks to ext4_xattr_block_get() | |
5 | ||
6 | From: Theodore Ts'o <tytso@mit.edu> | |
7 | ||
8 | commit 54dd0e0a1b255f115f8647fc6fb93273251b01b9 upstream. | |
9 | ||
10 | Add explicit checks in ext4_xattr_block_get() just in case the | |
11 | e_value_offs and e_value_size fields in the the xattr block are | |
12 | corrupted in memory after the buffer_verified bit is set on the xattr | |
13 | block. | |
14 | ||
15 | Signed-off-by: Theodore Ts'o <tytso@mit.edu> | |
16 | Cc: stable@kernel.org | |
17 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
18 | ||
19 | --- | |
20 | fs/ext4/xattr.c | 26 +++++++++++++++++++------- | |
21 | fs/ext4/xattr.h | 11 +++++++++++ | |
22 | 2 files changed, 30 insertions(+), 7 deletions(-) | |
23 | ||
24 | --- a/fs/ext4/xattr.c | |
25 | +++ b/fs/ext4/xattr.c | |
26 | @@ -197,7 +197,7 @@ ext4_xattr_check_entries(struct ext4_xat | |
27 | while (!IS_LAST_ENTRY(entry)) { | |
28 | u32 size = le32_to_cpu(entry->e_value_size); | |
29 | ||
30 | - if (size > INT_MAX) | |
31 | + if (size > EXT4_XATTR_SIZE_MAX) | |
32 | return -EFSCORRUPTED; | |
33 | ||
34 | if (size != 0 && entry->e_value_inum == 0) { | |
35 | @@ -540,8 +540,10 @@ ext4_xattr_block_get(struct inode *inode | |
36 | if (error) | |
37 | goto cleanup; | |
38 | size = le32_to_cpu(entry->e_value_size); | |
39 | + error = -ERANGE; | |
40 | + if (unlikely(size > EXT4_XATTR_SIZE_MAX)) | |
41 | + goto cleanup; | |
42 | if (buffer) { | |
43 | - error = -ERANGE; | |
44 | if (size > buffer_size) | |
45 | goto cleanup; | |
46 | if (entry->e_value_inum) { | |
47 | @@ -550,8 +552,12 @@ ext4_xattr_block_get(struct inode *inode | |
48 | if (error) | |
49 | goto cleanup; | |
50 | } else { | |
51 | - memcpy(buffer, bh->b_data + | |
52 | - le16_to_cpu(entry->e_value_offs), size); | |
53 | + u16 offset = le16_to_cpu(entry->e_value_offs); | |
54 | + void *p = bh->b_data + offset; | |
55 | + | |
56 | + if (unlikely(p + size > end)) | |
57 | + goto cleanup; | |
58 | + memcpy(buffer, p, size); | |
59 | } | |
60 | } | |
61 | error = size; | |
62 | @@ -589,8 +595,10 @@ ext4_xattr_ibody_get(struct inode *inode | |
63 | if (error) | |
64 | goto cleanup; | |
65 | size = le32_to_cpu(entry->e_value_size); | |
66 | + error = -ERANGE; | |
67 | + if (unlikely(size > EXT4_XATTR_SIZE_MAX)) | |
68 | + goto cleanup; | |
69 | if (buffer) { | |
70 | - error = -ERANGE; | |
71 | if (size > buffer_size) | |
72 | goto cleanup; | |
73 | if (entry->e_value_inum) { | |
74 | @@ -599,8 +607,12 @@ ext4_xattr_ibody_get(struct inode *inode | |
75 | if (error) | |
76 | goto cleanup; | |
77 | } else { | |
78 | - memcpy(buffer, (void *)IFIRST(header) + | |
79 | - le16_to_cpu(entry->e_value_offs), size); | |
80 | + u16 offset = le16_to_cpu(entry->e_value_offs); | |
81 | + void *p = (void *)IFIRST(header) + offset; | |
82 | + | |
83 | + if (unlikely(p + size > end)) | |
84 | + goto cleanup; | |
85 | + memcpy(buffer, p, size); | |
86 | } | |
87 | } | |
88 | error = size; | |
89 | --- a/fs/ext4/xattr.h | |
90 | +++ b/fs/ext4/xattr.h | |
91 | @@ -71,6 +71,17 @@ struct ext4_xattr_entry { | |
92 | #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) | |
93 | ||
94 | /* | |
95 | + * XATTR_SIZE_MAX is currently 64k, but for the purposes of checking | |
96 | + * for file system consistency errors, we use a somewhat bigger value. | |
97 | + * This allows XATTR_SIZE_MAX to grow in the future, but by using this | |
98 | + * instead of INT_MAX for certain consistency checks, we don't need to | |
99 | + * worry about arithmetic overflows. (Actually XATTR_SIZE_MAX is | |
100 | + * defined in include/uapi/linux/limits.h, so changing it is going | |
101 | + * not going to be trivial....) | |
102 | + */ | |
103 | +#define EXT4_XATTR_SIZE_MAX (1 << 24) | |
104 | + | |
105 | +/* | |
106 | * The minimum size of EA value when you start storing it in an external inode | |
107 | * size of block - size of header - size of 1 entry - 4 null bytes | |
108 | */ |