]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ACPICA: Fix use-after-free in acpi_ds_terminate_control_method()
authorikaros <void0red@gmail.com>
Wed, 27 May 2026 17:59:12 +0000 (19:59 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 27 May 2026 18:18:45 +0000 (20:18 +0200)
Fix use-after-free issue in acpi_ds_terminate_control_method() by
clearing references to method locals and arguments.

Link: https://github.com/acpica/acpica/commit/36f22a94cb1b
Signed-off-by: ikaros <void0red@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/8730924.NyiUUSuA9g@rafael.j.wysocki
drivers/acpi/acpica/dsmethod.c

index 45ec32e81903ab2ee7539849d9b0751b3d3247ce..08bfe83030838797c9cda183fb8d05844247e964 100644 (file)
@@ -705,6 +705,8 @@ void
 acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
                                 struct acpi_walk_state *walk_state)
 {
+       u32 i;
+       struct acpi_namespace_node *ref_node;
 
        ACPI_FUNCTION_TRACE_PTR(ds_terminate_control_method, walk_state);
 
@@ -715,6 +717,47 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
        }
 
        if (walk_state) {
+               /*
+                * Check if the return value is a ref_of reference to a method local
+                * or argument. If so, clear the reference to avoid use-after-free
+                * when the walk state is deleted.
+                */
+               if (walk_state->return_desc &&
+                   (walk_state->return_desc->common.type ==
+                    ACPI_TYPE_LOCAL_REFERENCE)
+                   && (walk_state->return_desc->reference.class ==
+                       ACPI_REFCLASS_REFOF)) {
+                       ref_node = walk_state->return_desc->reference.object;
+                       if (ref_node) {
+
+                               /* Check against method locals */
+                               for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) {
+                                       if (ref_node ==
+                                           &walk_state->local_variables[i]) {
+                                               acpi_ut_remove_reference
+                                                   (walk_state->return_desc);
+                                               walk_state->return_desc = NULL;
+                                               break;
+                                       }
+                               }
+
+                               /* Check against method arguments if not already cleared */
+                               if (walk_state->return_desc) {
+                                       for (i = 0; i < ACPI_METHOD_NUM_ARGS;
+                                            i++) {
+                                               if (ref_node ==
+                                                   &walk_state->arguments[i]) {
+                                                       acpi_ut_remove_reference
+                                                           (walk_state->
+                                                            return_desc);
+                                                       walk_state->
+                                                           return_desc = NULL;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
 
                /* Delete all arguments and locals */