diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-09-29 14:46:30 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-09-29 14:46:30 -0400 |
commit | 6d13f69444bd3d4888e43f7756449748f5a98bad (patch) | |
tree | 1a6b2e7df1dc85a0fbe4273621d54fda49523ec2 /block/cmdline-parser.c | |
parent | 1e3827bf8aebe29af2d6e49b89d85dfae4d0154f (diff) | |
download | op-kernel-dev-6d13f69444bd3d4888e43f7756449748f5a98bad.zip op-kernel-dev-6d13f69444bd3d4888e43f7756449748f5a98bad.tar.gz |
missing data dependency barrier in prepend_name()
AFAICS, prepend_name() is broken on SMP alpha. Disclaimer: I don't have
SMP alpha boxen to reproduce it on. However, it really looks like the race
is real.
CPU1: d_path() on /mnt/ramfs/<255-character>/foo
CPU2: mv /mnt/ramfs/<255-character> /mnt/ramfs/<63-character>
CPU2 does d_alloc(), which allocates an external name, stores the name there
including terminating NUL, does smp_wmb() and stores its address in
dentry->d_name.name. It proceeds to d_add(dentry, NULL) and d_move()
old dentry over to that. ->d_name.name value ends up in that dentry.
In the meanwhile, CPU1 gets to prepend_name() for that dentry. It fetches
->d_name.name and ->d_name.len; the former ends up pointing to new name
(64-byte kmalloc'ed array), the latter - 255 (length of the old name).
Nothing to force the ordering there, and normally that would be OK, since we'd
run into the terminating NUL and stop. Except that it's alpha, and we'd need
a data dependency barrier to guarantee that we see that store of NUL
__d_alloc() has done. In a similar situation dentry_cmp() would survive; it
does explicit smp_read_barrier_depends() after fetching ->d_name.name.
prepend_name() doesn't and it risks walking past the end of kmalloc'ed object
and possibly oops due to taking a page fault in kernel mode.
Cc: stable@vger.kernel.org # 3.12+
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'block/cmdline-parser.c')
0 files changed, 0 insertions, 0 deletions