summaryrefslogtreecommitdiffstats
path: root/fs/afs/dir.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 11:27:52 -0400
committerDavid Howells <dhowells@redhat.com>2017-11-13 10:38:20 -0500
commitdab17c1add5c51b68027a9a3861af3a99cb5485a (patch)
tree17d9d716ff0903555bd1d4451974df3088625e33 /fs/afs/dir.c
parent2c099014a0a456012c1778e80adce839bf956b77 (diff)
afs: Fix directory read/modify race
Because parsing of the directory wasn't being done under any sort of lock, the pages holding the directory content can get invalidated whilst the parsing is ongoing. Further, the directory page check function gets called outside of the page lock, so if the page gets cleared or updated, this may return reports of bad magic numbers in the directory page. Also, the directory may change size whilst checking and parsing are ongoing, so more care needs to be taken here. Fix this by: (1) Perform the page check from the page filling function before we set PageUptodate and drop the page lock. (2) Check for the file having shrunk and the page having been abandoned before checking the page contents. (3) Lock the page whilst parsing it for the directory iterator. Whilst we're at it, add a tracepoint to report check failure. Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs/dir.c')
-rw-r--r--fs/afs/dir.c27
1 files changed, 19 insertions, 8 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 53f3917440e7..ecda0e6a9f7e 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -130,10 +130,11 @@ struct afs_lookup_cookie {
130/* 130/*
131 * check that a directory page is valid 131 * check that a directory page is valid
132 */ 132 */
133static inline bool afs_dir_check_page(struct inode *dir, struct page *page) 133bool afs_dir_check_page(struct inode *dir, struct page *page)
134{ 134{
135 struct afs_dir_page *dbuf; 135 struct afs_dir_page *dbuf;
136 loff_t latter; 136 struct afs_vnode *vnode = AFS_FS_I(dir);
137 loff_t latter, i_size, off;
137 int tmp, qty; 138 int tmp, qty;
138 139
139#if 0 140#if 0
@@ -150,8 +151,15 @@ static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
150 } 151 }
151#endif 152#endif
152 153
153 /* determine how many magic numbers there should be in this page */ 154 /* Determine how many magic numbers there should be in this page, but
154 latter = dir->i_size - page_offset(page); 155 * we must take care because the directory may change size under us.
156 */
157 off = page_offset(page);
158 i_size = i_size_read(dir);
159 if (i_size <= off)
160 goto checked;
161
162 latter = i_size - off;
155 if (latter >= PAGE_SIZE) 163 if (latter >= PAGE_SIZE)
156 qty = PAGE_SIZE; 164 qty = PAGE_SIZE;
157 else 165 else
@@ -162,13 +170,15 @@ static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
162 dbuf = page_address(page); 170 dbuf = page_address(page);
163 for (tmp = 0; tmp < qty; tmp++) { 171 for (tmp = 0; tmp < qty; tmp++) {
164 if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { 172 if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) {
165 printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", 173 printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n",
166 __func__, dir->i_ino, tmp, qty, 174 __func__, dir->i_ino, tmp, qty,
167 ntohs(dbuf->blocks[tmp].pagehdr.magic)); 175 ntohs(dbuf->blocks[tmp].pagehdr.magic));
176 trace_afs_dir_check_failed(vnode, off, i_size);
168 goto error; 177 goto error;
169 } 178 }
170 } 179 }
171 180
181checked:
172 SetPageChecked(page); 182 SetPageChecked(page);
173 return true; 183 return true;
174 184
@@ -183,6 +193,7 @@ error:
183static inline void afs_dir_put_page(struct page *page) 193static inline void afs_dir_put_page(struct page *page)
184{ 194{
185 kunmap(page); 195 kunmap(page);
196 unlock_page(page);
186 put_page(page); 197 put_page(page);
187} 198}
188 199
@@ -197,9 +208,10 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
197 208
198 page = read_cache_page(dir->i_mapping, index, afs_page_filler, key); 209 page = read_cache_page(dir->i_mapping, index, afs_page_filler, key);
199 if (!IS_ERR(page)) { 210 if (!IS_ERR(page)) {
211 lock_page(page);
200 kmap(page); 212 kmap(page);
201 if (unlikely(!PageChecked(page))) { 213 if (unlikely(!PageChecked(page))) {
202 if (PageError(page) || !afs_dir_check_page(dir, page)) 214 if (PageError(page))
203 goto fail; 215 goto fail;
204 } 216 }
205 } 217 }
@@ -384,8 +396,7 @@ out:
384 */ 396 */
385static int afs_readdir(struct file *file, struct dir_context *ctx) 397static int afs_readdir(struct file *file, struct dir_context *ctx)
386{ 398{
387 return afs_dir_iterate(file_inode(file), 399 return afs_dir_iterate(file_inode(file), ctx, file->private_data);
388 ctx, file->private_data);
389} 400}
390 401
391/* 402/*