php / php-src

The PHP Interpreter
https://www.php.net
Other
38.16k stars 7.75k forks source link

UAF in asort #16648

Open chibinz opened 1 day ago

chibinz commented 1 day ago

Description

The following code:

<?php
function resize_arr() {
    global $arr;
    for ($i = 0; $i < 10; $i++) {
        $arr[$i] = $i;
    }
}

class C {
    function __tostring() {
        resize_arr();
        return "3";
    }
}

$arr = ["a" => "1", "3" => new C, "2" => "2"];
asort($arr);

Resulted in this output:

==1989687==ERROR: AddressSanitizer: heap-use-after-free on address 0x61200002d988 at pc 0x558c1bf788d9 bp 0x7fff5f764960 sp 0x7fff5f764958
READ of size 1 at 0x61200002d988 thread T0
    #0 0x558c1bf788d8 in zval_get_type /tmp/php-asan/Zend/zend_types.h:650:18
    #1 0x558c1bf90579 in zend_compare /tmp/php-asan/Zend/zend_operators.c:2259:11
    #2 0x558c1bf5af12 in zend_std_compare_objects /tmp/php-asan/Zend/zend_object_handlers.c:2082:57
    #3 0x558c1bf91378 in zend_compare /tmp/php-asan/Zend/zend_operators.c:2342:13
    #4 0x558c1b506e1f in php_array_data_compare_unstable_i /tmp/php-asan/ext/standard/array.c:288:15
    #5 0x558c1b506a4c in php_array_data_compare /tmp/php-asan/ext/standard/array.c:367:1
    #6 0x558c1bfae0e9 in zend_sort_3 /tmp/php-asan/Zend/zend_sort.c:32:8
    #7 0x558c1bfadd1c in zend_insert_sort /tmp/php-asan/Zend/zend_sort.c:93:4
    #8 0x558c1bfae39f in zend_sort /tmp/php-asan/Zend/zend_sort.c:252:4
    #9 0x558c1be6ba3a in zend_hash_sort_ex /tmp/php-asan/Zend/zend_hash.c:3022:2
    #10 0x558c1b497432 in zend_hash_sort /tmp/php-asan/Zend/zend_hash.h:305:2
    #11 0x558c1b49a6ed in zif_asort /tmp/php-asan/ext/standard/array.c:741:2
    #12 0x558c1bd4ebc2 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /tmp/php-asan/Zend/zend_vm_execute.h:1299:2
    #13 0x558c1bbb183d in execute_ex /tmp/php-asan/Zend/zend_vm_execute.h:58565:7
    #14 0x558c1bbb2067 in zend_execute /tmp/php-asan/Zend/zend_vm_execute.h:64217:2
    #15 0x558c1bfe6860 in zend_execute_script /tmp/php-asan/Zend/zend.c:1932:3
    #16 0x558c1b804d2b in php_execute_script_ex /tmp/php-asan/main/main.c:2574:13
    #17 0x558c1b805228 in php_execute_script /tmp/php-asan/main/main.c:2614:9
    #18 0x558c1bfee309 in do_cli /tmp/php-asan/sapi/cli/php_cli.c:935:5
    #19 0x558c1bfeb32c in main /tmp/php-asan/sapi/cli/php_cli.c:1310:18
    #20 0x7f0ec5429d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #21 0x7f0ec5429e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #22 0x558c1aa02de4 in _start (/workspaces/TriFuzz/targets/php-asan/bin/php+0x402de4)

0x61200002d988 is located 72 bytes inside of 320-byte region [0x61200002d940,0x61200002da80)
freed by thread T0 here:
    #0 0x558c1aa87702 in free /opt/llvm-15-build/llvm-15.x/final/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:52:3
    #1 0x558c1ba418a3 in __zend_free /tmp/php-asan/Zend/zend_alloc.c:3308:2
    #2 0x558c1ba45774 in _efree /tmp/php-asan/Zend/zend_alloc.c:2747:3
    #3 0x558c1be6fed8 in zend_hash_do_resize /tmp/php-asan/Zend/zend_hash.c:1321:3
    #4 0x558c1be5a240 in _zend_hash_index_add_or_update_i /tmp/php-asan/Zend/zend_hash.c:1168:3
    #5 0x558c1be5a8f5 in zend_hash_index_lookup /tmp/php-asan/Zend/zend_hash.c:1236:9
    #6 0x558c1bdee1c5 in zend_fetch_dimension_address_inner /tmp/php-asan/Zend/zend_execute.c:2698:4
    #7 0x558c1bdf92ce in zend_fetch_dimension_address_inner_W /tmp/php-asan/Zend/zend_execute.c:2755:9
    #8 0x558c1bc171f8 in ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_CV_HANDLER /tmp/php-asan/Zend/zend_vm_execute.h:53879:20
    #9 0x558c1bbb183d in execute_ex /tmp/php-asan/Zend/zend_vm_execute.h:58565:7
    #10 0x558c1bb8d1ac in zend_call_function /tmp/php-asan/Zend/zend_execute_API.c:996:3
    #11 0x558c1bb8f3b2 in zend_call_known_function /tmp/php-asan/Zend/zend_execute_API.c:1090:23
    #12 0x558c1bf5e7fa in zend_call_known_instance_method /tmp/php-asan/Zend/zend_API.h:860:2
    #13 0x558c1bf45c0b in zend_call_known_instance_method_with_0_params /tmp/php-asan/Zend/zend_API.h:866:2
    #14 0x558c1bf5d292 in zend_std_cast_object_tostring /tmp/php-asan/Zend/zend_object_handlers.c:2335:5
    #15 0x558c1bf5ac28 in zend_std_compare_objects /tmp/php-asan/Zend/zend_object_handlers.c:2068:7
    #16 0x558c1bf91378 in zend_compare /tmp/php-asan/Zend/zend_operators.c:2342:13
    #17 0x558c1b506e1f in php_array_data_compare_unstable_i /tmp/php-asan/ext/standard/array.c:288:15
    #18 0x558c1b506a4c in php_array_data_compare /tmp/php-asan/ext/standard/array.c:367:1
    #19 0x558c1bfae0e9 in zend_sort_3 /tmp/php-asan/Zend/zend_sort.c:32:8
    #20 0x558c1bfadd1c in zend_insert_sort /tmp/php-asan/Zend/zend_sort.c:93:4
    #21 0x558c1bfae39f in zend_sort /tmp/php-asan/Zend/zend_sort.c:252:4
    #22 0x558c1be6ba3a in zend_hash_sort_ex /tmp/php-asan/Zend/zend_hash.c:3022:2
    #23 0x558c1b497432 in zend_hash_sort /tmp/php-asan/Zend/zend_hash.h:305:2
    #24 0x558c1b49a6ed in zif_asort /tmp/php-asan/ext/standard/array.c:741:2
    #25 0x558c1bd4ebc2 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /tmp/php-asan/Zend/zend_vm_execute.h:1299:2
    #26 0x558c1bbb183d in execute_ex /tmp/php-asan/Zend/zend_vm_execute.h:58565:7
    #27 0x558c1bbb2067 in zend_execute /tmp/php-asan/Zend/zend_vm_execute.h:64217:2
    #28 0x558c1bfe6860 in zend_execute_script /tmp/php-asan/Zend/zend.c:1932:3
    #29 0x558c1b804d2b in php_execute_script_ex /tmp/php-asan/main/main.c:2574:13

