aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbibo,mao <bibo.mao@intel.com>2006-10-02 05:17:35 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-02 10:57:16 -0400
commit99219a3fbc2dcf2eaa954f7b2ac27299fd7894cd (patch)
tree895abde156c9fbeea9c5a87cfaaa411d4ad175c6
parentf2aa85a0ccd90110e76c6375535adc3ae358f971 (diff)
[PATCH] kretprobe spinlock deadlock patch
kprobe_flush_task() possibly calls kfree function during holding kretprobe_lock spinlock, if kfree function is probed by kretprobe that will incur spinlock deadlock. This patch moves kfree function out scope of kretprobe_lock. Signed-off-by: bibo, mao <bibo.mao@intel.com> Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/i386/kernel/kprobes.c9
-rw-r--r--arch/ia64/kernel/kprobes.c9
-rw-r--r--arch/powerpc/kernel/kprobes.c9
-rw-r--r--arch/s390/kernel/kprobes.c9
-rw-r--r--arch/x86_64/kernel/kprobes.c9
-rw-r--r--include/linux/kprobes.h2
-rw-r--r--kernel/kprobes.c15
7 files changed, 47 insertions, 15 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c
index 7a97544f15a0..d98e44b16fe2 100644
--- a/arch/i386/kernel/kprobes.c
+++ b/arch/i386/kernel/kprobes.c
@@ -396,11 +396,12 @@ no_kprobe:
396fastcall void *__kprobes trampoline_handler(struct pt_regs *regs) 396fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
397{ 397{
398 struct kretprobe_instance *ri = NULL; 398 struct kretprobe_instance *ri = NULL;
399 struct hlist_head *head; 399 struct hlist_head *head, empty_rp;
400 struct hlist_node *node, *tmp; 400 struct hlist_node *node, *tmp;
401 unsigned long flags, orig_ret_address = 0; 401 unsigned long flags, orig_ret_address = 0;
402 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; 402 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
403 403
404 INIT_HLIST_HEAD(&empty_rp);
404 spin_lock_irqsave(&kretprobe_lock, flags); 405 spin_lock_irqsave(&kretprobe_lock, flags);
405 head = kretprobe_inst_table_head(current); 406 head = kretprobe_inst_table_head(current);
406 407
@@ -429,7 +430,7 @@ fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
429 } 430 }
430 431
431 orig_ret_address = (unsigned long)ri->ret_addr; 432 orig_ret_address = (unsigned long)ri->ret_addr;
432 recycle_rp_inst(ri); 433 recycle_rp_inst(ri, &empty_rp);
433 434
434 if (orig_ret_address != trampoline_address) 435 if (orig_ret_address != trampoline_address)
435 /* 436 /*
@@ -444,6 +445,10 @@ fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
444 445
445 spin_unlock_irqrestore(&kretprobe_lock, flags); 446 spin_unlock_irqrestore(&kretprobe_lock, flags);
446 447
448 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
449 hlist_del(&ri->hlist);
450 kfree(ri);
451 }
447 return (void*)orig_ret_address; 452 return (void*)orig_ret_address;
448} 453}
449 454
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c
index 9c9c8fcdfbdc..51217d63285e 100644
--- a/arch/ia64/kernel/kprobes.c
+++ b/arch/ia64/kernel/kprobes.c
@@ -338,12 +338,13 @@ static void kretprobe_trampoline(void)
338int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 338int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
339{ 339{
340 struct kretprobe_instance *ri = NULL; 340 struct kretprobe_instance *ri = NULL;
341 struct hlist_head *head; 341 struct hlist_head *head, empty_rp;
342 struct hlist_node *node, *tmp; 342 struct hlist_node *node, *tmp;
343 unsigned long flags, orig_ret_address = 0; 343 unsigned long flags, orig_ret_address = 0;
344 unsigned long trampoline_address = 344 unsigned long trampoline_address =
345 ((struct fnptr *)kretprobe_trampoline)->ip; 345 ((struct fnptr *)kretprobe_trampoline)->ip;
346 346
347 INIT_HLIST_HEAD(&empty_rp);
347 spin_lock_irqsave(&kretprobe_lock, flags); 348 spin_lock_irqsave(&kretprobe_lock, flags);
348 head = kretprobe_inst_table_head(current); 349 head = kretprobe_inst_table_head(current);
349 350
@@ -369,7 +370,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
369 ri->rp->handler(ri, regs); 370 ri->rp->handler(ri, regs);
370 371
371 orig_ret_address = (unsigned long)ri->ret_addr; 372 orig_ret_address = (unsigned long)ri->ret_addr;
372 recycle_rp_inst(ri); 373 recycle_rp_inst(ri, &empty_rp);
373 374
374 if (orig_ret_address != trampoline_address) 375 if (orig_ret_address != trampoline_address)
375 /* 376 /*
@@ -387,6 +388,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
387 spin_unlock_irqrestore(&kretprobe_lock, flags); 388 spin_unlock_irqrestore(&kretprobe_lock, flags);
388 preempt_enable_no_resched(); 389 preempt_enable_no_resched();
389 390
391 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
392 hlist_del(&ri->hlist);
393 kfree(ri);
394 }
390 /* 395 /*
391 * By returning a non-zero value, we are telling 396 * By returning a non-zero value, we are telling
392 * kprobe_handler() that we don't want the post_handler 397 * kprobe_handler() that we don't want the post_handler
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index 46d2fd0e5789..7b8d12b9026c 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -260,11 +260,12 @@ void kretprobe_trampoline_holder(void)
260int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 260int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
261{ 261{
262 struct kretprobe_instance *ri = NULL; 262 struct kretprobe_instance *ri = NULL;
263 struct hlist_head *head; 263 struct hlist_head *head, empty_rp;
264 struct hlist_node *node, *tmp; 264 struct hlist_node *node, *tmp;
265 unsigned long flags, orig_ret_address = 0; 265 unsigned long flags, orig_ret_address = 0;
266 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; 266 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
267 267
268 INIT_HLIST_HEAD(&empty_rp);
268 spin_lock_irqsave(&kretprobe_lock, flags); 269 spin_lock_irqsave(&kretprobe_lock, flags);
269 head = kretprobe_inst_table_head(current); 270 head = kretprobe_inst_table_head(current);
270 271
@@ -290,7 +291,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
290 ri->rp->handler(ri, regs); 291 ri->rp->handler(ri, regs);
291 292
292 orig_ret_address = (unsigned long)ri->ret_addr; 293 orig_ret_address = (unsigned long)ri->ret_addr;
293 recycle_rp_inst(ri); 294 recycle_rp_inst(ri, &empty_rp);
294 295
295 if (orig_ret_address != trampoline_address) 296 if (orig_ret_address != trampoline_address)
296 /* 297 /*
@@ -308,6 +309,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
308 spin_unlock_irqrestore(&kretprobe_lock, flags); 309 spin_unlock_irqrestore(&kretprobe_lock, flags);
309 preempt_enable_no_resched(); 310 preempt_enable_no_resched();
310 311
312 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
313 hlist_del(&ri->hlist);
314 kfree(ri);
315 }
311 /* 316 /*
312 * By returning a non-zero value, we are telling 317 * By returning a non-zero value, we are telling
313 * kprobe_handler() that we don't want the post_handler 318 * kprobe_handler() that we don't want the post_handler
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index ca28fb0b3790..4d9ff5ce4cbd 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -369,11 +369,12 @@ void __kprobes kretprobe_trampoline_holder(void)
369int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 369int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
370{ 370{
371 struct kretprobe_instance *ri = NULL; 371 struct kretprobe_instance *ri = NULL;
372 struct hlist_head *head; 372 struct hlist_head *head, empty_rp;
373 struct hlist_node *node, *tmp; 373 struct hlist_node *node, *tmp;
374 unsigned long flags, orig_ret_address = 0; 374 unsigned long flags, orig_ret_address = 0;
375 unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; 375 unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
376 376
377 INIT_HLIST_HEAD(&empty_rp);
377 spin_lock_irqsave(&kretprobe_lock, flags); 378 spin_lock_irqsave(&kretprobe_lock, flags);
378 head = kretprobe_inst_table_head(current); 379 head = kretprobe_inst_table_head(current);
379 380
@@ -399,7 +400,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
399 ri->rp->handler(ri, regs); 400 ri->rp->handler(ri, regs);
400 401
401 orig_ret_address = (unsigned long)ri->ret_addr; 402 orig_ret_address = (unsigned long)ri->ret_addr;
402 recycle_rp_inst(ri); 403 recycle_rp_inst(ri, &empty_rp);
403 404
404 if (orig_ret_address != trampoline_address) { 405 if (orig_ret_address != trampoline_address) {
405 /* 406 /*
@@ -417,6 +418,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
417 spin_unlock_irqrestore(&kretprobe_lock, flags); 418 spin_unlock_irqrestore(&kretprobe_lock, flags);
418 preempt_enable_no_resched(); 419 preempt_enable_no_resched();
419 420
421 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
422 hlist_del(&ri->hlist);
423 kfree(ri);
424 }
420 /* 425 /*
421 * By returning a non-zero value, we are telling 426 * By returning a non-zero value, we are telling
422 * kprobe_handler() that we don't want the post_handler 427 * kprobe_handler() that we don't want the post_handler
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c
index d04f0ab2ff40..ac241567e682 100644
--- a/arch/x86_64/kernel/kprobes.c
+++ b/arch/x86_64/kernel/kprobes.c
@@ -405,11 +405,12 @@ no_kprobe:
405int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 405int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
406{ 406{
407 struct kretprobe_instance *ri = NULL; 407 struct kretprobe_instance *ri = NULL;
408 struct hlist_head *head; 408 struct hlist_head *head, empty_rp;
409 struct hlist_node *node, *tmp; 409 struct hlist_node *node, *tmp;
410 unsigned long flags, orig_ret_address = 0; 410 unsigned long flags, orig_ret_address = 0;
411 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; 411 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
412 412
413 INIT_HLIST_HEAD(&empty_rp);
413 spin_lock_irqsave(&kretprobe_lock, flags); 414 spin_lock_irqsave(&kretprobe_lock, flags);
414 head = kretprobe_inst_table_head(current); 415 head = kretprobe_inst_table_head(current);
415 416
@@ -435,7 +436,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
435 ri->rp->handler(ri, regs); 436 ri->rp->handler(ri, regs);
436 437
437 orig_ret_address = (unsigned long)ri->ret_addr; 438 orig_ret_address = (unsigned long)ri->ret_addr;
438 recycle_rp_inst(ri); 439 recycle_rp_inst(ri, &empty_rp);
439 440
440 if (orig_ret_address != trampoline_address) 441 if (orig_ret_address != trampoline_address)
441 /* 442 /*
@@ -453,6 +454,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
453 spin_unlock_irqrestore(&kretprobe_lock, flags); 454 spin_unlock_irqrestore(&kretprobe_lock, flags);
454 preempt_enable_no_resched(); 455 preempt_enable_no_resched();
455 456
457 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
458 hlist_del(&ri->hlist);
459 kfree(ri);
460 }
456 /* 461 /*
457 * By returning a non-zero value, we are telling 462 * By returning a non-zero value, we are telling
458 * kprobe_handler() that we don't want the post_handler 463 * kprobe_handler() that we don't want the post_handler
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index a5c5a0cb0d5c..ac4c0559f751 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -202,7 +202,7 @@ void unregister_kretprobe(struct kretprobe *rp);
202struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp); 202struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp);
203void add_rp_inst(struct kretprobe_instance *ri); 203void add_rp_inst(struct kretprobe_instance *ri);
204void kprobe_flush_task(struct task_struct *tk); 204void kprobe_flush_task(struct task_struct *tk);
205void recycle_rp_inst(struct kretprobe_instance *ri); 205void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);
206#else /* CONFIG_KPROBES */ 206#else /* CONFIG_KPROBES */
207 207
208#define __kprobes /**/ 208#define __kprobes /**/
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 41dfda50e22a..610c837ad9e0 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -319,7 +319,8 @@ void __kprobes add_rp_inst(struct kretprobe_instance *ri)
319} 319}
320 320
321/* Called with kretprobe_lock held */ 321/* Called with kretprobe_lock held */
322void __kprobes recycle_rp_inst(struct kretprobe_instance *ri) 322void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
323 struct hlist_head *head)
323{ 324{
324 /* remove rp inst off the rprobe_inst_table */ 325 /* remove rp inst off the rprobe_inst_table */
325 hlist_del(&ri->hlist); 326 hlist_del(&ri->hlist);
@@ -331,7 +332,7 @@ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri)
331 hlist_add_head(&ri->uflist, &ri->rp->free_instances); 332 hlist_add_head(&ri->uflist, &ri->rp->free_instances);
332 } else 333 } else
333 /* Unregistering */ 334 /* Unregistering */
334 kfree(ri); 335 hlist_add_head(&ri->hlist, head);
335} 336}
336 337
337struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) 338struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk)
@@ -348,17 +349,23 @@ struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk)
348void __kprobes kprobe_flush_task(struct task_struct *tk) 349void __kprobes kprobe_flush_task(struct task_struct *tk)
349{ 350{
350 struct kretprobe_instance *ri; 351 struct kretprobe_instance *ri;
351 struct hlist_head *head; 352 struct hlist_head *head, empty_rp;
352 struct hlist_node *node, *tmp; 353 struct hlist_node *node, *tmp;
353 unsigned long flags = 0; 354 unsigned long flags = 0;
354 355
356 INIT_HLIST_HEAD(&empty_rp);
355 spin_lock_irqsave(&kretprobe_lock, flags); 357 spin_lock_irqsave(&kretprobe_lock, flags);
356 head = kretprobe_inst_table_head(tk); 358 head = kretprobe_inst_table_head(tk);
357 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { 359 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
358 if (ri->task == tk) 360 if (ri->task == tk)
359 recycle_rp_inst(ri); 361 recycle_rp_inst(ri, &empty_rp);
360 } 362 }
361 spin_unlock_irqrestore(&kretprobe_lock, flags); 363 spin_unlock_irqrestore(&kretprobe_lock, flags);
364
365 hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
366 hlist_del(&ri->hlist);
367 kfree(ri);
368 }
362} 369}
363 370
364static inline void free_rp_inst(struct kretprobe *rp) 371static inline void free_rp_inst(struct kretprobe *rp)