Skip to content

Commit 3478c83

Browse files
committed
ext4: improve xattr consistency checking and error reporting
Refactor the in-inode and xattr block consistency checking, and report more fine-grained reports of the consistency problems. Also add more consistency checks for ea_inode number. Reviewed-by: Andreas Dilger <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 2241ab5 commit 3478c83

File tree

1 file changed

+80
-46
lines changed

1 file changed

+80
-46
lines changed

fs/ext4/xattr.c

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -184,27 +184,73 @@ ext4_xattr_handler(int name_index)
184184
}
185185

186186
static int
187-
ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end,
188-
void *value_start)
187+
check_xattrs(struct inode *inode, struct buffer_head *bh,
188+
struct ext4_xattr_entry *entry, void *end, void *value_start,
189+
const char *function, unsigned int line)
189190
{
190191
struct ext4_xattr_entry *e = entry;
192+
int err = -EFSCORRUPTED;
193+
char *err_str;
194+
195+
if (bh) {
196+
if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
197+
BHDR(bh)->h_blocks != cpu_to_le32(1)) {
198+
err_str = "invalid header";
199+
goto errout;
200+
}
201+
if (buffer_verified(bh))
202+
return 0;
203+
if (!ext4_xattr_block_csum_verify(inode, bh)) {
204+
err = -EFSBADCRC;
205+
err_str = "invalid checksum";
206+
goto errout;
207+
}
208+
} else {
209+
struct ext4_xattr_ibody_header *header = value_start;
210+
211+
header -= 1;
212+
if (end - (void *)header < sizeof(*header) + sizeof(u32)) {
213+
err_str = "in-inode xattr block too small";
214+
goto errout;
215+
}
216+
if (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
217+
err_str = "bad magic number in in-inode xattr";
218+
goto errout;
219+
}
220+
}
191221

192222
/* Find the end of the names list */
193223
while (!IS_LAST_ENTRY(e)) {
194224
struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
195-
if ((void *)next >= end)
196-
return -EFSCORRUPTED;
197-
if (strnlen(e->e_name, e->e_name_len) != e->e_name_len)
198-
return -EFSCORRUPTED;
225+
if ((void *)next >= end) {
226+
err_str = "e_name out of bounds";
227+
goto errout;
228+
}
229+
if (strnlen(e->e_name, e->e_name_len) != e->e_name_len) {
230+
err_str = "bad e_name length";
231+
goto errout;
232+
}
199233
e = next;
200234
}
201235

202236
/* Check the values */
203237
while (!IS_LAST_ENTRY(entry)) {
204238
u32 size = le32_to_cpu(entry->e_value_size);
239+
unsigned long ea_ino = le32_to_cpu(entry->e_value_inum);
205240

206-
if (size > EXT4_XATTR_SIZE_MAX)
207-
return -EFSCORRUPTED;
241+
if (!ext4_has_feature_ea_inode(inode->i_sb) && ea_ino) {
242+
err_str = "ea_inode specified without ea_inode feature enabled";
243+
goto errout;
244+
}
245+
if (ea_ino && ((ea_ino == EXT4_ROOT_INO) ||
246+
!ext4_valid_inum(inode->i_sb, ea_ino))) {
247+
err_str = "invalid ea_ino";
248+
goto errout;
249+
}
250+
if (size > EXT4_XATTR_SIZE_MAX) {
251+
err_str = "e_value size too large";
252+
goto errout;
253+
}
208254

209255
if (size != 0 && entry->e_value_inum == 0) {
210256
u16 offs = le16_to_cpu(entry->e_value_offs);
@@ -216,66 +262,54 @@ ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end,
216262
* the padded and unpadded sizes, since the size may
217263
* overflow to 0 when adding padding.
218264
*/
219-
if (offs > end - value_start)
220-
return -EFSCORRUPTED;
265+
if (offs > end - value_start) {
266+
err_str = "e_value out of bounds";
267+
goto errout;
268+
}
221269
value = value_start + offs;
222270
if (value < (void *)e + sizeof(u32) ||
223271
size > end - value ||
224-
EXT4_XATTR_SIZE(size) > end - value)
225-
return -EFSCORRUPTED;
272+
EXT4_XATTR_SIZE(size) > end - value) {
273+
err_str = "overlapping e_value ";
274+
goto errout;
275+
}
226276
}
227277
entry = EXT4_XATTR_NEXT(entry);
228278
}
229-
279+
if (bh)
280+
set_buffer_verified(bh);
230281
return 0;
282+
283+
errout:
284+
if (bh)
285+
__ext4_error_inode(inode, function, line, 0, -err,
286+
"corrupted xattr block %llu: %s",
287+
(unsigned long long) bh->b_blocknr,
288+
err_str);
289+
else
290+
__ext4_error_inode(inode, function, line, 0, -err,
291+
"corrupted in-inode xattr: %s", err_str);
292+
return err;
231293
}
232294

233295
static inline int
234296
__ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
235297
const char *function, unsigned int line)
236298
{
237-
int error = -EFSCORRUPTED;
238-
239-
if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
240-
BHDR(bh)->h_blocks != cpu_to_le32(1))
241-
goto errout;
242-
if (buffer_verified(bh))
243-
return 0;
244-
245-
error = -EFSBADCRC;
246-
if (!ext4_xattr_block_csum_verify(inode, bh))
247-
goto errout;
248-
error = ext4_xattr_check_entries(BFIRST(bh), bh->b_data + bh->b_size,
249-
bh->b_data);
250-
errout:
251-
if (error)
252-
__ext4_error_inode(inode, function, line, 0, -error,
253-
"corrupted xattr block %llu",
254-
(unsigned long long) bh->b_blocknr);
255-
else
256-
set_buffer_verified(bh);
257-
return error;
299+
return check_xattrs(inode, bh, BFIRST(bh), bh->b_data + bh->b_size,
300+
bh->b_data, function, line);
258301
}
259302

260303
#define ext4_xattr_check_block(inode, bh) \
261304
__ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
262305

263306

264-
static int
307+
static inline int
265308
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
266309
void *end, const char *function, unsigned int line)
267310
{
268-
int error = -EFSCORRUPTED;
269-
270-
if (end - (void *)header < sizeof(*header) + sizeof(u32) ||
271-
(header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)))
272-
goto errout;
273-
error = ext4_xattr_check_entries(IFIRST(header), end, IFIRST(header));
274-
errout:
275-
if (error)
276-
__ext4_error_inode(inode, function, line, 0, -error,
277-
"corrupted in-inode xattr");
278-
return error;
311+
return check_xattrs(inode, NULL, IFIRST(header), end, IFIRST(header),
312+
function, line);
279313
}
280314

281315
#define xattr_check_inode(inode, header, end) \

0 commit comments

Comments
 (0)