previously allocated by thread T0 here:
    #0 0x558c1aa879ae in malloc /opt/llvm-15-build/llvm-15.x/final/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
    #1 0x558c1ba45ce3 in __zend_malloc /tmp/php-asan/Zend/zend_alloc.c:3280:14
    #2 0x558c1ba45670 in _emalloc /tmp/php-asan/Zend/zend_alloc.c:2737:10
    #3 0x558c1be4b5bf in zend_hash_real_init_mixed_ex /tmp/php-asan/Zend/zend_hash.c:177:10
    #4 0x558c1be4b3f7 in zend_hash_real_init_mixed /tmp/php-asan/Zend/zend_hash.c:343:2
    #5 0x558c1bcb1c48 in ZEND_INIT_ARRAY_SPEC_CONST_CONST_HANDLER /tmp/php-asan/Zend/zend_vm_execute.h:7770:4
    #6 0x558c1bbb183d in execute_ex /tmp/php-asan/Zend/zend_vm_execute.h:58565:7
    #7 0x558c1bbb2067 in zend_execute /tmp/php-asan/Zend/zend_vm_execute.h:64217:2
    #8 0x558c1bfe6860 in zend_execute_script /tmp/php-asan/Zend/zend.c:1932:3
    #9 0x558c1b804d2b in php_execute_script_ex /tmp/php-asan/main/main.c:2574:13
    #10 0x558c1b805228 in php_execute_script /tmp/php-asan/main/main.c:2614:9
    #11 0x558c1bfee309 in do_cli /tmp/php-asan/sapi/cli/php_cli.c:935:5
    #12 0x558c1bfeb32c in main /tmp/php-asan/sapi/cli/php_cli.c:1310:18
    #13 0x7f0ec5429d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-use-after-free /tmp/php-asan/Zend/zend_types.h:650:18 in zval_get_type
Shadow bytes around the buggy address:
  0x0c247fffdae0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fffdaf0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c247fffdb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c247fffdb10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c247fffdb20: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
=>0x0c247fffdb30: fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fffdb40: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fffdb50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fffdb60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fffdb70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fffdb80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1989687==ABORTING

Requires USE_ZEND_ALLOC=0

PHP Version

PHP 8.5.0-dev

Operating System

No response

cmb69 commented 1 day ago

Yeah, references …

It seems to me that we would need to "lock" the array during sort operations (and basically all others which take arrays as reference).

iluuu1994 commented 1 day ago

We could try increasing the refcount to cause separation on any modification of the array to prevent it from going away. That's essentially locking, by making sure the original array can't go away, be modified or moved. That said, implementing this I ran into some issues with the GC... GH-16654

cmb69 commented 19 hours ago

Hmm, I'm not convinced that this is the right approach. After all, users manipulating an array during sorting (etc.) are probably doing something wrong. Instead of silently accepting these modifications without any effect, it would be better to inform users about their mistake (exception?)

And some manipulations might even be okay (I mean we're talking about references); we should avoid crashes, though.

iluuu1994 commented 19 hours ago

Instead of silently accepting these modifications without any effect, it would be better to inform users about their mistake (exception?)

This might be optimal in terms of behavior, but it might not be simple to implement. E.g. where do we actually trigger the exception? Optimally, we would avoid checking if the array is locked for every modification. Instead, we may still add a refcount when the array is locked, and then only check on separation. However, that also means separation can now fail (without bailing), which would require all users of SEPARATE_ARRAY() or zend_array_dup() (depending on where we put it) to handle errors, for something that really should never happen.

cmb69 commented 18 hours ago

@iluuu1994, indeed you're right. There is no really satifying solution. Thus I wrote:

Yeah, references …