[PATCH] ACPICA: Do not abandon reference count update on allocation failure

From: Jeff Bindel

Date: Tue Oct 27 2026 - 15:36:01 EST


Hello,

This patch fixes the syzbot KASAN use-after-free in
acpi_ns_get_attached_object (extid 5725792ce6a9938d1b24). syzbot has
tested it and the reproducer did not trigger any issue.

The same change is being opened as an ACPICA GitHub PR as well.

Please find the patch attached.

Thanks,
Jeff
From 32ba7deef70e17870094b4bff4fc9e96c2d7e36a Mon Sep 17 00:00:00 2001
From: Jeff Bindel <jeff@xxxxxxxxxxxxxxxxxx>
Date: Tue, 27 Oct 2026 10:46:41 -0500
Subject: [PATCH] ACPICA: Do not abandon reference count update on allocation
failure

acpi_ut_update_object_reference() walks a complex object and its
sub-objects with an explicit stack of update states. Each complex
sub-object (a nested package, a local reference, a bank/index field
region or data object, ...) is pushed with
acpi_ut_create_update_state_and_push(), which allocates. If that
allocation fails, the function jumps to error_exit and returns.

The object's own count is updated only after its sub-objects have been
handled. A failed push therefore leaves the tree half-updated: the
sub-objects visited so far have been changed, while the object itself
and every sub-object not yet visited have not. All three callers
ignore the return value (acpi_ut_add_reference() and
acpi_ut_remove_reference() cast it to void, and so does
acpi_ps_execute_method()). They have no way to tell how much of the
walk was done and no way to undo it.

syzbot hits this through _PRT, which on QEMU q35 returns a named
package (PRTA/PRTP) with one sub-package per routing entry:

1. Return (PRTA) calls acpi_ex_resolve_node_to_value(), which takes a
reference on the namespace-owned package with
acpi_ut_add_reference().
2. Fault injection fails the state allocation for one of the
sub-packages. The walk is abandoned, and PRTA never gets its
increment.
3. acpi_rs_get_prt_method_data() is done with the returned object and
calls acpi_ut_remove_reference(). That decrement walk completes,
drops PRTA from 1 to 0 and frees it while it is still attached to
the \_SB.PCI0.PRTA namespace node.
4. The next _PRT evaluation reads node->object. Reproduced locally on
mainline with KASAN and fail-nth under QEMU q35 (the stacks match
the syzbot report):

BUG: KASAN: slab-use-after-free in acpi_ns_get_attached_object+0x105/0x160
Read of size 1 at addr ff11000001e7aa30 by task prtfault/82
acpi_ns_get_attached_object
acpi_ex_resolve_node_to_value
acpi_ex_resolve_to_value
acpi_ds_evaluate_name_path
acpi_ds_exec_end_op
acpi_ps_parse_loop
acpi_ps_parse_aml
acpi_ps_execute_method
acpi_ns_evaluate
acpi_ut_evaluate_object
acpi_rs_get_prt_method_data
acpi_get_irq_routing_table
acpi_pci_irq_find_prt_entry
Allocated by task 1:
acpi_ds_create_node
acpi_ds_exec_end_op
...
acpi_ns_load_table
acpi_load_tables
acpi_init
Freed by task 82:
acpi_ut_delete_object_desc
acpi_ut_update_ref_count
acpi_ut_update_object_reference
acpi_rs_get_prt_method_data
acpi_get_irq_routing_table
acpi_pci_irq_find_prt_entry
acpi_pci_irq_enable
do_pci_enable_device
The buggy address belongs to the object at ff11000001e7aa28
which belongs to the cache Acpi-Operand of size 72

The same failure during a decrement walk is not safe either: it leaves
the object and its remaining sub-objects over-referenced, so they are
never freed. Making callers handle an error would not help, because a
partial walk cannot be rolled back reliably under memory pressure.

Make the walk always complete instead. When a sub-object cannot be
deferred to the state stack, update it immediately by calling
acpi_ut_update_object_reference() on it. Recursion happens only when an
allocation has just failed, and each extra level needs another failed
allocation for a sub-object nested one level deeper, so the depth is
bounded by the nesting depth of the object. Real firmware nests
packages only a few levels deep. (An object that refers back to itself
already makes this walk loop forever, with or without this change.)
With error_exit gone there is nothing left to drain from the state
stack on failure, so remove it.

Tao Yu analysed this report first. His syzbot patch tests showed that
taking an extra reference on reused package elements in dspkginit.c
does not fix it, which pointed away from the elements and to the named
package itself as the freed object.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+5725792ce6a9938d1b24@xxxxxxxxxxxxxxxxxxxxxxxxx
Tested-by: syzbot+5725792ce6a9938d1b24@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=5725792ce6a9938d1b24
Cc: Tao Yu <tao1.yu@xxxxxxxxx>
Signed-off-by: Jeff Bindel <jeff@xxxxxxxxxxxxxxxxxx>
---
drivers/acpi/acpica/utdelete.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index e818009..28a13f0 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -558,12 +558,18 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
/*
* For complex sub-objects, push them onto the stack
* for later processing (this eliminates recursion.)
+ * If no state can be allocated, update the sub-object
+ * right away. Abandoning the walk here would leave
+ * this object and the remaining sub-objects with
+ * unbalanced reference counts.
*/
status =
acpi_ut_create_update_state_and_push
(next_object, action, &state_list);
if (ACPI_FAILURE(status)) {
- goto error_exit;
+ (void)
+ acpi_ut_update_object_reference
+ (next_object, action);
}
break;
}
@@ -587,7 +593,10 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
action,
&state_list);
if (ACPI_FAILURE(status)) {
- goto error_exit;
+ (void)acpi_ut_update_object_reference(object->
+ bank_field.
+ region_obj,
+ action);
}
break;

@@ -601,7 +610,10 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
action,
&state_list);
if (ACPI_FAILURE(status)) {
- goto error_exit;
+ (void)acpi_ut_update_object_reference(object->
+ index_field.
+ data_obj,
+ action);
}
break;

@@ -645,20 +657,6 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
}

return (AE_OK);
-
-error_exit:
-
- ACPI_EXCEPTION((AE_INFO, status,
- "Could not update object reference count"));
-
- /* Free any stacked Update State objects */
-
- while (state_list) {
- state = acpi_ut_pop_generic_state(&state_list);
- acpi_ut_delete_generic_state(state);
- }
-
- return (status);
}

/*******************************************************************************
--
2.47.3