Skip to content

Commit 4838205

Browse files
support neighbouring extension in the ZendMM custom handlers hook
1 parent ce96aa9 commit 4838205

File tree

4 files changed

+95
-31
lines changed

4 files changed

+95
-31
lines changed

ext/zend_test/php_test.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
5656
bool print_stderr_mshutdown;
5757
zend_long limit_copy_file_range;
5858
int observe_opline_in_zendmm;
59-
zend_mm_heap* zend_orig_heap;
60-
zend_mm_heap* zend_test_heap;
59+
// previous handlers that might have been installed
60+
// `USE_ZEND_ALLOC=0` installs custom handlers
61+
void* (*zendmm_orig_malloc)(size_t);
62+
void (*zendmm_orig_free)(void*);
63+
void* (*zendmm_orig_realloc)(void *, size_t);
6164
zend_test_fiber *active_fiber;
6265
zend_long quantity_value;
6366
zend_string *str_test;

ext/zend_test/test.c

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -641,28 +641,110 @@ static bool has_opline(zend_execute_data *execute_data)
641641
;
642642
}
643643

644+
// When a custom memory manager is installed, ZendMM alters its behaviour. For
645+
// example `zend_mm_gc()` will not do anything anymore, or `zend_mm_shutdown()`
646+
// won't cleanup chunks and leak memory. This might not be a problem in tests,
647+
// but I fear folks might see and use this implementation as a boiler plate when
648+
// trying to use this hook
649+
int zend_test_prepare_zendmm_for_call(zend_mm_heap *heap)
650+
{
651+
int flag = 0;
652+
memcpy(&flag, heap, sizeof(int));
653+
int new_flag = 0;
654+
memcpy(heap, &new_flag, sizeof(int));
655+
return flag;
656+
}
657+
658+
void zend_test_restore_zendmm_after_call(zend_mm_heap *heap, int flag)
659+
{
660+
memcpy(heap, &flag, sizeof(int));
661+
}
662+
644663
void * zend_test_custom_malloc(size_t len)
645664
{
646665
if (has_opline(EG(current_execute_data))) {
647666
assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1);
648667
}
649-
return _zend_mm_alloc(ZT_G(zend_orig_heap), len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
668+
if (ZT_G(zendmm_orig_malloc)) {
669+
return ZT_G(zendmm_orig_malloc)(len);
670+
}
671+
int flag = zend_test_prepare_zendmm_for_call(zend_mm_get_heap());
672+
void * ptr = _zend_mm_alloc(zend_mm_get_heap(), len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
673+
zend_test_restore_zendmm_after_call(zend_mm_get_heap(), flag);
674+
return ptr;
650675
}
651676

652677
void zend_test_custom_free(void *ptr)
653678
{
654679
if (has_opline(EG(current_execute_data))) {
655680
assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1);
656681
}
657-
_zend_mm_free(ZT_G(zend_orig_heap), ptr ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
682+
if (ZT_G(zendmm_orig_free)) {
683+
ZT_G(zendmm_orig_free)(ptr);
684+
return;
685+
}
686+
int flag = zend_test_prepare_zendmm_for_call(zend_mm_get_heap());
687+
_zend_mm_free(zend_mm_get_heap(), ptr ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
688+
zend_test_restore_zendmm_after_call(zend_mm_get_heap(), flag);
658689
}
659690

660691
void * zend_test_custom_realloc(void * ptr, size_t len)
661692
{
662693
if (has_opline(EG(current_execute_data))) {
663694
assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1);
664695
}
665-
return _zend_mm_realloc(ZT_G(zend_orig_heap), ptr, len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
696+
if (ZT_G(zendmm_orig_realloc)) {
697+
return ZT_G(zendmm_orig_realloc)(ptr, len);
698+
}
699+
int flag = zend_test_prepare_zendmm_for_call(zend_mm_get_heap());
700+
void * new_ptr = _zend_mm_realloc(zend_mm_get_heap(), ptr, len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
701+
zend_test_restore_zendmm_after_call(zend_mm_get_heap(), flag);
702+
return new_ptr;
703+
}
704+
705+
void zend_test_install_custom_mm_handler(void)
706+
{
707+
// fetch maybe installed previous handlers
708+
if (!is_zend_mm()) {
709+
zend_mm_get_custom_handlers(
710+
zend_mm_get_heap(),
711+
&ZT_G(zendmm_orig_malloc),
712+
&ZT_G(zendmm_orig_free),
713+
&ZT_G(zendmm_orig_realloc)
714+
);
715+
}
716+
zend_mm_set_custom_handlers(
717+
zend_mm_get_heap(),
718+
zend_test_custom_malloc,
719+
zend_test_custom_free,
720+
zend_test_custom_realloc
721+
);
722+
}
723+
724+
void zend_test_uninstall_custom_mm_handler(void)
725+
{
726+
void* (*custom_malloc)(size_t);
727+
void (*custom_free)(void*);
728+
void* (*custom_realloc)(void *, size_t);
729+
zend_mm_get_custom_handlers(
730+
zend_mm_get_heap(),
731+
&custom_malloc,
732+
&custom_free,
733+
&custom_realloc
734+
);
735+
if (custom_malloc == zend_test_custom_malloc ||
736+
custom_free == zend_test_custom_free ||
737+
custom_realloc == zend_test_custom_realloc) {
738+
zend_mm_set_custom_handlers(
739+
zend_mm_get_heap(),
740+
ZT_G(zendmm_orig_malloc),
741+
ZT_G(zendmm_orig_free),
742+
ZT_G(zendmm_orig_realloc)
743+
);
744+
ZT_G(zendmm_orig_malloc) = NULL;
745+
ZT_G(zendmm_orig_free) = NULL;
746+
ZT_G(zendmm_orig_realloc) = NULL;
747+
}
666748
}
667749

668750
static PHP_INI_MH(OnUpdateZendTestObserveOplineInZendMM)
@@ -674,22 +756,9 @@ static PHP_INI_MH(OnUpdateZendTestObserveOplineInZendMM)
674756
int int_value = zend_ini_parse_bool(new_value);
675757

676758
if (int_value == 1) {
677-
// `zend_mm_heap` is a private struct, so we have not way to find the
678-
// actual size, but 4096 bytes should be enough
679-
ZT_G(zend_test_heap) = malloc(4096);
680-
memset(ZT_G(zend_test_heap), 0, 4096);
681-
zend_mm_set_custom_handlers(
682-
ZT_G(zend_test_heap),
683-
zend_test_custom_malloc,
684-
zend_test_custom_free,
685-
zend_test_custom_realloc
686-
);
687-
ZT_G(zend_orig_heap) = zend_mm_get_heap();
688-
zend_mm_set_heap(ZT_G(zend_test_heap));
689-
} else if (ZT_G(zend_test_heap)) {
690-
free(ZT_G(zend_test_heap));
691-
ZT_G(zend_test_heap) = NULL;
692-
zend_mm_set_heap(ZT_G(zend_orig_heap));
759+
zend_test_install_custom_mm_handler();
760+
} else {
761+
zend_test_uninstall_custom_mm_handler();
693762
}
694763
return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
695764
}
@@ -1206,6 +1275,8 @@ PHP_MSHUTDOWN_FUNCTION(zend_test)
12061275

12071276
zend_test_observer_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU);
12081277

1278+
zend_test_uninstall_custom_mm_handler();
1279+
12091280
if (ZT_G(print_stderr_mshutdown)) {
12101281
fprintf(stderr, "[zend_test] MSHUTDOWN\n");
12111282
}
@@ -1228,12 +1299,6 @@ PHP_RSHUTDOWN_FUNCTION(zend_test)
12281299
} ZEND_HASH_FOREACH_END();
12291300
zend_hash_destroy(&ZT_G(global_weakmap));
12301301

1231-
if (ZT_G(zend_test_heap)) {
1232-
free(ZT_G(zend_test_heap));
1233-
ZT_G(zend_test_heap) = NULL;
1234-
zend_mm_set_heap(ZT_G(zend_orig_heap));
1235-
}
1236-
12371302
return SUCCESS;
12381303
}
12391304

ext/zend_test/tests/opline_dangling.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ possible segfault in `ZEND_BIND_STATIC`
44
https://github.com/php/php-src/pull/12758
55
--EXTENSIONS--
66
zend_test
7-
--ENV--
8-
USE_ZEND_ALLOC=1
97
--INI--
108
zend_test.observe_opline_in_zendmm=1
119
--FILE--

ext/zend_test/tests/opline_dangling_02.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
possible segfault in `ZEND_FUNC_GET_ARGS`
33
--EXTENSIONS--
44
zend_test
5-
--ENV--
6-
USE_ZEND_ALLOC=1
75
--INI--
86
zend_test.observe_opline_in_zendmm=1
97
--FILE--

0 commit comments

Comments
 (0)