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

freevxfs: refactor readdir and lookup code

This change fixes also a buffer overflow which was caused by
accessing address space beyond mapped page

Signed-off-by: Krzysztof Błaszkowski <kb@sysmikro.com.pl>
Signed-off-by: Christoph Hellwig <hch@lst.de>

authored by

Krzysztof Błaszkowski and committed by
Christoph Hellwig
12495ea3 f2fe2fa1

+103 -120
+103 -120
fs/freevxfs/vxfs_lookup.c
··· 62 62 .iterate_shared = vxfs_readdir, 63 63 }; 64 64 65 - static inline u_long 66 - dir_blocks(struct inode *ip) 67 - { 68 - u_long bsize = ip->i_sb->s_blocksize; 69 - return (ip->i_size + bsize - 1) & ~(bsize - 1); 70 - } 71 - 72 - /* 73 - * NOTE! unlike strncmp, vxfs_match returns 1 for success, 0 for failure. 74 - * 75 - * len <= VXFS_NAMELEN and de != NULL are guaranteed by caller. 76 - */ 77 - static inline int 78 - vxfs_match(struct vxfs_sb_info *sbi, int len, const char *const name, 79 - struct vxfs_direct *de) 80 - { 81 - if (len != fs16_to_cpu(sbi, de->d_namelen)) 82 - return 0; 83 - if (!de->d_ino) 84 - return 0; 85 - return !memcmp(name, de->d_name, len); 86 - } 87 - 88 - static inline struct vxfs_direct * 89 - vxfs_next_entry(struct vxfs_sb_info *sbi, struct vxfs_direct *de) 90 - { 91 - return ((struct vxfs_direct *) 92 - ((char *)de + fs16_to_cpu(sbi, de->d_reclen))); 93 - } 94 65 95 66 /** 96 67 * vxfs_find_entry - find a mathing directory entry for a dentry ··· 80 109 static struct vxfs_direct * 81 110 vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp) 82 111 { 83 - struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); 84 - u_long npages, page, nblocks, pblocks, block; 85 - u_long bsize = ip->i_sb->s_blocksize; 86 - const char *name = dp->d_name.name; 87 - int namelen = dp->d_name.len; 112 + u_long bsize = ip->i_sb->s_blocksize; 113 + const char *name = dp->d_name.name; 114 + int namelen = dp->d_name.len; 115 + loff_t limit = VXFS_DIRROUND(ip->i_size); 116 + struct vxfs_direct *de_exit = NULL; 117 + loff_t pos = 0; 118 + struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); 88 119 89 - npages = dir_pages(ip); 90 - nblocks = dir_blocks(ip); 91 - pblocks = VXFS_BLOCK_PER_PAGE(ip->i_sb); 92 - 93 - for (page = 0; page < npages; page++) { 94 - caddr_t kaddr; 95 - struct page *pp; 120 + while (pos < limit) { 121 + struct page *pp; 122 + char *kaddr; 123 + int pg_ofs = pos & ~PAGE_MASK; 96 124 97 - pp = vxfs_get_page(ip->i_mapping, page); 125 + pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT); 98 126 if (IS_ERR(pp)) 99 - continue; 100 - kaddr = (caddr_t)page_address(pp); 127 + return NULL; 128 + kaddr = (char *)page_address(pp); 101 129 102 - for (block = 0; block <= nblocks && block <= pblocks; block++) { 103 - caddr_t baddr, limit; 104 - struct vxfs_dirblk *dbp; 105 - struct vxfs_direct *de; 130 + while (pg_ofs < PAGE_SIZE && pos < limit) { 131 + struct vxfs_direct *de; 106 132 107 - baddr = kaddr + (block * bsize); 108 - limit = baddr + bsize - VXFS_DIRLEN(1); 109 - 110 - dbp = (struct vxfs_dirblk *)baddr; 111 - de = (struct vxfs_direct *) 112 - (baddr + VXFS_DIRBLKOV(sbi, dbp)); 133 + if ((pos & (bsize - 1)) < 4) { 134 + struct vxfs_dirblk *dbp = 135 + (struct vxfs_dirblk *) 136 + (kaddr + (pos & ~PAGE_MASK)); 137 + int overhead = VXFS_DIRBLKOV(sbi, dbp); 113 138 114 - for (; (caddr_t)de <= limit; 115 - de = vxfs_next_entry(sbi, de)) { 116 - if (!de->d_reclen) 117 - break; 118 - if (!de->d_ino) 119 - continue; 120 - if (vxfs_match(sbi, namelen, name, de)) { 121 - *ppp = pp; 122 - return (de); 123 - } 139 + pos += overhead; 140 + pg_ofs += overhead; 141 + } 142 + de = (struct vxfs_direct *)(kaddr + pg_ofs); 143 + 144 + if (!de->d_reclen) { 145 + pos += bsize - 1; 146 + pos &= ~(bsize - 1); 147 + break; 148 + } 149 + 150 + pg_ofs += fs16_to_cpu(sbi, de->d_reclen); 151 + pos += fs16_to_cpu(sbi, de->d_reclen); 152 + if (!de->d_ino) 153 + continue; 154 + 155 + if (namelen != fs16_to_cpu(sbi, de->d_namelen)) 156 + continue; 157 + if (!memcmp(name, de->d_name, namelen)) { 158 + *ppp = pp; 159 + de_exit = de; 160 + break; 124 161 } 125 162 } 126 - vxfs_put_page(pp); 163 + if (!de_exit) 164 + vxfs_put_page(pp); 165 + else 166 + break; 127 167 } 128 168 129 - return NULL; 169 + return de_exit; 130 170 } 131 171 132 172 /** ··· 220 238 { 221 239 struct inode *ip = file_inode(fp); 222 240 struct super_block *sbp = ip->i_sb; 223 - struct vxfs_sb_info *sbi = VXFS_SBI(sbp); 224 241 u_long bsize = sbp->s_blocksize; 225 - u_long page, npages, block, pblocks, nblocks, offset; 226 - loff_t pos; 227 - 242 + loff_t pos, limit; 243 + struct vxfs_sb_info *sbi = VXFS_SBI(sbp); 228 244 229 245 if (ctx->pos == 0) { 230 246 if (!dir_emit_dot(fp, ctx)) 231 - return 0; 232 - ctx->pos = 1; 247 + goto out; 248 + ctx->pos++; 233 249 } 234 250 if (ctx->pos == 1) { 235 251 if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR)) 236 - return 0; 237 - ctx->pos = 2; 252 + goto out; 253 + ctx->pos++; 238 254 } 239 - pos = ctx->pos - 2; 240 - 241 - if (pos > VXFS_DIRROUND(ip->i_size)) 242 - return 0; 243 255 244 - npages = dir_pages(ip); 245 - nblocks = dir_blocks(ip); 246 - pblocks = VXFS_BLOCK_PER_PAGE(sbp); 256 + limit = VXFS_DIRROUND(ip->i_size); 257 + if (ctx->pos > limit) 258 + goto out; 247 259 248 - page = pos >> PAGE_SHIFT; 249 - offset = pos & ~PAGE_MASK; 250 - block = (u_long)(pos >> sbp->s_blocksize_bits) % pblocks; 260 + pos = ctx->pos & ~3L; 251 261 252 - for (; page < npages; page++, block = 0) { 253 - char *kaddr; 254 - struct page *pp; 262 + while (pos < limit) { 263 + struct page *pp; 264 + char *kaddr; 265 + int pg_ofs = pos & ~PAGE_MASK; 266 + int rc = 0; 255 267 256 - pp = vxfs_get_page(ip->i_mapping, page); 268 + pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT); 257 269 if (IS_ERR(pp)) 258 - continue; 270 + return -ENOMEM; 271 + 259 272 kaddr = (char *)page_address(pp); 260 273 261 - for (; block <= nblocks && block <= pblocks; block++) { 262 - char *baddr, *limit; 263 - struct vxfs_dirblk *dbp; 264 - struct vxfs_direct *de; 274 + while (pg_ofs < PAGE_SIZE && pos < limit) { 275 + struct vxfs_direct *de; 265 276 266 - baddr = kaddr + (block * bsize); 267 - limit = baddr + bsize - VXFS_DIRLEN(1); 268 - 269 - dbp = (struct vxfs_dirblk *)baddr; 270 - de = (struct vxfs_direct *) 271 - (offset ? 272 - (kaddr + offset) : 273 - (baddr + VXFS_DIRBLKOV(sbi, dbp))); 277 + if ((pos & (bsize - 1)) < 4) { 278 + struct vxfs_dirblk *dbp = 279 + (struct vxfs_dirblk *) 280 + (kaddr + (pos & ~PAGE_MASK)); 281 + int overhead = VXFS_DIRBLKOV(sbi, dbp); 274 282 275 - for (; (char *)de <= limit; 276 - de = vxfs_next_entry(sbi, de)) { 277 - if (!de->d_reclen) 278 - break; 279 - if (!de->d_ino) 280 - continue; 281 - 282 - offset = (char *)de - kaddr; 283 - ctx->pos = ((page << PAGE_SHIFT) | offset) + 2; 284 - if (!dir_emit(ctx, de->d_name, 285 - fs16_to_cpu(sbi, de->d_namelen), 286 - fs32_to_cpu(sbi, de->d_ino), 287 - DT_UNKNOWN)) { 288 - vxfs_put_page(pp); 289 - return 0; 290 - } 283 + pos += overhead; 284 + pg_ofs += overhead; 291 285 } 292 - offset = 0; 286 + de = (struct vxfs_direct *)(kaddr + pg_ofs); 287 + 288 + if (!de->d_reclen) { 289 + pos += bsize - 1; 290 + pos &= ~(bsize - 1); 291 + break; 292 + } 293 + 294 + pg_ofs += fs16_to_cpu(sbi, de->d_reclen); 295 + pos += fs16_to_cpu(sbi, de->d_reclen); 296 + if (!de->d_ino) 297 + continue; 298 + 299 + rc = dir_emit(ctx, de->d_name, 300 + fs16_to_cpu(sbi, de->d_namelen), 301 + fs32_to_cpu(sbi, de->d_ino), 302 + DT_UNKNOWN); 303 + if (!rc) { 304 + /* the dir entry was not read, fix pos. */ 305 + pos -= fs16_to_cpu(sbi, de->d_reclen); 306 + break; 307 + } 293 308 } 294 309 vxfs_put_page(pp); 295 - offset = 0; 310 + if (!rc) 311 + break; 296 312 } 297 - ctx->pos = ((page << PAGE_SHIFT) | offset) + 2; 313 + 314 + ctx->pos = pos | 2; 315 + 316 + out: 298 317 return 0; 299 318 }