aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLi Zefan <lizf@cn.fujitsu.com>2009-07-07 01:54:48 -0400
committerIngo Molnar <mingo@elte.hu>2009-07-10 05:59:43 -0400
commit558df6c8f74ac4a0b9026ef85b0028280f364d96 (patch)
treee44aed98dfe7e410a87e813a226bfa57bbd67e74
parent0d109c8f70eab8b9f693bd5caea23012394e4876 (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.c61
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,
179int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) 179int 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;
220err:
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
223static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf, 228static 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; 322out:
322 goto unlock_ret_path; 323 mutex_unlock(&ksym_tracer_mutex);
323 324
324err_ret:
325 kfree(input_string); 325 kfree(input_string);
326 326
327unlock_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 }