linux/drivers/acpi/utilities/utalloc.c
Ingo Molnar f88133d76e acpi: fix crash in core ACPI code, triggered by CONFIG_ACPI_PCI_SLOT=y
-tip testing found the following boot crash on 32-bit x86 (Core2Duo
laptop) yesterday:

[    5.606664] scsi4 : ata_piix
[    5.606664] scsi5 : ata_piix
[    5.606664] ACPI Error (psargs-0358): [\_SB_.PCI0.LPC_.EC__.BSTA] Namespace lookup failure, AE_NOT_FOUND
[    5.606664] ACPI Error (psparse-0530): ACPI Error (nsnames-0186): Invalid NS Node (f7c0e960) while traversing path [20080609]
[    5.606664] BUG: unable to handle kernel NULL pointer dereference at 0000000f
[    5.606664] IP: [<80339e2f>] acpi_ns_build_external_path+0x1f/0x80
[    5.609997] *pdpt = 0000000000a03001 *pde = 0000000000000000
[    5.609997] Oops: 0002 [#1] SMP
[    5.609997]
[    5.609997] Pid: 1, comm: swapper Not tainted (2.6.26-tip-03965-gbbfb62e-dirty #3153)
[    5.609997] EIP: 0060:[<80339e2f>] EFLAGS: 00010286 CPU: 0
[    5.609997] EIP is at acpi_ns_build_external_path+0x1f/0x80
[    5.609997] EAX: f7c18c18 EBX: ffffffff ECX: 00000010 EDX: 00000000
[    5.609997] ESI: f7c18c18 EDI: 00000010 EBP: f7c4dc28 ESP: f7c4dc18
[    5.609997]  DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
[    5.609997] Process swapper (pid: 1, ti=f7c4c000 task=f7c50000 task.ti=f7c4c000)
[    5.609997] Stack: 00000000 00000000 f7c18c18 f7c4dc48 f7c4dc40 80339ed0 00000000 f7c18c18
[    5.609997]        8084c1b6 8084c1b6 f7c4dc58 8033a60a 00000000 00000010 00000000 f7c18c18
[    5.609997]        f7c4dc70 8033a68f f7c18c18 00000000 f6de7600 00000005 f7c4dc98 8033c34d
[    5.609997] Call Trace:
[    5.609997]  [<80339ed0>] ? acpi_ns_handle_to_pathname+0x40/0x72
[    5.609997]  [<8033a60a>] ? acpi_ns_print_node_pathname+0x2c/0x61
[    5.609997]  [<8033a68f>] ? acpi_ns_report_method_error+0x50/0x6d
[    5.609997]  [<8033c34d>] ? acpi_ps_parse_aml+0x149/0x2f9
[    5.609997]  [<8033d6dd>] ? acpi_ps_execute_method+0x132/0x201
[    5.609997]  [<80339d19>] ? acpi_ns_evaluate+0x1ad/0x258
[    5.609997]  [<803406c4>] ? acpi_ut_evaluate_object+0x55/0x18f
[    5.609997]  [<803408b7>] ? acpi_ut_execute_STA+0x22/0x7a
[    5.609997]  [<8033a907>] ? acpi_get_object_info+0x131/0x1be
[    5.609997]  [<80344bb2>] ? do_acpi_find_child+0x22/0x4b
[    5.609997]  [<8033b855>] ? acpi_ns_walk_namespace+0xa5/0x124
[    5.609997]  [<803394f3>] ? acpi_walk_namespace+0x54/0x74
[    5.609997]  [<80344b90>] ? do_acpi_find_child+0x0/0x4b
[    5.609997]  [<80344b85>] ? acpi_get_child+0x38/0x43
[    5.609997]  [<80344b90>] ? do_acpi_find_child+0x0/0x4b
[    5.609997]  [<804d0148>] ? ata_acpi_associate+0xb5/0x1b5
[    5.609997]  [<804c6ecb>] ? ata_scsi_add_hosts+0x8e/0xdc
[    5.609997]  [<804c40c8>] ? ata_host_register+0x9f/0x1d6
[    5.609997]  [<804cbc7f>] ? ata_pci_sff_activate_host+0x179/0x19f
[    5.609997]  [<804cdd45>] ? ata_sff_interrupt+0x0/0x1c7
[    5.609997]  [<8069b033>] ? piix_init_one+0x569/0x5b0
[    5.609997]  [<801bd400>] ? sysfs_ilookup_test+0x0/0x11
[    5.609997]  [<801987d7>] ? ilookup5_nowait+0x29/0x30
[    5.609997]  [<802efc7e>] ? pci_match_device+0x99/0xa3
[    5.609997]  [<802efd3c>] ? pci_device_probe+0x39/0x59
[    5.609997]  [<803bc4af>] ? driver_probe_device+0xa0/0x11b
[    5.609997]  [<803bc564>] ? __driver_attach+0x3a/0x59
[    5.609997]  [<803bbde3>] ? bus_for_each_dev+0x36/0x58
[    5.609997]  [<803bc354>] ? driver_attach+0x14/0x16
[    5.609997]  [<803bc52a>] ? __driver_attach+0x0/0x59
[    5.609997]  [<803bc161>] ? bus_add_driver+0x93/0x196
[    5.609997]  [<803bc773>] ? driver_register+0x71/0xcd
[    5.609997]  [<802eff05>] ? __pci_register_driver+0x3f/0x6e
[    5.609997]  [<809af7ff>] ? piix_init+0x14/0x24
[    5.609997]  [<80984568>] ? kernel_init+0x128/0x269
[    5.609997]  [<809af7eb>] ? piix_init+0x0/0x24
[    5.609997]  [<802e2758>] ? trace_hardirqs_on_thunk+0xc/0x10
[    5.609997]  [<80116aef>] ? restore_nocheck_notrace+0x0/0xe
[    5.609997]  [<80984440>] ? kernel_init+0x0/0x269
[    5.609997]  [<80984440>] ? kernel_init+0x0/0x269
[    5.609997]  [<80117d87>] ? kernel_thread_helper+0x7/0x10
[    5.609997]  =======================
[    5.609997] Code: 75 02 b3 01 8d 43 01 8b 5d fc c9 c3 55 89 e5 57 89 cf 56 53 89 d3 4b 83 ec 04 83 fb 03 89 55 f0 77 09 c6 01 5c c6 41 01 00 eb 59 <c6> 04 19 00 8b 55 f0 8d 34 11 89 c2 eb 19 8b 42 08 83 eb 05 89
[    5.609997] EIP: [<80339e2f>] acpi_ns_build_external_path+0x1f/0x80 SS:ESP 0068:f7c4dc18
[    5.613331] Kernel panic - not syncing: Fatal exception
[    5.613331] Rebooting in 1 seconds..[    4.646664] ata1: SATA link up 1.5 Gbps (SStatus 113 SControl 300)

I have bisected it down to:

 # bad:  [5b664cbe] Merge branch 'upstream-linus' of git://git.kernel.
 # good: [bce7f795] Linux 2.6.26
 # good: [e18425ab] Merge branch 'tracing/for-linus' of git://git.kern
 # good: [cadc7236] Merge branch 'bkl-removal' into next
 # good: [4515889a] Merge branch 'merge' of git://git.kernel.org/pub/s
 # good: [42fdd14e] Merge git://git.kernel.org/pub/scm/linux/kernel/gi
 # good: [8a0ca91f] Merge branch 'for-linus' of git://git.kernel.org/p
 # bad:  [0af4b8cb] ACPI: Introduce new device wakeup flag 'prepared'
 # good: [fe997407] PCI: construct one fakephp slot per PCI slot
 # bad:  [531f254a] PCIE: aer: use dev_printk when possible
 # bad:  [15650a20] x86/PCI: fixup early quirk probing
 # good: [0e6859d9] ACPI PM: Remove obsolete Toshiba workaround
 # bad:  [8344b566] PCI: ACPI PCI slot detection driver
 # good: [f46753c9] PCI: introduce pci_slot

 | 8344b568f5 is first bad commit
 | commit 8344b568f5
 | Author: Alex Chiang <achiang@hp.com>
 | Date:   Tue Jun 10 15:30:42 2008 -0600
 |
 |     PCI: ACPI PCI slot detection driver
 |
 |     Detect all physical PCI slots as described by ACPI, and create entries in
 |     /sys/bus/pci/slots/.

I.e. the new CONFIG_ACPI_PCI_SLOT=y option was causing this crash.

But the bug is not mainly in this new PCI code - that code was just
hitting the ACPI code in a new way which made ACPI break.

The crash signature shows that we are crashing on this instruction:

   movb $0x0, (%ecx, %ebx, 1)

ECX and EBX are 0x10 and -1. It's this line in
drivers/acpi/namespace/nsnames.c's acpi_ns_build_external_path():

        name_buffer[index] = 0;

I.e. name_buffer is 0x10 and index is -1.

index -1 corresponds to size 0, and name_buffer 0x10 is slab's
ZERO_SIZE_PTR special-case for zero-sized allocations.

I.e. when we called acpi_ns_handle_to_pathname(), we got required_size
of 0 due to an error condition, but this is passed to the ACPI allocator
unconditionally:

        required_size = acpi_ns_get_pathname_length(node);

        /* Validate/Allocate/Clear caller buffer */

        status = acpi_ut_initialize_buffer(buffer, required_size);
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }

Where acpi_ut_initialize_buffer(), through many (unnecessary) layers,
ends up calling kzalloc(0). Which returns 0x10 and that then causes the
crash later on.

So fix both callers of acpi_ns_get_pathname_length(), which can return 0
in case of an invalid node.

Also add a WARN_ON() against zero sized allocations in
acpi_ut_initialize_buffer() to make it easier to find similar instances
of this bug.

I have tested this patch for the past 24 hours and the crash has not
reappeared.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
2008-07-22 00:27:48 +02:00

377 lines
9.8 KiB
C

/******************************************************************************
*
* Module Name: utalloc - local memory allocation routines
*
*****************************************************************************/
/*
* Copyright (C) 2000 - 2008, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*/
#include <acpi/acpi.h>
#include <acpi/acdebug.h>
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utalloc")
/*******************************************************************************
*
* FUNCTION: acpi_ut_create_caches
*
* PARAMETERS: None
*
* RETURN: Status
*
* DESCRIPTION: Create all local caches
*
******************************************************************************/
acpi_status acpi_ut_create_caches(void)
{
acpi_status status;
/* Object Caches, for frequently used objects */
status =
acpi_os_create_cache("Acpi-Namespace",
sizeof(struct acpi_namespace_node),
ACPI_MAX_NAMESPACE_CACHE_DEPTH,
&acpi_gbl_namespace_cache);
if (ACPI_FAILURE(status)) {
return (status);
}
status =
acpi_os_create_cache("Acpi-State", sizeof(union acpi_generic_state),
ACPI_MAX_STATE_CACHE_DEPTH,
&acpi_gbl_state_cache);
if (ACPI_FAILURE(status)) {
return (status);
}
status =
acpi_os_create_cache("Acpi-Parse",
sizeof(struct acpi_parse_obj_common),
ACPI_MAX_PARSE_CACHE_DEPTH,
&acpi_gbl_ps_node_cache);
if (ACPI_FAILURE(status)) {
return (status);
}
status =
acpi_os_create_cache("Acpi-ParseExt",
sizeof(struct acpi_parse_obj_named),
ACPI_MAX_EXTPARSE_CACHE_DEPTH,
&acpi_gbl_ps_node_ext_cache);
if (ACPI_FAILURE(status)) {
return (status);
}
status =
acpi_os_create_cache("Acpi-Operand",
sizeof(union acpi_operand_object),
ACPI_MAX_OBJECT_CACHE_DEPTH,
&acpi_gbl_operand_cache);
if (ACPI_FAILURE(status)) {
return (status);
}
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
/* Memory allocation lists */
status = acpi_ut_create_list("Acpi-Global", 0, &acpi_gbl_global_list);
if (ACPI_FAILURE(status)) {
return (status);
}
status =
acpi_ut_create_list("Acpi-Namespace",
sizeof(struct acpi_namespace_node),
&acpi_gbl_ns_node_list);
if (ACPI_FAILURE(status)) {
return (status);
}
#endif
return (AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_delete_caches
*
* PARAMETERS: None
*
* RETURN: Status
*
* DESCRIPTION: Purge and delete all local caches
*
******************************************************************************/
acpi_status acpi_ut_delete_caches(void)
{
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
char buffer[7];
if (acpi_gbl_display_final_mem_stats) {
ACPI_STRCPY(buffer, "MEMORY");
(void)acpi_db_display_statistics(buffer);
}
#endif
(void)acpi_os_delete_cache(acpi_gbl_namespace_cache);
acpi_gbl_namespace_cache = NULL;
(void)acpi_os_delete_cache(acpi_gbl_state_cache);
acpi_gbl_state_cache = NULL;
(void)acpi_os_delete_cache(acpi_gbl_operand_cache);
acpi_gbl_operand_cache = NULL;
(void)acpi_os_delete_cache(acpi_gbl_ps_node_cache);
acpi_gbl_ps_node_cache = NULL;
(void)acpi_os_delete_cache(acpi_gbl_ps_node_ext_cache);
acpi_gbl_ps_node_ext_cache = NULL;
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
/* Debug only - display leftover memory allocation, if any */
acpi_ut_dump_allocations(ACPI_UINT32_MAX, NULL);
/* Free memory lists */
ACPI_FREE(acpi_gbl_global_list);
acpi_gbl_global_list = NULL;
ACPI_FREE(acpi_gbl_ns_node_list);
acpi_gbl_ns_node_list = NULL;
#endif
return (AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_validate_buffer
*
* PARAMETERS: Buffer - Buffer descriptor to be validated
*
* RETURN: Status
*
* DESCRIPTION: Perform parameter validation checks on an struct acpi_buffer
*
******************************************************************************/
acpi_status acpi_ut_validate_buffer(struct acpi_buffer * buffer)
{
/* Obviously, the structure pointer must be valid */
if (!buffer) {
return (AE_BAD_PARAMETER);
}
/* Special semantics for the length */
if ((buffer->length == ACPI_NO_BUFFER) ||
(buffer->length == ACPI_ALLOCATE_BUFFER) ||
(buffer->length == ACPI_ALLOCATE_LOCAL_BUFFER)) {
return (AE_OK);
}
/* Length is valid, the buffer pointer must be also */
if (!buffer->pointer) {
return (AE_BAD_PARAMETER);
}
return (AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_initialize_buffer
*
* PARAMETERS: Buffer - Buffer to be validated
* required_length - Length needed
*
* RETURN: Status
*
* DESCRIPTION: Validate that the buffer is of the required length or
* allocate a new buffer. Returned buffer is always zeroed.
*
******************************************************************************/
acpi_status
acpi_ut_initialize_buffer(struct acpi_buffer * buffer,
acpi_size required_length)
{
acpi_status status = AE_OK;
if (!required_length) {
WARN_ON(1);
return AE_ERROR;
}
switch (buffer->length) {
case ACPI_NO_BUFFER:
/* Set the exception and returned the required length */
status = AE_BUFFER_OVERFLOW;
break;
case ACPI_ALLOCATE_BUFFER:
/* Allocate a new buffer */
buffer->pointer = acpi_os_allocate(required_length);
if (!buffer->pointer) {
return (AE_NO_MEMORY);
}
/* Clear the buffer */
ACPI_MEMSET(buffer->pointer, 0, required_length);
break;
case ACPI_ALLOCATE_LOCAL_BUFFER:
/* Allocate a new buffer with local interface to allow tracking */
buffer->pointer = ACPI_ALLOCATE_ZEROED(required_length);
if (!buffer->pointer) {
return (AE_NO_MEMORY);
}
break;
default:
/* Existing buffer: Validate the size of the buffer */
if (buffer->length < required_length) {
status = AE_BUFFER_OVERFLOW;
break;
}
/* Clear the buffer */
ACPI_MEMSET(buffer->pointer, 0, required_length);
break;
}
buffer->length = required_length;
return (status);
}
#ifdef NOT_USED_BY_LINUX
/*******************************************************************************
*
* FUNCTION: acpi_ut_allocate
*
* PARAMETERS: Size - Size of the allocation
* Component - Component type of caller
* Module - Source file name of caller
* Line - Line number of caller
*
* RETURN: Address of the allocated memory on success, NULL on failure.
*
* DESCRIPTION: Subsystem equivalent of malloc.
*
******************************************************************************/
void *acpi_ut_allocate(acpi_size size,
u32 component, const char *module, u32 line)
{
void *allocation;
ACPI_FUNCTION_TRACE_U32(ut_allocate, size);
/* Check for an inadvertent size of zero bytes */
if (!size) {
ACPI_WARNING((module, line,
"Attempt to allocate zero bytes, allocating 1 byte"));
size = 1;
}
allocation = acpi_os_allocate(size);
if (!allocation) {
/* Report allocation error */
ACPI_WARNING((module, line,
"Could not allocate size %X", (u32) size));
return_PTR(NULL);
}
return_PTR(allocation);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_allocate_zeroed
*
* PARAMETERS: Size - Size of the allocation
* Component - Component type of caller
* Module - Source file name of caller
* Line - Line number of caller
*
* RETURN: Address of the allocated memory on success, NULL on failure.
*
* DESCRIPTION: Subsystem equivalent of calloc. Allocate and zero memory.
*
******************************************************************************/
void *acpi_ut_allocate_zeroed(acpi_size size,
u32 component, const char *module, u32 line)
{
void *allocation;
ACPI_FUNCTION_ENTRY();
allocation = acpi_ut_allocate(size, component, module, line);
if (allocation) {
/* Clear the memory block */
ACPI_MEMSET(allocation, 0, size);
}
return (allocation);
}
#endif