Commit a35780005eb256eb5ec83ffcc802967295887a45

Authored by Lai Jiangshan
Committed by Ingo Molnar
1 parent d8ea37d5de

tracing/workqueues: Add refcnt to struct cpu_workqueue_stats

The stat entries can be freed when the stat file is being read.
The worse is, the ptr can be freed immediately after it's returned
from workqueue_stat_start/next().

Add a refcnt to struct cpu_workqueue_stats to avoid use-after-free.

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <4A51B16F.6010608@cn.fujitsu.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

Showing 1 changed file with 26 additions and 6 deletions Side-by-side Diff

kernel/trace/trace_workqueue.c
... ... @@ -9,6 +9,7 @@
9 9 #include <trace/events/workqueue.h>
10 10 #include <linux/list.h>
11 11 #include <linux/percpu.h>
  12 +#include <linux/kref.h>
12 13 #include "trace_stat.h"
13 14 #include "trace.h"
14 15  
... ... @@ -16,6 +17,7 @@
16 17 /* A cpu workqueue thread */
17 18 struct cpu_workqueue_stats {
18 19 struct list_head list;
  20 + struct kref kref;
19 21 int cpu;
20 22 pid_t pid;
21 23 /* Can be inserted from interrupt or user context, need to be atomic */
... ... @@ -39,6 +41,11 @@
39 41 static DEFINE_PER_CPU(struct workqueue_global_stats, all_workqueue_stat);
40 42 #define workqueue_cpu_stat(cpu) (&per_cpu(all_workqueue_stat, cpu))
41 43  
  44 +static void cpu_workqueue_stat_free(struct kref *kref)
  45 +{
  46 + kfree(container_of(kref, struct cpu_workqueue_stats, kref));
  47 +}
  48 +
42 49 /* Insertion of a work */
43 50 static void
44 51 probe_workqueue_insertion(struct task_struct *wq_thread,
45 52  
... ... @@ -96,8 +103,8 @@
96 103 return;
97 104 }
98 105 INIT_LIST_HEAD(&cws->list);
  106 + kref_init(&cws->kref);
99 107 cws->cpu = cpu;
100   -
101 108 cws->pid = wq_thread->pid;
102 109  
103 110 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
... ... @@ -118,7 +125,7 @@
118 125 list) {
119 126 if (node->pid == wq_thread->pid) {
120 127 list_del(&node->list);
121   - kfree(node);
  128 + kref_put(&node->kref, cpu_workqueue_stat_free);
122 129 goto found;
123 130 }
124 131 }
125 132  
... ... @@ -137,9 +144,11 @@
137 144  
138 145 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
139 146  
140   - if (!list_empty(&workqueue_cpu_stat(cpu)->list))
  147 + if (!list_empty(&workqueue_cpu_stat(cpu)->list)) {
141 148 ret = list_entry(workqueue_cpu_stat(cpu)->list.next,
142 149 struct cpu_workqueue_stats, list);
  150 + kref_get(&ret->kref);
  151 + }
143 152  
144 153 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags);
145 154  
146 155  
... ... @@ -162,9 +171,9 @@
162 171 static void *workqueue_stat_next(void *prev, int idx)
163 172 {
164 173 struct cpu_workqueue_stats *prev_cws = prev;
  174 + struct cpu_workqueue_stats *ret;
165 175 int cpu = prev_cws->cpu;
166 176 unsigned long flags;
167   - void *ret = NULL;
168 177  
169 178 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
170 179 if (list_is_last(&prev_cws->list, &workqueue_cpu_stat(cpu)->list)) {
171 180  
... ... @@ -175,11 +184,14 @@
175 184 return NULL;
176 185 } while (!(ret = workqueue_stat_start_cpu(cpu)));
177 186 return ret;
  187 + } else {
  188 + ret = list_entry(prev_cws->list.next,
  189 + struct cpu_workqueue_stats, list);
  190 + kref_get(&ret->kref);
178 191 }
179 192 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags);
180 193  
181   - return list_entry(prev_cws->list.next, struct cpu_workqueue_stats,
182   - list);
  194 + return ret;
183 195 }
184 196  
185 197 static int workqueue_stat_show(struct seq_file *s, void *p)
... ... @@ -203,6 +215,13 @@
203 215 return 0;
204 216 }
205 217  
  218 +static void workqueue_stat_release(void *stat)
  219 +{
  220 + struct cpu_workqueue_stats *node = stat;
  221 +
  222 + kref_put(&node->kref, cpu_workqueue_stat_free);
  223 +}
  224 +
206 225 static int workqueue_stat_headers(struct seq_file *s)
207 226 {
208 227 seq_printf(s, "# CPU INSERTED EXECUTED NAME\n");
... ... @@ -215,6 +234,7 @@
215 234 .stat_start = workqueue_stat_start,
216 235 .stat_next = workqueue_stat_next,
217 236 .stat_show = workqueue_stat_show,
  237 + .stat_release = workqueue_stat_release,
218 238 .stat_headers = workqueue_stat_headers
219 239 };
220 240