Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocfs2: add bounds checking to ocfs2_check_dir_entry()

This adds sanity checks for ocfs2_dir_entry to make sure all members of
ocfs2_dir_entry don't stray beyond valid memory region.

Link: https://lkml.kernel.org/r/20240626104433.163270-1-llfamsec@gmail.com
Signed-off-by: lei lu <llfamsec@gmail.com>
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

lei lu and committed by
Andrew Morton
255547c6 937b2972

+29 -17
+29 -17
fs/ocfs2/dir.c
··· 294 294 * bh passed here can be an inode block or a dir data block, depending 295 295 * on the inode inline data flag. 296 296 */ 297 - static int ocfs2_check_dir_entry(struct inode * dir, 298 - struct ocfs2_dir_entry * de, 299 - struct buffer_head * bh, 297 + static int ocfs2_check_dir_entry(struct inode *dir, 298 + struct ocfs2_dir_entry *de, 299 + struct buffer_head *bh, 300 + char *buf, 301 + unsigned int size, 300 302 unsigned long offset) 301 303 { 302 304 const char *error_msg = NULL; 303 305 const int rlen = le16_to_cpu(de->rec_len); 306 + const unsigned long next_offset = ((char *) de - buf) + rlen; 304 307 305 308 if (unlikely(rlen < OCFS2_DIR_REC_LEN(1))) 306 309 error_msg = "rec_len is smaller than minimal"; ··· 311 308 error_msg = "rec_len % 4 != 0"; 312 309 else if (unlikely(rlen < OCFS2_DIR_REC_LEN(de->name_len))) 313 310 error_msg = "rec_len is too small for name_len"; 314 - else if (unlikely( 315 - ((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)) 316 - error_msg = "directory entry across blocks"; 311 + else if (unlikely(next_offset > size)) 312 + error_msg = "directory entry overrun"; 313 + else if (unlikely(next_offset > size - OCFS2_DIR_REC_LEN(1)) && 314 + next_offset != size) 315 + error_msg = "directory entry too close to end"; 317 316 318 317 if (unlikely(error_msg != NULL)) 319 318 mlog(ML_ERROR, "bad entry in directory #%llu: %s - " ··· 357 352 de_buf = first_de; 358 353 dlimit = de_buf + bytes; 359 354 360 - while (de_buf < dlimit) { 355 + while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) { 361 356 /* this code is executed quadratically often */ 362 357 /* do minimal checking `by hand' */ 363 358 364 359 de = (struct ocfs2_dir_entry *) de_buf; 365 360 366 - if (de_buf + namelen <= dlimit && 361 + if (de->name + namelen <= dlimit && 367 362 ocfs2_match(namelen, name, de)) { 368 363 /* found a match - just to be sure, do a full check */ 369 - if (!ocfs2_check_dir_entry(dir, de, bh, offset)) { 364 + if (!ocfs2_check_dir_entry(dir, de, bh, first_de, 365 + bytes, offset)) { 370 366 ret = -1; 371 367 goto bail; 372 368 } ··· 1144 1138 pde = NULL; 1145 1139 de = (struct ocfs2_dir_entry *) first_de; 1146 1140 while (i < bytes) { 1147 - if (!ocfs2_check_dir_entry(dir, de, bh, i)) { 1141 + if (!ocfs2_check_dir_entry(dir, de, bh, first_de, bytes, i)) { 1148 1142 status = -EIO; 1149 1143 mlog_errno(status); 1150 1144 goto bail; ··· 1641 1635 /* These checks should've already been passed by the 1642 1636 * prepare function, but I guess we can leave them 1643 1637 * here anyway. */ 1644 - if (!ocfs2_check_dir_entry(dir, de, insert_bh, offset)) { 1638 + if (!ocfs2_check_dir_entry(dir, de, insert_bh, data_start, 1639 + size, offset)) { 1645 1640 retval = -ENOENT; 1646 1641 goto bail; 1647 1642 } ··· 1781 1774 } 1782 1775 1783 1776 de = (struct ocfs2_dir_entry *) (data->id_data + ctx->pos); 1784 - if (!ocfs2_check_dir_entry(inode, de, di_bh, ctx->pos)) { 1777 + if (!ocfs2_check_dir_entry(inode, de, di_bh, (char *)data->id_data, 1778 + i_size_read(inode), ctx->pos)) { 1785 1779 /* On error, skip the f_pos to the end. */ 1786 1780 ctx->pos = i_size_read(inode); 1787 1781 break; ··· 1875 1867 while (ctx->pos < i_size_read(inode) 1876 1868 && offset < sb->s_blocksize) { 1877 1869 de = (struct ocfs2_dir_entry *) (bh->b_data + offset); 1878 - if (!ocfs2_check_dir_entry(inode, de, bh, offset)) { 1870 + if (!ocfs2_check_dir_entry(inode, de, bh, bh->b_data, 1871 + sb->s_blocksize, offset)) { 1879 1872 /* On error, skip the f_pos to the 1880 1873 next block. */ 1881 1874 ctx->pos = (ctx->pos | (sb->s_blocksize - 1)) + 1; ··· 3348 3339 struct super_block *sb = dir->i_sb; 3349 3340 struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; 3350 3341 struct ocfs2_dir_entry *de, *last_de = NULL; 3351 - char *de_buf, *limit; 3342 + char *first_de, *de_buf, *limit; 3352 3343 unsigned long offset = 0; 3353 3344 unsigned int rec_len, new_rec_len, free_space; 3354 3345 ··· 3361 3352 else 3362 3353 free_space = dir->i_sb->s_blocksize - i_size_read(dir); 3363 3354 3364 - de_buf = di->id2.i_data.id_data; 3355 + first_de = di->id2.i_data.id_data; 3356 + de_buf = first_de; 3365 3357 limit = de_buf + i_size_read(dir); 3366 3358 rec_len = OCFS2_DIR_REC_LEN(namelen); 3367 3359 3368 3360 while (de_buf < limit) { 3369 3361 de = (struct ocfs2_dir_entry *)de_buf; 3370 3362 3371 - if (!ocfs2_check_dir_entry(dir, de, di_bh, offset)) { 3363 + if (!ocfs2_check_dir_entry(dir, de, di_bh, first_de, 3364 + i_size_read(dir), offset)) { 3372 3365 ret = -ENOENT; 3373 3366 goto out; 3374 3367 } ··· 3452 3441 /* move to next block */ 3453 3442 de = (struct ocfs2_dir_entry *) bh->b_data; 3454 3443 } 3455 - if (!ocfs2_check_dir_entry(dir, de, bh, offset)) { 3444 + if (!ocfs2_check_dir_entry(dir, de, bh, bh->b_data, blocksize, 3445 + offset)) { 3456 3446 status = -ENOENT; 3457 3447 goto bail; 3458 3448 }