提交 6c4c27ea 编写于 作者: Z zhengbin 提交者: Xie XiuQi

Revert "lockless next_positive()"

hulk inclusion
category: bugfix
bugzilla: 20810
CVE: NA

---------------------------

[ This reverts commit ebaaa80e ("lockless next_positive()"),
To avoid the following oops. Of course, we can still find a
better way, I suggest revert this first. ]

Use a script to test kernel(arm64) in the following steps:
(filesystem is tmpfs, dirA already have 10 files, dirB
have 12 files)
1. keep open filenotexist(O_RDWR) in dirA
2. keep open filenotexist(O_RDWR) in dirB
3. keep ls dirB

After 10 minutes, there will be an oops:
Unable to handle kernel paging request at virtual address 00000000003564ad
Process ls (pid: 142652, stack limit = 0x0000000055c452f6)
Call trace:
 dcache_readdir+0xf8/0x1b0
 iterate_dir+0x8c/0x1a8
 ksys_getdents64+0xa4/0x190
 __arm64_sys_getdents64+0x28/0x38
 el0_svc_common+0x78/0x130
 el0_svc_handler+0x38/0x78
 el0_svc+0x8/0xc

The reason is as follows:
1. dirA create new dentryA(dentryA->next = fileA1), and will delete it
lookup_open
  d_alloc_parallel
    d_alloc
    dput -->prev allocated dentry has been added to dentry_hashtable

dput remove dentryA from dirA, dentryA->next is still fileA1.

2. dirB create new dentry(use dentryA), and add it to dirB
d_alloc -->This will need dirB shared lock
   __d_alloc
     INIT_LIST_HEAD(&dentry->d_child)
   spin_lock(&parent->d_lock)
   list_add(&dentry->d_child, &parent->d_subdirs)

3. At the same time, ls dirB -->This will need dirB shared lock
dcache_readdir
  p = &dentry->d_subdirs
  next_positive
    p = from->next

Although d_alloc has spin_lock, next_positive does not have it since
commit ebaaa80e ("lockless next_positive()").

In arm64, CPU may run out of order. Maybe set parent->d_subdirs->next
first, while dentry->d_child.next is still uninitialized.

dentryA->next is still fileA1, So ls dirB will goto fileA1 which
belongs to dirA, thus oops happens.
Signed-off-by: Nzhengbin <zhengbin13@huawei.com>
Reviewed-by: Nzhangyi (F) <yi.zhang@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 6701d806
...@@ -90,53 +90,31 @@ static struct dentry *next_positive(struct dentry *parent, ...@@ -90,53 +90,31 @@ static struct dentry *next_positive(struct dentry *parent,
struct list_head *from, struct list_head *from,
int count) int count)
{ {
unsigned *seq = &parent->d_inode->i_dir_seq, n; struct dentry *res = NULL;
struct dentry *res;
struct list_head *p; struct list_head *p;
bool skipped;
int i;
retry: spin_lock(&parent->d_lock);
i = count;
skipped = false;
n = smp_load_acquire(seq) & ~1;
res = NULL;
rcu_read_lock();
for (p = from->next; p != &parent->d_subdirs; p = p->next) { for (p = from->next; p != &parent->d_subdirs; p = p->next) {
struct dentry *d = list_entry(p, struct dentry, d_child); struct dentry *d = list_entry(p, struct dentry, d_child);
if (!simple_positive(d)) { if (simple_positive(d) && !--count) {
skipped = true;
} else if (!--i) {
res = d; res = d;
break; break;
} }
} }
rcu_read_unlock(); spin_unlock(&parent->d_lock);
if (skipped) {
smp_rmb();
if (unlikely(*seq != n))
goto retry;
}
return res; return res;
} }
static void move_cursor(struct dentry *cursor, struct list_head *after) static void move_cursor(struct dentry *cursor, struct list_head *after)
{ {
struct dentry *parent = cursor->d_parent; struct dentry *parent = cursor->d_parent;
unsigned n, *seq = &parent->d_inode->i_dir_seq;
spin_lock(&parent->d_lock); spin_lock(&parent->d_lock);
for (;;) {
n = *seq;
if (!(n & 1) && cmpxchg(seq, n, n + 1) == n)
break;
cpu_relax();
}
__list_del(cursor->d_child.prev, cursor->d_child.next); __list_del(cursor->d_child.prev, cursor->d_child.next);
if (after) if (after)
list_add(&cursor->d_child, after); list_add(&cursor->d_child, after);
else else
list_add_tail(&cursor->d_child, &parent->d_subdirs); list_add_tail(&cursor->d_child, &parent->d_subdirs);
smp_store_release(seq, n + 2);
spin_unlock(&parent->d_lock); spin_unlock(&parent->d_lock);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册