From: Yufen Yu yuyufen@huawei.com
hulk inclusion category: bugfix bugzilla: 30109 CVE: NA ---------------------------
bdi->dev may be set as "NULL" or freed by bdi_unregister(). To avoid causing "NULL" pointer reference or use-after-free in user, we add a common function bdi_get_dev_name(), in which dev is protected by RCU lock. Then, the caller can get device name safely.
Fixes: 5ca4579ae59b ("bdi: fix use-after-free for the bdi device") Signed-off-by: Yufen Yu yuyufen@huawei.com Reviewed-by: Hou Tao houao1@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- block/bfq-iosched.c | 7 +++++-- block/blk-core.c | 4 +++- fs/fs-writeback.c | 4 +++- include/linux/backing-dev.h | 15 +++++++++++++++ include/trace/events/wbt.h | 12 ++++-------- include/trace/events/writeback.h | 18 ++++++++---------- 6 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 66b1ebc..73ef266 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -132,6 +132,7 @@ #include <linux/ioprio.h> #include <linux/sbitmap.h> #include <linux/delay.h> +#include <linux/backing-dev.h>
#include "blk.h" #include "blk-mq.h" @@ -4204,6 +4205,7 @@ static void bfq_exit_icq(struct io_cq *icq) struct task_struct *tsk = current; int ioprio_class; struct bfq_data *bfqd = bfqq->bfqd; + char dname[BDI_DEV_NAME_LEN];
if (!bfqd) return; @@ -4211,8 +4213,9 @@ static void bfq_exit_icq(struct io_cq *icq) ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); switch (ioprio_class) { default: - dev_err(bfqq->bfqd->queue->backing_dev_info->dev, - "bfq: bad prio class %d\n", ioprio_class); + bdi_get_dev_name(bfqq->bfqd->queue->backing_dev_info, + dname, BDI_DEV_NAME_LEN); + pr_err("%s bfq: bad prio class %d\n", dname, ioprio_class); /* fall through */ case IOPRIO_CLASS_NONE: /* diff --git a/block/blk-core.c b/block/blk-core.c index a52a2f5..b64dec2 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1353,6 +1353,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op, const bool is_sync = op_is_sync(op); int may_queue; req_flags_t rq_flags = RQF_ALLOCED; + char dname[BDI_DEV_NAME_LEN];
lockdep_assert_held(q->queue_lock);
@@ -1474,8 +1475,9 @@ static struct request *__get_request(struct request_list *rl, unsigned int op, * shouldn't stall IO. Treat this request as !elvpriv. This will * disturb iosched and blkcg but weird is bettern than dead. */ + bdi_get_dev_name(q->backing_dev_info, dname, BDI_DEV_NAME_LEN); printk_ratelimited(KERN_WARNING "%s: dev %s: request aux data allocation failed, iosched may be disturbed\n", - __func__, dev_name(q->backing_dev_info->dev)); + __func__, dname);
rq->rq_flags &= ~RQF_ELVPRIV; rq->elv.icq = NULL; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index a89e273..027e504 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1980,8 +1980,10 @@ void wb_workfn(struct work_struct *work) struct bdi_writeback *wb = container_of(to_delayed_work(work), struct bdi_writeback, dwork); long pages_written; + char dname[BDI_DEV_NAME_LEN];
- set_worker_desc("flush-%s", dev_name(wb->bdi->dev)); + bdi_get_dev_name(wb->bdi, dname, BDI_DEV_NAME_LEN); + set_worker_desc("flush-%s", dname); current->flags |= PF_SWAPWRITE;
if (likely(!current_is_workqueue_rescuer() || diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index c28a47c..947b046 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -18,6 +18,8 @@ #include <linux/backing-dev-defs.h> #include <linux/slab.h>
+#define BDI_DEV_NAME_LEN 32 + static inline struct backing_dev_info *bdi_get(struct backing_dev_info *bdi) { kref_get(&bdi->refcnt); @@ -498,4 +500,17 @@ static inline int bdi_rw_congested(struct backing_dev_info *bdi) (1 << WB_async_congested)); }
+static inline void bdi_get_dev_name(struct backing_dev_info *bdi, char *dname, + int len) +{ + struct rcu_device *rcu_dev; + + rcu_read_lock(); + + rcu_dev = rcu_dereference(bdi->rcu_dev); + strlcpy(dname, rcu_dev ? dev_name(&rcu_dev->dev) : "(unknown)", len); + + rcu_read_unlock(); +} + #endif /* _LINUX_BACKING_DEV_H */ diff --git a/include/trace/events/wbt.h b/include/trace/events/wbt.h index 37342a1..5dc52ef 100644 --- a/include/trace/events/wbt.h +++ b/include/trace/events/wbt.h @@ -33,8 +33,7 @@ ),
TP_fast_assign( - strlcpy(__entry->name, dev_name(bdi->dev), - ARRAY_SIZE(__entry->name)); + bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name)); __entry->rmean = stat[0].mean; __entry->rmin = stat[0].min; __entry->rmax = stat[0].max; @@ -68,8 +67,7 @@ ),
TP_fast_assign( - strlcpy(__entry->name, dev_name(bdi->dev), - ARRAY_SIZE(__entry->name)); + bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name)); __entry->lat = div_u64(lat, 1000); ),
@@ -105,8 +103,7 @@ ),
TP_fast_assign( - strlcpy(__entry->name, dev_name(bdi->dev), - ARRAY_SIZE(__entry->name)); + bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name)); __entry->msg = msg; __entry->step = step; __entry->window = div_u64(window, 1000); @@ -141,8 +138,7 @@ ),
TP_fast_assign( - strlcpy(__entry->name, dev_name(bdi->dev), - ARRAY_SIZE(__entry->name)); + bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name)); __entry->status = status; __entry->step = step; __entry->inflight = inflight; diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 32db72c..fbba9d2 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -96,8 +96,7 @@ struct backing_dev_info *bdi = inode_to_bdi(inode);
/* may be called for files on pseudo FSes w/ unregistered bdi */ - strncpy(__entry->name, - bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); + bdi_get_dev_name(bdi, __entry->name, 32); __entry->ino = inode->i_ino; __entry->state = inode->i_state; __entry->flags = flags; @@ -220,8 +219,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w __field(unsigned int, cgroup_ino) ), TP_fast_assign( - strncpy(__entry->name, - wb->bdi->dev ? dev_name(wb->bdi->dev) : "(unknown)", 32); + bdi_get_dev_name(wb->bdi, __entry->name, 32); __entry->nr_pages = work->nr_pages; __entry->sb_dev = work->sb ? work->sb->s_dev : 0; __entry->sync_mode = work->sync_mode; @@ -274,7 +272,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w __field(unsigned int, cgroup_ino) ), TP_fast_assign( - strncpy(__entry->name, dev_name(wb->bdi->dev), 32); + bdi_get_dev_name(wb->bdi, __entry->name, 32); __entry->cgroup_ino = __trace_wb_assign_cgroup(wb); ), TP_printk("bdi %s: cgroup_ino=%u", @@ -296,7 +294,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w __array(char, name, 32) ), TP_fast_assign( - strncpy(__entry->name, dev_name(bdi->dev), 32); + bdi_get_dev_name(bdi, __entry->name, 32); ), TP_printk("bdi %s", __entry->name @@ -321,7 +319,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w ),
TP_fast_assign( - strncpy(__entry->name, dev_name(bdi->dev), 32); + bdi_get_dev_name(bdi, __entry->name, 32); __entry->nr_to_write = wbc->nr_to_write; __entry->pages_skipped = wbc->pages_skipped; __entry->sync_mode = wbc->sync_mode; @@ -372,7 +370,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w ), TP_fast_assign( unsigned long *older_than_this = work->older_than_this; - strncpy(__entry->name, dev_name(wb->bdi->dev), 32); + bdi_get_dev_name(wb->bdi, __entry->name, 32); __entry->older = older_than_this ? *older_than_this : 0; __entry->age = older_than_this ? (jiffies - *older_than_this) * 1000 / HZ : -1; @@ -458,7 +456,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w ),
TP_fast_assign( - strlcpy(__entry->bdi, dev_name(wb->bdi->dev), 32); + bdi_get_dev_name(wb->bdi, __entry->bdi, 32); __entry->write_bw = KBps(wb->write_bandwidth); __entry->avg_write_bw = KBps(wb->avg_write_bandwidth); __entry->dirty_rate = KBps(dirty_rate); @@ -523,7 +521,7 @@ static inline unsigned int __trace_wbc_assign_cgroup(struct writeback_control *w
TP_fast_assign( unsigned long freerun = (thresh + bg_thresh) / 2; - strlcpy(__entry->bdi, dev_name(wb->bdi->dev), 32); + bdi_get_dev_name(wb->bdi, __entry->bdi, 32);
__entry->limit = global_wb_domain.dirty_limit; __entry->setpoint = (global_wb_domain.dirty_limit +