Matthew Wilcox wrote on 2019/5/12 20:37:
On Sun, May 12, 2019 at 12:20:14PM +0300, Amir Goldstein wrote:
On Fri, May 10, 2019 at 5:38 PM yangerkun <yangerkun@xxxxxxxxxx> wrote:
We find the lock of lockref has been catched with cpu 40. And since
there is too much negative dentry in root dentry's d_subdirs, traversing
will spend so long time with holding d_lock of root dentry. So other
thread waiting for the lockref.lock will softlockup.
IMO, this is DoS that can be manifested in several other ways.
__fsnotify_update_child_dentry_flags() is just a private case of single
level d_walk(). Many other uses of d_walk(), such as path_has_submounts()
will exhibit the same behavior under similar DoS.
Here is a link to a discussion of a similar issue with negative dentries:
https://lore.kernel.org/lkml/187ee69a-451d-adaa-0714-2acbefc46d2f@xxxxxxxxxx/
I suppose we can think of better ways to iterate all non-negative
child dentries,
like keep them all at the tail of d_subdirs, but not sure about the implications
of moving the dentry in the list on d_instantiate().
We don't really have to move the dentries that turn negative (i.e. d_delete()),
because those are not likely to be the real source of DoS.
We should probably be more aggressive about reclaiming negative dentries.
It's clearly ludicrous to have a million negative dentries in a single
directory, for example.
How about add a reference to record allocation of negative under one
dir, and once it come to the limit, we return false directly in
retain_dentry while wo do dput?
Thanks,
Kun.
.