Skip to content

Commit 5c966f4

Browse files
name2965gregkh
authored andcommitted
mm/vmalloc: fix data race in show_numa_info()
commit 5c5f046 upstream. The following data-race was found in show_numa_info(): ================================================================== BUG: KCSAN: data-race in vmalloc_info_show / vmalloc_info_show read to 0xffff88800971fe30 of 4 bytes by task 8289 on cpu 0: show_numa_info mm/vmalloc.c:4936 [inline] vmalloc_info_show+0x5a8/0x7e0 mm/vmalloc.c:5016 seq_read_iter+0x373/0xb40 fs/seq_file.c:230 proc_reg_read_iter+0x11e/0x170 fs/proc/inode.c:299 .... write to 0xffff88800971fe30 of 4 bytes by task 8287 on cpu 1: show_numa_info mm/vmalloc.c:4934 [inline] vmalloc_info_show+0x38f/0x7e0 mm/vmalloc.c:5016 seq_read_iter+0x373/0xb40 fs/seq_file.c:230 proc_reg_read_iter+0x11e/0x170 fs/proc/inode.c:299 .... value changed: 0x0000008f -> 0x00000000 ================================================================== According to this report,there is a read/write data-race because m->private is accessible to multiple CPUs. To fix this, instead of allocating the heap in proc_vmalloc_init() and passing the heap address to m->private, vmalloc_info_show() should allocate the heap. Link: https://lkml.kernel.org/r/20250508165620.15321-1-aha310510@gmail.com Fixes: 8e1d743 ("mm: vmalloc: support multiple nodes in vmallocinfo") Signed-off-by: Jeongjun Park <aha310510@gmail.com> Suggested-by: Eric Dumazet <edumazet@google.com> Suggested-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: "Uladzislau Rezki (Sony)" <urezki@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 80c25d7 commit 5c966f4

1 file changed

Lines changed: 35 additions & 28 deletions

File tree

mm/vmalloc.c

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3100,7 +3100,7 @@ static void clear_vm_uninitialized_flag(struct vm_struct *vm)
31003100
/*
31013101
* Before removing VM_UNINITIALIZED,
31023102
* we should make sure that vm has proper values.
3103-
* Pair with smp_rmb() in show_numa_info().
3103+
* Pair with smp_rmb() in vread_iter() and vmalloc_info_show().
31043104
*/
31053105
smp_wmb();
31063106
vm->flags &= ~VM_UNINITIALIZED;
@@ -4934,28 +4934,29 @@ bool vmalloc_dump_obj(void *object)
49344934
#endif
49354935

49364936
#ifdef CONFIG_PROC_FS
4937-
static void show_numa_info(struct seq_file *m, struct vm_struct *v)
4938-
{
4939-
if (IS_ENABLED(CONFIG_NUMA)) {
4940-
unsigned int nr, *counters = m->private;
4941-
unsigned int step = 1U << vm_area_page_order(v);
49424937

4943-
if (!counters)
4944-
return;
4938+
/*
4939+
* Print number of pages allocated on each memory node.
4940+
*
4941+
* This function can only be called if CONFIG_NUMA is enabled
4942+
* and VM_UNINITIALIZED bit in v->flags is disabled.
4943+
*/
4944+
static void show_numa_info(struct seq_file *m, struct vm_struct *v,
4945+
unsigned int *counters)
4946+
{
4947+
unsigned int nr;
4948+
unsigned int step = 1U << vm_area_page_order(v);
49454949

4946-
if (v->flags & VM_UNINITIALIZED)
4947-
return;
4948-
/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */
4949-
smp_rmb();
4950+
if (!counters)
4951+
return;
49504952

4951-
memset(counters, 0, nr_node_ids * sizeof(unsigned int));
4953+
memset(counters, 0, nr_node_ids * sizeof(unsigned int));
49524954

4953-
for (nr = 0; nr < v->nr_pages; nr += step)
4954-
counters[page_to_nid(v->pages[nr])] += step;
4955-
for_each_node_state(nr, N_HIGH_MEMORY)
4956-
if (counters[nr])
4957-
seq_printf(m, " N%u=%u", nr, counters[nr]);
4958-
}
4955+
for (nr = 0; nr < v->nr_pages; nr += step)
4956+
counters[page_to_nid(v->pages[nr])] += step;
4957+
for_each_node_state(nr, N_HIGH_MEMORY)
4958+
if (counters[nr])
4959+
seq_printf(m, " N%u=%u", nr, counters[nr]);
49594960
}
49604961

49614962
static void show_purge_info(struct seq_file *m)
@@ -4983,6 +4984,10 @@ static int vmalloc_info_show(struct seq_file *m, void *p)
49834984
struct vmap_area *va;
49844985
struct vm_struct *v;
49854986
int i;
4987+
unsigned int *counters;
4988+
4989+
if (IS_ENABLED(CONFIG_NUMA))
4990+
counters = kmalloc(nr_node_ids * sizeof(unsigned int), GFP_KERNEL);
49864991

49874992
for (i = 0; i < nr_vmap_nodes; i++) {
49884993
vn = &vmap_nodes[i];
@@ -4999,6 +5004,11 @@ static int vmalloc_info_show(struct seq_file *m, void *p)
49995004
}
50005005

50015006
v = va->vm;
5007+
if (v->flags & VM_UNINITIALIZED)
5008+
continue;
5009+
5010+
/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */
5011+
smp_rmb();
50025012

50035013
seq_printf(m, "0x%pK-0x%pK %7ld",
50045014
v->addr, v->addr + v->size, v->size);
@@ -5033,7 +5043,9 @@ static int vmalloc_info_show(struct seq_file *m, void *p)
50335043
if (is_vmalloc_addr(v->pages))
50345044
seq_puts(m, " vpages");
50355045

5036-
show_numa_info(m, v);
5046+
if (IS_ENABLED(CONFIG_NUMA))
5047+
show_numa_info(m, v, counters);
5048+
50375049
seq_putc(m, '\n');
50385050
}
50395051
spin_unlock(&vn->busy.lock);
@@ -5043,19 +5055,14 @@ static int vmalloc_info_show(struct seq_file *m, void *p)
50435055
* As a final step, dump "unpurged" areas.
50445056
*/
50455057
show_purge_info(m);
5058+
if (IS_ENABLED(CONFIG_NUMA))
5059+
kfree(counters);
50465060
return 0;
50475061
}
50485062

50495063
static int __init proc_vmalloc_init(void)
50505064
{
5051-
void *priv_data = NULL;
5052-
5053-
if (IS_ENABLED(CONFIG_NUMA))
5054-
priv_data = kmalloc(nr_node_ids * sizeof(unsigned int), GFP_KERNEL);
5055-
5056-
proc_create_single_data("vmallocinfo",
5057-
0400, NULL, vmalloc_info_show, priv_data);
5058-
5065+
proc_create_single("vmallocinfo", 0400, NULL, vmalloc_info_show);
50595066
return 0;
50605067
}
50615068
module_init(proc_vmalloc_init);

0 commit comments

Comments
 (0)