Bug 670373
Summary: | panic in kfree() due to race condition in acpi_bus_receive_event() | ||||||
---|---|---|---|---|---|---|---|
Product: | Red Hat Enterprise Linux 5 | Reporter: | Lachlan McIlroy <lmcilroy> | ||||
Component: | kernel | Assignee: | John Feeney <jfeeney> | ||||
Status: | CLOSED ERRATA | QA Contact: | Zhang Kexin <kzhang> | ||||
Severity: | high | Docs Contact: | |||||
Priority: | high | ||||||
Version: | 5.5 | CC: | anton, chtan, cww, dhoward, kzhang, peterm, qcai, vgaikwad, yusufhadiwinata | ||||
Target Milestone: | rc | Keywords: | ZStream | ||||
Target Release: | --- | ||||||
Hardware: | All | ||||||
OS: | Linux | ||||||
Whiteboard: | |||||||
Fixed In Version: | Doc Type: | Bug Fix | |||||
Doc Text: |
Prior to this update, kernel panic occurred in the kfree() due to a race condition in the acpi_bus_receive_event() function. The acpi_bus_receive_event() function left the acpi_bus_event_list list attribute unlocked between checking it whether it was empty and calling the kfree() function on it. With this update, a check was added after the lock has been lifted in order to prevent the race and the calling of the kfree() function on an empty list.
|
Story Points: | --- | ||||
Clone Of: | Environment: | ||||||
Last Closed: | 2011-07-21 09:39:09 UTC | Type: | --- | ||||
Regression: | --- | Mount Type: | --- | ||||
Documentation: | --- | CRM: | |||||
Verified Versions: | Category: | --- | |||||
oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |||||
Cloudforms Team: | --- | Target Upstream Version: | |||||
Embargoed: | |||||||
Bug Depends On: | |||||||
Bug Blocks: | 670797 | ||||||
Attachments: |
|
Description
Lachlan McIlroy
2011-01-18 03:59:36 UTC
vmcore is available from megatron.rdu.redhat.com:/cores/20110114003429/work crash> dis -r ffffffff8000b4e4 0xffffffff8000b486 <kfree>: push %r14 0xffffffff8000b488 <kfree+2>: push %r13 0xffffffff8000b48a <kfree+4>: mov %rdi,%r13 0xffffffff8000b48d <kfree+7>: push %r12 0xffffffff8000b48f <kfree+9>: push %rbp 0xffffffff8000b490 <kfree+10>: push %rbx 0xffffffff8000b491 <kfree+11>: sub $0x10,%rsp 0xffffffff8000b495 <kfree+15>: test %rdi,%rdi 0xffffffff8000b498 <kfree+18>: je 0xffffffff8000b652 <kfree+460> 0xffffffff8000b49e <kfree+24>: pushfq 0xffffffff8000b49f <kfree+25>: pop %r14 0xffffffff8000b4a1 <kfree+27>: cli 0xffffffff8000b4a2 <kfree+28>: mov $0x7f0000000000,%rax 0xffffffff8000b4ac <kfree+38>: lea (%rdi,%rax,1),%rax 0xffffffff8000b4b0 <kfree+42>: mov %rax,%rdi 0xffffffff8000b4b3 <kfree+45>: mov %rax,%rsi 0xffffffff8000b4b6 <kfree+48>: mov %rax,%rcx 0xffffffff8000b4b9 <kfree+51>: shr $0x24,%rdi 0xffffffff8000b4bd <kfree+55>: shr $0xc,%rsi 0xffffffff8000b4c1 <kfree+59>: shr $0x1b,%rcx 0xffffffff8000b4c5 <kfree+63>: mov -0x7fb4dc80(,%rdi,8),%rdx 0xffffffff8000b4cd <kfree+71>: xor %eax,%eax 0xffffffff8000b4cf <kfree+73>: test %rdx,%rdx 0xffffffff8000b4d2 <kfree+76>: je 0xffffffff8000b4e0 <kfree+90> 0xffffffff8000b4d4 <kfree+78>: mov %rcx,%rax 0xffffffff8000b4d7 <kfree+81>: and $0x1ff,%eax 0xffffffff8000b4dc <kfree+86>: lea (%rdx,%rax,8),%rax 0xffffffff8000b4e0 <kfree+90>: imul $0x38,%rsi,%rsi 0xffffffff8000b4e4 <kfree+94>: mov (%rax),%rax argument to kfree() is saved in %r13 crash> kmem ffffffff8032ee00 ffffffff8032ee00 (D) acpi_bus_event_list we are trying to free the head of the acpi_bus_event_list list which is not a dynamically allocated object. This looks like a race condition checking/accessing the acpi_bus_event_list. int acpi_bus_receive_event(struct acpi_bus_event *event) { unsigned long flags = 0; struct acpi_bus_event *entry = NULL; DECLARE_WAITQUEUE(wait, current); if (!event) return -EINVAL; if (list_empty(&acpi_bus_event_list)) { // if the list is empty put ourselves to sleep set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&acpi_bus_event_queue, &wait); if (list_empty(&acpi_bus_event_list)) // check one more time before scheduling out schedule(); remove_wait_queue(&acpi_bus_event_queue, &wait); set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; } // the above list empty checks are done without acquiring the list lock // so by the time we get here the list may have been emptied by another // thread and if so ... spin_lock_irqsave(&acpi_bus_event_lock, flags); entry = list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node); // acpi_bus_event_list.next will point to itself (the head) if (entry) list_del(&entry->node); // removed the head from the list spin_unlock_irqrestore(&acpi_bus_event_lock, flags); if (!entry) return -ENODEV; memcpy(event, entry, sizeof(struct acpi_bus_event)); kfree(entry); // and we try to free the list head return 0; } So we probably just need another list empty check that's done inside the list lock and either return early or put ourselves back to sleep. Upstream commit that should fix this problem: commit f0a37e008750ead1751b7d5e89d220a260a46147 Author: Chuck Ebbert <cebbert> Date: Tue Apr 15 14:34:47 2008 -0700 acpi: bus: check once more for an empty list after locking it List could have become empty after the unlocked check that was made earlier, so check again inside the lock. Should fix https://bugzilla.redhat.com/show_bug.cgi?id=427765 Signed-off-by: Chuck Ebbert <cebbert> Cc: <stable> Cc: Len Brown <lenb> Signed-off-by: Andrew Morton <akpm> Signed-off-by: Linus Torvalds <torvalds> diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 5b6760e..2d1955c 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -373,10 +373,11 @@ int acpi_bus_receive_event(struct acpi_bus_event *event) } spin_lock_irqsave(&acpi_bus_event_lock, flags); - entry = - list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node); - if (entry) + if (!list_empty(&acpi_bus_event_list)) { + entry = list_entry(acpi_bus_event_list.next, + struct acpi_bus_event, node); list_del(&entry->node); + } spin_unlock_irqrestore(&acpi_bus_event_lock, flags); if (!entry) Created attachment 473965 [details]
Patch to check for empty list while guarded by acpi bus event list lock
in kernel-2.6.18-241.el5 You can download this test kernel (or newer) from http://people.redhat.com/jwilson/el5 Detailed testing feedback is always welcomed. Technical note added. If any revisions are required, please edit the "Technical Notes" field accordingly. All revisions will be proofread by the Engineering Content Services team. New Contents: Prior to this update, kernel panic occurred in the kfree() due to a race condition in the acpi_bus_receive_event() function. The acpi_bus_receive_event() function left the acpi_bus_event_list list attribute unlocked between checking it whether it was empty and calling the kfree() function on it. With this update, a check was added after the lock has been lifted in order to prevent the race and the calling of the kfree() function on an empty list. An advisory has been issued which should help the problem described in this bug report. This report is therefore being closed with a resolution of ERRATA. For more information on therefore solution and/or where to find the updated files, please follow the link below. You may reopen this bug report if the solution does not work for you. http://rhn.redhat.com/errata/RHSA-2011-1065.html |