提交 de1b854e 编写于 作者: Y Yufen Yu 提交者: Yang Yingliang

bdi: get device name under rcu protect

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: NYufen Yu <yuyufen@huawei.com>
Reviewed-by: NHou Tao <houao1@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 eafb0009
...@@ -132,6 +132,7 @@ ...@@ -132,6 +132,7 @@
#include <linux/ioprio.h> #include <linux/ioprio.h>
#include <linux/sbitmap.h> #include <linux/sbitmap.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/backing-dev.h>
#include "blk.h" #include "blk.h"
#include "blk-mq.h" #include "blk-mq.h"
...@@ -4204,6 +4205,7 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) ...@@ -4204,6 +4205,7 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic)
struct task_struct *tsk = current; struct task_struct *tsk = current;
int ioprio_class; int ioprio_class;
struct bfq_data *bfqd = bfqq->bfqd; struct bfq_data *bfqd = bfqq->bfqd;
char dname[BDI_DEV_NAME_LEN];
if (!bfqd) if (!bfqd)
return; return;
...@@ -4211,8 +4213,9 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) ...@@ -4211,8 +4213,9 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic)
ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio);
switch (ioprio_class) { switch (ioprio_class) {
default: default:
dev_err(bfqq->bfqd->queue->backing_dev_info->dev, bdi_get_dev_name(bfqq->bfqd->queue->backing_dev_info,
"bfq: bad prio class %d\n", ioprio_class); dname, BDI_DEV_NAME_LEN);
pr_err("%s bfq: bad prio class %d\n", dname, ioprio_class);
/* fall through */ /* fall through */
case IOPRIO_CLASS_NONE: case IOPRIO_CLASS_NONE:
/* /*
......
...@@ -1353,6 +1353,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op, ...@@ -1353,6 +1353,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
const bool is_sync = op_is_sync(op); const bool is_sync = op_is_sync(op);
int may_queue; int may_queue;
req_flags_t rq_flags = RQF_ALLOCED; req_flags_t rq_flags = RQF_ALLOCED;
char dname[BDI_DEV_NAME_LEN];
lockdep_assert_held(q->queue_lock); lockdep_assert_held(q->queue_lock);
...@@ -1474,8 +1475,9 @@ static struct request *__get_request(struct request_list *rl, unsigned int op, ...@@ -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 * shouldn't stall IO. Treat this request as !elvpriv. This will
* disturb iosched and blkcg but weird is bettern than dead. * 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", 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->rq_flags &= ~RQF_ELVPRIV;
rq->elv.icq = NULL; rq->elv.icq = NULL;
......
...@@ -1980,8 +1980,10 @@ void wb_workfn(struct work_struct *work) ...@@ -1980,8 +1980,10 @@ void wb_workfn(struct work_struct *work)
struct bdi_writeback *wb = container_of(to_delayed_work(work), struct bdi_writeback *wb = container_of(to_delayed_work(work),
struct bdi_writeback, dwork); struct bdi_writeback, dwork);
long pages_written; 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; current->flags |= PF_SWAPWRITE;
if (likely(!current_is_workqueue_rescuer() || if (likely(!current_is_workqueue_rescuer() ||
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/backing-dev-defs.h> #include <linux/backing-dev-defs.h>
#include <linux/slab.h> #include <linux/slab.h>
#define BDI_DEV_NAME_LEN 32
static inline struct backing_dev_info *bdi_get(struct backing_dev_info *bdi) static inline struct backing_dev_info *bdi_get(struct backing_dev_info *bdi)
{ {
kref_get(&bdi->refcnt); kref_get(&bdi->refcnt);
...@@ -498,4 +500,17 @@ static inline int bdi_rw_congested(struct backing_dev_info *bdi) ...@@ -498,4 +500,17 @@ static inline int bdi_rw_congested(struct backing_dev_info *bdi)
(1 << WB_async_congested)); (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 */ #endif /* _LINUX_BACKING_DEV_H */
...@@ -33,8 +33,7 @@ TRACE_EVENT(wbt_stat, ...@@ -33,8 +33,7 @@ TRACE_EVENT(wbt_stat,
), ),
TP_fast_assign( TP_fast_assign(
strlcpy(__entry->name, dev_name(bdi->dev), bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name));
ARRAY_SIZE(__entry->name));
__entry->rmean = stat[0].mean; __entry->rmean = stat[0].mean;
__entry->rmin = stat[0].min; __entry->rmin = stat[0].min;
__entry->rmax = stat[0].max; __entry->rmax = stat[0].max;
...@@ -68,8 +67,7 @@ TRACE_EVENT(wbt_lat, ...@@ -68,8 +67,7 @@ TRACE_EVENT(wbt_lat,
), ),
TP_fast_assign( TP_fast_assign(
strlcpy(__entry->name, dev_name(bdi->dev), bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name));
ARRAY_SIZE(__entry->name));
__entry->lat = div_u64(lat, 1000); __entry->lat = div_u64(lat, 1000);
), ),
...@@ -105,8 +103,7 @@ TRACE_EVENT(wbt_step, ...@@ -105,8 +103,7 @@ TRACE_EVENT(wbt_step,
), ),
TP_fast_assign( TP_fast_assign(
strlcpy(__entry->name, dev_name(bdi->dev), bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name));
ARRAY_SIZE(__entry->name));
__entry->msg = msg; __entry->msg = msg;
__entry->step = step; __entry->step = step;
__entry->window = div_u64(window, 1000); __entry->window = div_u64(window, 1000);
...@@ -141,8 +138,7 @@ TRACE_EVENT(wbt_timer, ...@@ -141,8 +138,7 @@ TRACE_EVENT(wbt_timer,
), ),
TP_fast_assign( TP_fast_assign(
strlcpy(__entry->name, dev_name(bdi->dev), bdi_get_dev_name(bdi, __entry->name, ARRAY_SIZE(__entry->name));
ARRAY_SIZE(__entry->name));
__entry->status = status; __entry->status = status;
__entry->step = step; __entry->step = step;
__entry->inflight = inflight; __entry->inflight = inflight;
......
...@@ -96,8 +96,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, ...@@ -96,8 +96,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template,
struct backing_dev_info *bdi = inode_to_bdi(inode); struct backing_dev_info *bdi = inode_to_bdi(inode);
/* may be called for files on pseudo FSes w/ unregistered bdi */ /* may be called for files on pseudo FSes w/ unregistered bdi */
strncpy(__entry->name, bdi_get_dev_name(bdi, __entry->name, 32);
bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32);
__entry->ino = inode->i_ino; __entry->ino = inode->i_ino;
__entry->state = inode->i_state; __entry->state = inode->i_state;
__entry->flags = flags; __entry->flags = flags;
...@@ -220,8 +219,7 @@ DECLARE_EVENT_CLASS(writeback_work_class, ...@@ -220,8 +219,7 @@ DECLARE_EVENT_CLASS(writeback_work_class,
__field(unsigned int, cgroup_ino) __field(unsigned int, cgroup_ino)
), ),
TP_fast_assign( TP_fast_assign(
strncpy(__entry->name, bdi_get_dev_name(wb->bdi, __entry->name, 32);
wb->bdi->dev ? dev_name(wb->bdi->dev) : "(unknown)", 32);
__entry->nr_pages = work->nr_pages; __entry->nr_pages = work->nr_pages;
__entry->sb_dev = work->sb ? work->sb->s_dev : 0; __entry->sb_dev = work->sb ? work->sb->s_dev : 0;
__entry->sync_mode = work->sync_mode; __entry->sync_mode = work->sync_mode;
...@@ -274,7 +272,7 @@ DECLARE_EVENT_CLASS(writeback_class, ...@@ -274,7 +272,7 @@ DECLARE_EVENT_CLASS(writeback_class,
__field(unsigned int, cgroup_ino) __field(unsigned int, cgroup_ino)
), ),
TP_fast_assign( 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); __entry->cgroup_ino = __trace_wb_assign_cgroup(wb);
), ),
TP_printk("bdi %s: cgroup_ino=%u", TP_printk("bdi %s: cgroup_ino=%u",
...@@ -296,7 +294,7 @@ TRACE_EVENT(writeback_bdi_register, ...@@ -296,7 +294,7 @@ TRACE_EVENT(writeback_bdi_register,
__array(char, name, 32) __array(char, name, 32)
), ),
TP_fast_assign( TP_fast_assign(
strncpy(__entry->name, dev_name(bdi->dev), 32); bdi_get_dev_name(bdi, __entry->name, 32);
), ),
TP_printk("bdi %s", TP_printk("bdi %s",
__entry->name __entry->name
...@@ -321,7 +319,7 @@ DECLARE_EVENT_CLASS(wbc_class, ...@@ -321,7 +319,7 @@ DECLARE_EVENT_CLASS(wbc_class,
), ),
TP_fast_assign( 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->nr_to_write = wbc->nr_to_write;
__entry->pages_skipped = wbc->pages_skipped; __entry->pages_skipped = wbc->pages_skipped;
__entry->sync_mode = wbc->sync_mode; __entry->sync_mode = wbc->sync_mode;
...@@ -372,7 +370,7 @@ TRACE_EVENT(writeback_queue_io, ...@@ -372,7 +370,7 @@ TRACE_EVENT(writeback_queue_io,
), ),
TP_fast_assign( TP_fast_assign(
unsigned long *older_than_this = work->older_than_this; 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->older = older_than_this ? *older_than_this : 0;
__entry->age = older_than_this ? __entry->age = older_than_this ?
(jiffies - *older_than_this) * 1000 / HZ : -1; (jiffies - *older_than_this) * 1000 / HZ : -1;
...@@ -458,7 +456,7 @@ TRACE_EVENT(bdi_dirty_ratelimit, ...@@ -458,7 +456,7 @@ TRACE_EVENT(bdi_dirty_ratelimit,
), ),
TP_fast_assign( 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->write_bw = KBps(wb->write_bandwidth);
__entry->avg_write_bw = KBps(wb->avg_write_bandwidth); __entry->avg_write_bw = KBps(wb->avg_write_bandwidth);
__entry->dirty_rate = KBps(dirty_rate); __entry->dirty_rate = KBps(dirty_rate);
...@@ -523,7 +521,7 @@ TRACE_EVENT(balance_dirty_pages, ...@@ -523,7 +521,7 @@ TRACE_EVENT(balance_dirty_pages,
TP_fast_assign( TP_fast_assign(
unsigned long freerun = (thresh + bg_thresh) / 2; 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->limit = global_wb_domain.dirty_limit;
__entry->setpoint = (global_wb_domain.dirty_limit + __entry->setpoint = (global_wb_domain.dirty_limit +
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册