php / php/php-src

Heap use-after-free in zend_object_is_lazy

Open
#21,776 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Status: Needs Triage
Dominant language
C
Stars
40.4k
Forks
8.1k
Avg merge
2d 13h
Merged PRs (30d)
96

Description

Description

The following code:

<?php
class C {
    function __isset($x) {
        $GLOBALS['o'] = 0;
        return true;
    }
}
$o = new C;
$o->a ?? 0;

Resulted in this output:

==14==ERROR: AddressSanitizer: heap-use-after-free on address 0x7bf52c2f022c at pc 0x556c44a42d3f bp 0x7ffc0bb4a590 sp 0x7ffc0bb4a588
READ of size 4 at 0x7bf52c2f022c thread T0
SCARINESS: 45 (4-byte-read-heap-use-after-free)
    #0 0x556c44a42d3e in zend_object_is_lazy /src/php-src/Zend/zend_lazy_objects.h:82:10
    #1 0x556c44a42d3e in zend_lazy_object_must_init /src/php-src/Zend/zend_lazy_objects.h:99:9
    #2 0x556c44a42d3e in zend_std_read_property /src/php-src/Zend/zend_object_handlers.c:984:6
    #3 0x556c446a3836 in ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HANDLER /src/php-src/Zend/zend_vm_execute.h:42445:12
    #4 0x556c44ace5fb in fuzzer_execute_ex /src/php-src/sapi/fuzzer/fuzzer-execute-common.h:65:12
    #5 0x556c445ca23d in zend_execute /src/php-src/Zend/zend_vm_execute.h:115542:2
    #6 0x556c44acf65f in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:293:5
    #7 0x556c44ace3e7 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-execute.c:25:2
    #8 0x556c436269dd in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
    #9 0x556c43611752 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
    #10 0x556c43617620 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9
    #11 0x556c43643152 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #12 0x7f952ce2c082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
    #13 0x556c4360a83d in _start (/out/php-fuzz-execute+0xa0a83d)

DEDUP_TOKEN: zend_object_is_lazy--zend_lazy_object_must_init--zend_std_read_property
0x7bf52c2f022c is located 12 bytes inside of 56-byte region [0x7bf52c2f0220,0x7bf52c2f0258)
freed by thread T0 here:
    #0 0x556c43745096 in free /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:51:3
    #1 0x556c44a55fd4 in zend_objects_store_del /src/php-src/Zend/zend_objects_API.c:197:3
    #2 0x556c44a41c0c in zend_std_read_property /src/php-src/Zend/zend_object_handlers.c:940:4
    #3 0x556c446a3836 in ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HANDLER /src/php-src/Zend/zend_vm_execute.h:42445:12
    #4 0x556c44ace5fb in fuzzer_execute_ex /src/php-src/sapi/fuzzer/fuzzer-execute-common.h:65:12
    #5 0x556c445ca23d in zend_execute /src/php-src/Zend/zend_vm_execute.h:115542:2
    #6 0x556c44acf65f in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:293:5
    #7 0x556c44ace3e7 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-execute.c:25:2
    #8 0x556c436269dd in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
    #9 0x556c43611752 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
    #10 0x556c43617620 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9
    #11 0x556c43643152 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #12 0x7f952ce2c082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)

DEDUP_TOKEN: __interceptor_free--zend_objects_store_del--zend_std_read_property
previously allocated by thread T0 here:
    #0 0x556c43745334 in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3
    #1 0x556c44456a01 in tracked_malloc /src/php-src/Zend/zend_alloc.c:3016:14
    #2 0x556c44a581dd in zend_objects_new /src/php-src/Zend/zend_objects.c:190:24
    #3 0x556c444767ad in _object_and_properties_init /src/php-src/Zend/zend_API.c:1819:22
    #4 0x556c4466c588 in ZEND_NEW_SPEC_CONST_UNUSED_HANDLER /src/php-src/Zend/zend_vm_execute.h:11366:6
    #5 0x556c44ace5fb in fuzzer_execute_ex /src/php-src/sapi/fuzzer/fuzzer-execute-common.h:65:12
    #6 0x556c445ca23d in zend_execute /src/php-src/Zend/zend_vm_execute.h:115542:2
    #7 0x556c44acf65f in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:293:5
    #8 0x556c44ace3e7 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-execute.c:25:2
    #9 0x556c436269dd in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
    #10 0x556c43611752 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
    #11 0x556c43617620 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:865:9
    #12 0x556c43643152 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #13 0x7f952ce2c082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)

DEDUP_TOKEN: __interceptor_malloc--tracked_malloc--zend_objects_new
SUMMARY: AddressSanitizer: heap-use-after-free /src/php-src/Zend/zend_lazy_objects.h:82:10 in zend_object_is_lazy
Shadow bytes around the buggy address:
  0x7bf52c2eff80: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
  0x7bf52c2f0000: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 fa
  0x7bf52c2f0080: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
  0x7bf52c2f0100: 00 00 00 00 00 00 00 fa fa fa fa fa fd fd fd fd
  0x7bf52c2f0180: fd fd fd fd fa fa fa fa 00 00 00 00 00 00 00 fa
=>0x7bf52c2f0200: fa fa fa fa fd[fd]fd fd fd fd fd fa fa fa fa fa
  0x7bf52c2f0280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf52c2f0300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf52c2f0380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf52c2f0400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7bf52c2f0480: 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
==14==ABORTING
PHP Version
PHP 8.5
Operating System

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the PHP snippet with an AddressSanitizer build, then inspect Zend/zend_lazy_objects.h and Zend/zend_object_handlers.c around zend_object_is_lazy, zend_lazy_object_must_init, and zend_std_read_property. Trace the object lifetime through the shown __isset callback and confirm the fix eliminates the heap-use-after-free under the same reproducer.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, php
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.