diff options
author | Li Zefan <lizf@cn.fujitsu.com> | 2009-07-07 01:54:48 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-07-10 05:59:43 -0400 |
commit | 558df6c8f74ac4a0b9026ef85b0028280f364d96 (patch) | |
tree | e44aed98dfe7e410a87e813a226bfa57bbd67e74 /kernel/trace/trace_ksym.c | |
parent | 0d109c8f70eab8b9f693bd5caea23012394e4876 (diff) |
ksym_tracer: Fix memory leak
- When remove a filter, we leak entry->ksym_hbp->info.name.
- With CONFIG_FTRAC_SELFTEST enabled, we leak ->info.name:
# echo ksym_tracer > current_tracer
# echo 'ksym_selftest_dummy:rw-' > ksym_trace_filter
# echo nop > current_tracer
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: "K.Prasad" <prasad@linux.vnet.ibm.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <4A52E328.8010200@cn.fujitsu.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_ksym.c')
-rw-r--r-- | kernel/trace/trace_ksym.c | 61 |
1 files changed, 27 insertions, 34 deletions
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c index 891e3b86b3f6..7d349d34a0d1 100644 --- a/kernel/trace/trace_ksym.c +++ b/kernel/trace/trace_ksym.c | |||
@@ -179,7 +179,7 @@ static int parse_ksym_trace_str(char *input_string, char **ksymname, | |||
179 | int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | 179 | int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) |
180 | { | 180 | { |
181 | struct trace_ksym *entry; | 181 | struct trace_ksym *entry; |
182 | int ret; | 182 | int ret = -ENOMEM; |
183 | 183 | ||
184 | if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { | 184 | if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { |
185 | printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" | 185 | printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" |
@@ -193,12 +193,13 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | |||
193 | return -ENOMEM; | 193 | return -ENOMEM; |
194 | 194 | ||
195 | entry->ksym_hbp = kzalloc(sizeof(struct hw_breakpoint), GFP_KERNEL); | 195 | entry->ksym_hbp = kzalloc(sizeof(struct hw_breakpoint), GFP_KERNEL); |
196 | if (!entry->ksym_hbp) { | 196 | if (!entry->ksym_hbp) |
197 | kfree(entry); | 197 | goto err; |
198 | return -ENOMEM; | 198 | |
199 | } | 199 | entry->ksym_hbp->info.name = kstrdup(ksymname, GFP_KERNEL); |
200 | if (!entry->ksym_hbp->info.name) | ||
201 | goto err; | ||
200 | 202 | ||
201 | entry->ksym_hbp->info.name = ksymname; | ||
202 | entry->ksym_hbp->info.type = op; | 203 | entry->ksym_hbp->info.type = op; |
203 | entry->ksym_addr = entry->ksym_hbp->info.address = addr; | 204 | entry->ksym_addr = entry->ksym_hbp->info.address = addr; |
204 | #ifdef CONFIG_X86 | 205 | #ifdef CONFIG_X86 |
@@ -210,14 +211,18 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | |||
210 | if (ret < 0) { | 211 | if (ret < 0) { |
211 | printk(KERN_INFO "ksym_tracer request failed. Try again" | 212 | printk(KERN_INFO "ksym_tracer request failed. Try again" |
212 | " later!!\n"); | 213 | " later!!\n"); |
213 | kfree(entry->ksym_hbp); | 214 | ret = -EAGAIN; |
214 | kfree(entry); | 215 | goto err; |
215 | return -EAGAIN; | ||
216 | } | 216 | } |
217 | hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); | 217 | hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); |
218 | ksym_filter_entry_count++; | 218 | ksym_filter_entry_count++; |
219 | |||
220 | return 0; | 219 | return 0; |
220 | err: | ||
221 | if (entry->ksym_hbp) | ||
222 | kfree(entry->ksym_hbp->info.name); | ||
223 | kfree(entry->ksym_hbp); | ||
224 | kfree(entry); | ||
225 | return ret; | ||
221 | } | 226 | } |
222 | 227 | ||
223 | static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf, | 228 | static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf, |
@@ -289,7 +294,7 @@ static ssize_t ksym_trace_filter_write(struct file *file, | |||
289 | if (entry->ksym_hbp->info.type != op) | 294 | if (entry->ksym_hbp->info.type != op) |
290 | changed = 1; | 295 | changed = 1; |
291 | else | 296 | else |
292 | goto err_ret; | 297 | goto out; |
293 | break; | 298 | break; |
294 | } | 299 | } |
295 | } | 300 | } |
@@ -298,34 +303,29 @@ static ssize_t ksym_trace_filter_write(struct file *file, | |||
298 | entry->ksym_hbp->info.type = op; | 303 | entry->ksym_hbp->info.type = op; |
299 | if (op > 0) { | 304 | if (op > 0) { |
300 | ret = register_kernel_hw_breakpoint(entry->ksym_hbp); | 305 | ret = register_kernel_hw_breakpoint(entry->ksym_hbp); |
301 | if (ret == 0) { | 306 | if (ret == 0) |
302 | ret = count; | 307 | goto out; |
303 | goto unlock_ret_path; | 308 | } |
304 | } | ||
305 | } else | ||
306 | ret = count; | ||
307 | ksym_filter_entry_count--; | 309 | ksym_filter_entry_count--; |
308 | hlist_del_rcu(&(entry->ksym_hlist)); | 310 | hlist_del_rcu(&(entry->ksym_hlist)); |
309 | synchronize_rcu(); | 311 | synchronize_rcu(); |
312 | kfree(entry->ksym_hbp->info.name); | ||
310 | kfree(entry->ksym_hbp); | 313 | kfree(entry->ksym_hbp); |
311 | kfree(entry); | 314 | kfree(entry); |
312 | goto err_ret; | 315 | goto out; |
313 | } else { | 316 | } else { |
314 | /* Check for malformed request: (4) */ | 317 | /* Check for malformed request: (4) */ |
315 | if (op == 0) | 318 | if (op == 0) |
316 | goto err_ret; | 319 | goto out; |
317 | ret = process_new_ksym_entry(ksymname, op, ksym_addr); | 320 | ret = process_new_ksym_entry(ksymname, op, ksym_addr); |
318 | if (ret) | ||
319 | goto err_ret; | ||
320 | } | 321 | } |
321 | ret = count; | 322 | out: |
322 | goto unlock_ret_path; | 323 | mutex_unlock(&ksym_tracer_mutex); |
323 | 324 | ||
324 | err_ret: | ||
325 | kfree(input_string); | 325 | kfree(input_string); |
326 | 326 | ||
327 | unlock_ret_path: | 327 | if (!ret) |
328 | mutex_unlock(&ksym_tracer_mutex); | 328 | ret = count; |
329 | return ret; | 329 | return ret; |
330 | } | 330 | } |
331 | 331 | ||
@@ -349,14 +349,7 @@ static void ksym_trace_reset(struct trace_array *tr) | |||
349 | ksym_filter_entry_count--; | 349 | ksym_filter_entry_count--; |
350 | hlist_del_rcu(&(entry->ksym_hlist)); | 350 | hlist_del_rcu(&(entry->ksym_hlist)); |
351 | synchronize_rcu(); | 351 | synchronize_rcu(); |
352 | /* Free the 'input_string' only if reset | 352 | kfree(entry->ksym_hbp->info.name); |
353 | * after startup self-test | ||
354 | */ | ||
355 | #ifdef CONFIG_FTRACE_SELFTEST | ||
356 | if (strncmp(entry->ksym_hbp->info.name, KSYM_SELFTEST_ENTRY, | ||
357 | strlen(KSYM_SELFTEST_ENTRY)) != 0) | ||
358 | #endif /* CONFIG_FTRACE_SELFTEST*/ | ||
359 | kfree(entry->ksym_hbp->info.name); | ||
360 | kfree(entry->ksym_hbp); | 353 | kfree(entry->ksym_hbp); |
361 | kfree(entry); | 354 | kfree(entry); |
362 | } | 355 | } |