aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-21 19:14:04 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-21 19:14:04 -0400
commit6326c71fd2fb3bef5fa33951479298b683da35fe (patch)
tree6bccc510bc127380439d2ac922fea5ec3ec6927d /fs
parenta70b52ec1aaeaf60f4739edb1b422827cb6f3893 (diff)
vfs: be even more careful about dentry RCU name lookups
Miklos Szeredi points out that we need to also worry about memory odering when doing the dentry name comparison asynchronously with RCU. In particular, doing a rename can do a memcpy() of one dentry name over another, and we want to make sure that any unlocked reader will always see the proper terminating NUL character, so that it won't ever run off the allocation. Rather than having to be extra careful with the name copy or at lookup time for each character, this resolves the issue by making sure that all names that are inlined in the dentry always have a NUL character at the end of the name allocation. If we do that at dentry allocation time, we know that no future name copy will ever change that final NUL to anything else, so there are no memory ordering issues. So even if a concurrent rename ends up overwriting the NUL character that terminates the original name, we always know that there is one final NUL at the end, and there is no worry about the lockless RCU lookup traversing the name too far. The out-of-line allocations are never copied over, so we can just make sure that we write the name (with terminating NULL) and do a write barrier before we expose the name to anything else by setting it in the dentry. Reported-by: Miklos Szeredi <mszeredi@suse.cz> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Nick Piggin <npiggin@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c17
1 files changed, 15 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 92099f61bc64..a7675e0109f0 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -192,6 +192,7 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
192 192
193static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) 193static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
194{ 194{
195 const unsigned char *cs;
195 /* 196 /*
196 * Be careful about RCU walk racing with rename: 197 * Be careful about RCU walk racing with rename:
197 * use ACCESS_ONCE to fetch the name pointer. 198 * use ACCESS_ONCE to fetch the name pointer.
@@ -208,7 +209,9 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
208 * early because the data cannot match (there can 209 * early because the data cannot match (there can
209 * be no NUL in the ct/tcount data) 210 * be no NUL in the ct/tcount data)
210 */ 211 */
211 return dentry_string_cmp(ACCESS_ONCE(dentry->d_name.name), ct, tcount); 212 cs = ACCESS_ONCE(dentry->d_name.name);
213 smp_read_barrier_depends();
214 return dentry_string_cmp(cs, ct, tcount);
212} 215}
213 216
214static void __d_free(struct rcu_head *head) 217static void __d_free(struct rcu_head *head)
@@ -1271,6 +1274,13 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
1271 if (!dentry) 1274 if (!dentry)
1272 return NULL; 1275 return NULL;
1273 1276
1277 /*
1278 * We guarantee that the inline name is always NUL-terminated.
1279 * This way the memcpy() done by the name switching in rename
1280 * will still always have a NUL at the end, even if we might
1281 * be overwriting an internal NUL character
1282 */
1283 dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
1274 if (name->len > DNAME_INLINE_LEN-1) { 1284 if (name->len > DNAME_INLINE_LEN-1) {
1275 dname = kmalloc(name->len + 1, GFP_KERNEL); 1285 dname = kmalloc(name->len + 1, GFP_KERNEL);
1276 if (!dname) { 1286 if (!dname) {
@@ -1280,13 +1290,16 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
1280 } else { 1290 } else {
1281 dname = dentry->d_iname; 1291 dname = dentry->d_iname;
1282 } 1292 }
1283 dentry->d_name.name = dname;
1284 1293
1285 dentry->d_name.len = name->len; 1294 dentry->d_name.len = name->len;
1286 dentry->d_name.hash = name->hash; 1295 dentry->d_name.hash = name->hash;
1287 memcpy(dname, name->name, name->len); 1296 memcpy(dname, name->name, name->len);
1288 dname[name->len] = 0; 1297 dname[name->len] = 0;
1289 1298
1299 /* Make sure we always see the terminating NUL character */
1300 smp_wmb();
1301 dentry->d_name.name = dname;
1302
1290 dentry->d_count = 1; 1303 dentry->d_count = 1;
1291 dentry->d_flags = 0; 1304 dentry->d_flags = 0;
1292 spin_lock_init(&dentry->d_lock); 1305 spin_lock_init(&dentry->d_lock);