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 | |
| 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>
| -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 | } | 
