From 510fef4d69be119959946f01ccf8498278f9b309 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Thu, 9 May 2019 09:26:32 +0800 Subject: [PATCH] fs/dcache.c: avoid panic while lockref of dentry overflow euler inclusion category: bugfix bugzilla: 14351 CVE: NA --------------------------- We use lockref for dentry reference without notice that so many negative dentry under one dir can lead to overflow of lockref. This can lead to system crash if we do this under root dir. Since there is not a perfect solution, we just limit max number of dentry count up to INT_MAX / 2. Also, it will cost a lot of time from INT_MAX / 2 to INT_MAX, so we no need to do this under protection of dentry lock. Also, we limit the FILES_MAX to INT_MAX / 2, since a lot open for same file can lead to overflow too. Changelog: v1->v2: add a function to do check / add a Macro to mean INT_MAX / 2 Signed-off-by: yangerkun Reviewed-by: Miao Xie Signed-off-by: Yang Yingliang --- fs/dcache.c | 32 ++++++++++++++++++++++++++++---- fs/filescontrol.c | 2 +- include/linux/fs.h | 3 +++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 4361940af334..6c7ff1bbf195 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1682,6 +1682,18 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) return dentry; } +static inline bool d_forbid_overflow(struct dentry *dentry) +{ + if (unlikely(d_count(dentry) >= D_COUNT_MAX)) { + shrink_dcache_parent(dentry); + + if (d_count(dentry) >= D_COUNT_MAX) + return false; + } + + return true; +} + /** * d_alloc - allocate a dcache entry * @parent: parent of entry to allocate @@ -1693,9 +1705,15 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) */ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) { - struct dentry *dentry = __d_alloc(parent->d_sb, name); + struct dentry *dentry = NULL; + + if (unlikely(!d_forbid_overflow(parent))) + goto out; + + dentry = __d_alloc(parent->d_sb, name); if (!dentry) - return NULL; + goto out; + dentry->d_flags |= DCACHE_RCUACCESS; spin_lock(&parent->d_lock); /* @@ -1706,7 +1724,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_parent = parent; list_add(&dentry->d_child, &parent->d_subdirs); spin_unlock(&parent->d_lock); - +out: return dentry; } EXPORT_SYMBOL(d_alloc); @@ -1719,11 +1737,17 @@ EXPORT_SYMBOL(d_alloc_anon); struct dentry *d_alloc_cursor(struct dentry * parent) { - struct dentry *dentry = d_alloc_anon(parent->d_sb); + struct dentry *dentry = NULL; + + if (unlikely(!d_forbid_overflow(parent))) + goto out; + + dentry = d_alloc_anon(parent->d_sb); if (dentry) { dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR; dentry->d_parent = dget(parent); } +out: return dentry; } diff --git a/fs/filescontrol.c b/fs/filescontrol.c index 5fba9c376dd5..fbaeacbe2de8 100644 --- a/fs/filescontrol.c +++ b/fs/filescontrol.c @@ -26,7 +26,7 @@ #include #include -#define FILES_MAX ULLONG_MAX +#define FILES_MAX D_COUNT_MAX #define FILES_MAX_STR "max" diff --git a/include/linux/fs.h b/include/linux/fs.h index 09d0646acf66..dfe4b0d98bde 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -42,6 +42,9 @@ #include #include +#define D_COUNT_MAX (INT_MAX / 2) + + struct backing_dev_info; struct bdi_writeback; struct bio; -- GitLab