diff options
author | bibo,mao <bibo.mao@intel.com> | 2006-10-02 05:17:35 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-02 10:57:16 -0400 |
commit | 99219a3fbc2dcf2eaa954f7b2ac27299fd7894cd (patch) | |
tree | 895abde156c9fbeea9c5a87cfaaa411d4ad175c6 | |
parent | f2aa85a0ccd90110e76c6375535adc3ae358f971 (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.c | 9 | ||||
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 9 | ||||
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 9 | ||||
-rw-r--r-- | arch/s390/kernel/kprobes.c | 9 | ||||
-rw-r--r-- | arch/x86_64/kernel/kprobes.c | 9 | ||||
-rw-r--r-- | include/linux/kprobes.h | 2 | ||||
-rw-r--r-- | kernel/kprobes.c | 15 |
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: | |||
396 | fastcall void *__kprobes trampoline_handler(struct pt_regs *regs) | 396 | fastcall 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) | |||
338 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 338 | int __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) | |||
260 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 260 | int __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) | |||
369 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 369 | int __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: | |||
405 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 405 | int __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); | |||
202 | struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp); | 202 | struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp); |
203 | void add_rp_inst(struct kretprobe_instance *ri); | 203 | void add_rp_inst(struct kretprobe_instance *ri); |
204 | void kprobe_flush_task(struct task_struct *tk); | 204 | void kprobe_flush_task(struct task_struct *tk); |
205 | void recycle_rp_inst(struct kretprobe_instance *ri); | 205 | void 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 */ |
322 | void __kprobes recycle_rp_inst(struct kretprobe_instance *ri) | 322 | void __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 | ||
337 | struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) | 338 | struct 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) | |||
348 | void __kprobes kprobe_flush_task(struct task_struct *tk) | 349 | void __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 | ||
364 | static inline void free_rp_inst(struct kretprobe *rp) | 371 | static inline void free_rp_inst(struct kretprobe *rp) |