aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Błaszkowski <kb@sysmikro.com.pl>2016-06-12 13:26:45 -0400
committerChristoph Hellwig <hch@lst.de>2016-06-12 13:35:17 -0400
commit12495ea3ac47ec5b53611e0d68ba3222a09784d1 (patch)
treeb767e6e4c507d2b3c94bab0e7f97382ae2bf61b9
parentf2fe2fa1fbad72e469f49da3716f176a9b53fb75 (diff)
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>
-rw-r--r--fs/freevxfs/vxfs_lookup.c233
1 files changed, 108 insertions, 125 deletions
diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c
index f042cafbb85f..ce4785fd81c6 100644
--- a/fs/freevxfs/vxfs_lookup.c
+++ b/fs/freevxfs/vxfs_lookup.c
@@ -62,35 +62,6 @@ const struct file_operations vxfs_dir_operations = {
62 .iterate_shared = vxfs_readdir, 62 .iterate_shared = vxfs_readdir,
63}; 63};
64 64
65static inline u_long
66dir_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 */
77static inline int
78vxfs_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
88static inline struct vxfs_direct *
89vxfs_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 * vxfs_find_entry - find a mathing directory entry for a dentry 67 * vxfs_find_entry - find a mathing directory entry for a dentry
@@ -109,53 +80,64 @@ vxfs_next_entry(struct vxfs_sb_info *sbi, struct vxfs_direct *de)
109static struct vxfs_direct * 80static struct vxfs_direct *
110vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp) 81vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp)
111{ 82{
112 struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb); 83 u_long bsize = ip->i_sb->s_blocksize;
113 u_long npages, page, nblocks, pblocks, block; 84 const char *name = dp->d_name.name;
114 u_long bsize = ip->i_sb->s_blocksize; 85 int namelen = dp->d_name.len;
115 const char *name = dp->d_name.name; 86 loff_t limit = VXFS_DIRROUND(ip->i_size);
116 int namelen = dp->d_name.len; 87 struct vxfs_direct *de_exit = NULL;
117 88 loff_t pos = 0;
118 npages = dir_pages(ip); 89 struct vxfs_sb_info *sbi = VXFS_SBI(ip->i_sb);
119 nblocks = dir_blocks(ip); 90
120 pblocks = VXFS_BLOCK_PER_PAGE(ip->i_sb); 91 while (pos < limit) {
121 92 struct page *pp;
122 for (page = 0; page < npages; page++) { 93 char *kaddr;
123 caddr_t kaddr; 94 int pg_ofs = pos & ~PAGE_MASK;
124 struct page *pp;
125 95
126 pp = vxfs_get_page(ip->i_mapping, page); 96 pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT);
127 if (IS_ERR(pp)) 97 if (IS_ERR(pp))
128 continue; 98 return NULL;
129 kaddr = (caddr_t)page_address(pp); 99 kaddr = (char *)page_address(pp);
130 100
131 for (block = 0; block <= nblocks && block <= pblocks; block++) { 101 while (pg_ofs < PAGE_SIZE && pos < limit) {
132 caddr_t baddr, limit; 102 struct vxfs_direct *de;
133 struct vxfs_dirblk *dbp; 103
134 struct vxfs_direct *de; 104 if ((pos & (bsize - 1)) < 4) {
135 105 struct vxfs_dirblk *dbp =
136 baddr = kaddr + (block * bsize); 106 (struct vxfs_dirblk *)
137 limit = baddr + bsize - VXFS_DIRLEN(1); 107 (kaddr + (pos & ~PAGE_MASK));
138 108 int overhead = VXFS_DIRBLKOV(sbi, dbp);
139 dbp = (struct vxfs_dirblk *)baddr; 109
140 de = (struct vxfs_direct *) 110 pos += overhead;
141 (baddr + VXFS_DIRBLKOV(sbi, dbp)); 111 pg_ofs += overhead;
142 112 }
143 for (; (caddr_t)de <= limit; 113 de = (struct vxfs_direct *)(kaddr + pg_ofs);
144 de = vxfs_next_entry(sbi, de)) { 114
145 if (!de->d_reclen) 115 if (!de->d_reclen) {
146 break; 116 pos += bsize - 1;
147 if (!de->d_ino) 117 pos &= ~(bsize - 1);
148 continue; 118 break;
149 if (vxfs_match(sbi, namelen, name, de)) { 119 }
150 *ppp = pp; 120
151 return (de); 121 pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
152 } 122 pos += fs16_to_cpu(sbi, de->d_reclen);
123 if (!de->d_ino)
124 continue;
125
126 if (namelen != fs16_to_cpu(sbi, de->d_namelen))
127 continue;
128 if (!memcmp(name, de->d_name, namelen)) {
129 *ppp = pp;
130 de_exit = de;
131 break;
153 } 132 }
154 } 133 }
155 vxfs_put_page(pp); 134 if (!de_exit)
135 vxfs_put_page(pp);
136 else
137 break;
156 } 138 }
157 139
158 return NULL; 140 return de_exit;
159} 141}
160 142
161/** 143/**
@@ -238,80 +220,81 @@ vxfs_readdir(struct file *fp, struct dir_context *ctx)
238{ 220{
239 struct inode *ip = file_inode(fp); 221 struct inode *ip = file_inode(fp);
240 struct super_block *sbp = ip->i_sb; 222 struct super_block *sbp = ip->i_sb;
241 struct vxfs_sb_info *sbi = VXFS_SBI(sbp);
242 u_long bsize = sbp->s_blocksize; 223 u_long bsize = sbp->s_blocksize;
243 u_long page, npages, block, pblocks, nblocks, offset; 224 loff_t pos, limit;
244 loff_t pos; 225 struct vxfs_sb_info *sbi = VXFS_SBI(sbp);
245
246 226
247 if (ctx->pos == 0) { 227 if (ctx->pos == 0) {
248 if (!dir_emit_dot(fp, ctx)) 228 if (!dir_emit_dot(fp, ctx))
249 return 0; 229 goto out;
250 ctx->pos = 1; 230 ctx->pos++;
251 } 231 }
252 if (ctx->pos == 1) { 232 if (ctx->pos == 1) {
253 if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR)) 233 if (!dir_emit(ctx, "..", 2, VXFS_INO(ip)->vii_dotdot, DT_DIR))
254 return 0; 234 goto out;
255 ctx->pos = 2; 235 ctx->pos++;
256 } 236 }
257 pos = ctx->pos - 2;
258
259 if (pos > VXFS_DIRROUND(ip->i_size))
260 return 0;
261 237
262 npages = dir_pages(ip); 238 limit = VXFS_DIRROUND(ip->i_size);
263 nblocks = dir_blocks(ip); 239 if (ctx->pos > limit)
264 pblocks = VXFS_BLOCK_PER_PAGE(sbp); 240 goto out;
265 241
266 page = pos >> PAGE_SHIFT; 242 pos = ctx->pos & ~3L;
267 offset = pos & ~PAGE_MASK;
268 block = (u_long)(pos >> sbp->s_blocksize_bits) % pblocks;
269 243
270 for (; page < npages; page++, block = 0) { 244 while (pos < limit) {
271 char *kaddr; 245 struct page *pp;
272 struct page *pp; 246 char *kaddr;
247 int pg_ofs = pos & ~PAGE_MASK;
248 int rc = 0;
273 249
274 pp = vxfs_get_page(ip->i_mapping, page); 250 pp = vxfs_get_page(ip->i_mapping, pos >> PAGE_SHIFT);
275 if (IS_ERR(pp)) 251 if (IS_ERR(pp))
276 continue; 252 return -ENOMEM;
253
277 kaddr = (char *)page_address(pp); 254 kaddr = (char *)page_address(pp);
278 255
279 for (; block <= nblocks && block <= pblocks; block++) { 256 while (pg_ofs < PAGE_SIZE && pos < limit) {
280 char *baddr, *limit; 257 struct vxfs_direct *de;
281 struct vxfs_dirblk *dbp;
282 struct vxfs_direct *de;
283 258
284 baddr = kaddr + (block * bsize); 259 if ((pos & (bsize - 1)) < 4) {
285 limit = baddr + bsize - VXFS_DIRLEN(1); 260 struct vxfs_dirblk *dbp =
286 261 (struct vxfs_dirblk *)
287 dbp = (struct vxfs_dirblk *)baddr; 262 (kaddr + (pos & ~PAGE_MASK));
288 de = (struct vxfs_direct *) 263 int overhead = VXFS_DIRBLKOV(sbi, dbp);
289 (offset ? 264
290 (kaddr + offset) : 265 pos += overhead;
291 (baddr + VXFS_DIRBLKOV(sbi, dbp))); 266 pg_ofs += overhead;
292 267 }
293 for (; (char *)de <= limit; 268 de = (struct vxfs_direct *)(kaddr + pg_ofs);
294 de = vxfs_next_entry(sbi, de)) { 269
295 if (!de->d_reclen) 270 if (!de->d_reclen) {
296 break; 271 pos += bsize - 1;
297 if (!de->d_ino) 272 pos &= ~(bsize - 1);
298 continue; 273 break;
299 274 }
300 offset = (char *)de - kaddr; 275
301 ctx->pos = ((page << PAGE_SHIFT) | offset) + 2; 276 pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
302 if (!dir_emit(ctx, de->d_name, 277 pos += fs16_to_cpu(sbi, de->d_reclen);
303 fs16_to_cpu(sbi, de->d_namelen), 278 if (!de->d_ino)
304 fs32_to_cpu(sbi, de->d_ino), 279 continue;
305 DT_UNKNOWN)) { 280
306 vxfs_put_page(pp); 281 rc = dir_emit(ctx, de->d_name,
307 return 0; 282 fs16_to_cpu(sbi, de->d_namelen),
308 } 283 fs32_to_cpu(sbi, de->d_ino),
284 DT_UNKNOWN);
285 if (!rc) {
286 /* the dir entry was not read, fix pos. */
287 pos -= fs16_to_cpu(sbi, de->d_reclen);
288 break;
309 } 289 }
310 offset = 0;
311 } 290 }
312 vxfs_put_page(pp); 291 vxfs_put_page(pp);
313 offset = 0; 292 if (!rc)
293 break;
314 } 294 }
315 ctx->pos = ((page << PAGE_SHIFT) | offset) + 2; 295
296 ctx->pos = pos | 2;
297
298out:
316 return 0; 299 return 0;
317} 300}