Skip to content

Do not delete main chunk in zend_gc #18756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: PHP-8.3
Choose a base branch
from

Conversation

danog
Copy link
Contributor

@danog danog commented Jun 4, 2025

Fixes an issue where invoking zend_mm_gc twice with no allocations (and only one chunk, and no cached chunks) causes a segfault, because the first zend_mm_gc assigns NULL to the next property when copying the chunk to the (initially empty) cached chunks list, which breaks the main chunk list which expects a circular linked list instead of a null-terminated linked list.

This matches the behavior of the other invocation of zend_mm_gc, which is also skipped when acting upon the main chunk.

@danog
Copy link
Contributor Author

danog commented Jun 4, 2025

Ping @bukka / @nielsdos :)

@nielsdos
Copy link
Member

nielsdos commented Jun 4, 2025

On first sight it seems right but I would need to make myself more familiar with zend_alloc first to say for sure. Do you also have a test for this?

@danog
Copy link
Contributor Author

danog commented Jun 4, 2025

Calling zend_mm_gc twice in a row during the shutdown process (before the heap is freed obviously), or super early after it's created so it has no usage reproduces the issue.

Currently using https://github.com/danog/php-src/tree/heap_validation to try and catch a heap corruption bug (#17974) that won't popup with ASAN (w/ the zend allocator enabled), this is were I found the issue.

(Side note, the original #17974 issue seems to trigger when cleaning up the objects store, specifically when cleaning up a weakmap, currently debugging more to better reproduce that issue)

@bukka
Copy link
Member

bukka commented Jun 4, 2025

I'm not really zend_alloc expert - should be more a ping for @arnaud-lb I guess :)

Copy link
Member

@arnaud-lb arnaud-lb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirm the issue, and the fix looks good to me.

Here is a reproducer:

--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -1613,3 +1613,10 @@ static PHP_FUNCTION(zend_test_compile_to_ast)
 
        RETVAL_STR(result);
 }
+
+PHP_FUNCTION(zend_test_mm)
+{
+       zend_mm_heap *heap = zend_mm_startup();
+       zend_mm_gc(heap);
+       zend_mm_gc(heap);
+}
diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php
index 9923da6dcee..48be105d219 100644
--- a/ext/zend_test/test.stub.php
+++ b/ext/zend_test/test.stub.php
@@ -202,6 +202,8 @@ enum ZendTestIntEnum: int {
         case Baz = -1;
     }
 
+    function zend_test_mm(): void {}
+
     function zend_test_array_return(): array {}
 
     /** @genstubs-expose-comment-block
@@ -399,3 +401,4 @@ function namespaced_aliased_func(): void {}
      */
     function namespaced_deprecated_aliased_func(): void {}
 }

The first zend_mm_gc() call deletes the main chunk, which shouldn't happen. The second call crashes because of that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be in ext/zend_test and a --EXTENSION-- section for zend_test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--EXTENSION-- section for zend_test.

Indeed, my bad.

Should be in ext/zend_test

Are you sure? I see ext/zend_test as a set of APIs for testing underlying mechanisms that are outside of ext/zend_test. Tests in ext/zend_test/tests would be for testing zend_test itself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure?

No. What you say makes sense, boundaries are probably a little muddy 😃

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but needs a small fix in the test code (besides Tim's EXTENSIONS section remark)

@@ -1516,3 +1516,11 @@ static PHP_FUNCTION(zend_test_create_throwing_resource)
zend_resource *res = zend_register_resource(NULL, le_throwing_resource);
ZVAL_RES(return_value, res);
}

PHP_FUNCTION(zend_test_gh18756)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need ZEND_PARSE_PARAMETERS_NONE(); for the arginfo/zpp check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed (that's my mistake, not @danog's, I did push this test)

@arnaud-lb arnaud-lb force-pushed the do_not_delete_main_chunk branch from c58210c to 33a2155 Compare June 6, 2025 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants