Skip to content

Commit

Permalink
Add support for internal enums
Browse files Browse the repository at this point in the history
This adds support for internal enums with the same basic approach
as userland enums. Enum values are stored as CONSTANT_AST and
objects created during constant updating at runtime. This means
that we need to use mutable_data for internal enums.

This just adds basic support and APIs, it does not include the
stubs integration from #7212.

Closes GH-7302.
  • Loading branch information
nikic committed Jul 27, 2021
1 parent ff8e04a commit a374230
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 12 deletions.
58 changes: 58 additions & 0 deletions Zend/tests/enum/internal_enums.phpt
@@ -0,0 +1,58 @@
--TEST--
Internal enums
--EXTENSIONS--
zend_test
--FILE--
<?php

var_dump($bar = ZendTestUnitEnum::Bar);
var_dump($bar === ZendTestUnitEnum::Bar);
var_dump($bar instanceof UnitEnum);

var_dump($foo = zend_get_unit_enum());
var_dump($foo === ZendTestUnitEnum::Foo);

var_dump(ZendTestUnitEnum::cases());
echo "\n";

var_dump($foo = ZendTestStringEnum::Foo);
var_dump($foo instanceof BackedEnum);
var_dump(ZendTestStringEnum::Foo->value);
var_dump($bar = ZendTestStringEnum::from("Test2"));
var_dump($bar === ZendTestStringEnum::Bar);
var_dump(ZendTestStringEnum::tryFrom("Test3"));
var_dump(ZendTestStringEnum::cases());

var_dump($s = serialize($foo));
var_dump(unserialize($s));
var_dump(unserialize($s) === $foo);

?>
--EXPECT--
enum(ZendTestUnitEnum::Bar)
bool(true)
bool(true)
enum(ZendTestUnitEnum::Foo)
bool(true)
array(2) {
[0]=>
enum(ZendTestUnitEnum::Foo)
[1]=>
enum(ZendTestUnitEnum::Bar)
}

enum(ZendTestStringEnum::Foo)
bool(true)
string(5) "Test1"
enum(ZendTestStringEnum::Bar)
bool(true)
NULL
array(2) {
[0]=>
enum(ZendTestStringEnum::Foo)
[1]=>
enum(ZendTestStringEnum::Bar)
}
string(30) "E:22:"ZendTestStringEnum:Foo";"
enum(ZendTestStringEnum::Foo)
bool(true)
5 changes: 3 additions & 2 deletions Zend/zend_API.c
Expand Up @@ -1298,7 +1298,6 @@ static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *cla
{
zend_class_mutable_data *mutable_data;

ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
ZEND_ASSERT(ZEND_MAP_PTR_GET_IMM(class_type->mutable_data) == NULL);

Expand Down Expand Up @@ -1331,7 +1330,6 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
_zend_hash_append_ptr(constants_table, key, c);
} ZEND_HASH_FOREACH_END();

ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);

mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
Expand Down Expand Up @@ -4365,6 +4363,9 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
if (ce->type == ZEND_INTERNAL_CLASS && !ZEND_MAP_PTR(ce->mutable_data)) {
ZEND_MAP_PTR_NEW(ce->mutable_data);
}
}

if (!zend_hash_add_ptr(&ce->constants_table, name, c)) {
Expand Down
6 changes: 2 additions & 4 deletions Zend/zend_API.h
Expand Up @@ -416,8 +416,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);

static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS)
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) && ZEND_MAP_PTR(ce->mutable_data)) {
zend_class_mutable_data *mutable_data =
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
if (mutable_data && mutable_data->constants_table) {
Expand All @@ -431,8 +430,7 @@ static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry
}

static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES) && ZEND_MAP_PTR(ce->mutable_data)) {
zend_class_mutable_data *mutable_data =
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
return mutable_data->default_properties_table;
Expand Down
4 changes: 0 additions & 4 deletions Zend/zend_ast.c
Expand Up @@ -38,10 +38,6 @@ static inline void *zend_ast_realloc(void *old, size_t old_size, size_t new_size
return new;
}

static inline size_t zend_ast_size(uint32_t children) {
return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
}

static inline size_t zend_ast_list_size(uint32_t children) {
return sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
}
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_ast.h
Expand Up @@ -307,6 +307,10 @@ ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast);
typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr, void *context);
ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *context);

static zend_always_inline size_t zend_ast_size(uint32_t children) {
return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
}

static zend_always_inline bool zend_ast_is_special(zend_ast *ast) {
return (ast->kind >> ZEND_AST_SPECIAL_SHIFT) & 1;
}
Expand Down
137 changes: 137 additions & 0 deletions Zend/zend_enum.c
Expand Up @@ -371,3 +371,140 @@ void zend_enum_register_props(zend_class_entry *ce)
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC, NULL, value_type);
}
}

static const zend_function_entry unit_enum_methods[] = {
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
};

static const zend_function_entry backed_enum_methods[] = {
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
};

ZEND_API zend_class_entry *zend_register_internal_enum(
const char *name, zend_uchar type, zend_function_entry *functions)
{
ZEND_ASSERT(type == IS_UNDEF || type == IS_LONG || type == IS_STRING);

zend_class_entry tmp_ce;
INIT_CLASS_ENTRY_EX(tmp_ce, name, strlen(name), functions);

zend_class_entry *ce = zend_register_internal_class(&tmp_ce);
ce->ce_flags |= ZEND_ACC_ENUM;
ce->enum_backing_type = type;
if (type != IS_UNDEF) {
ce->backed_enum_table = pemalloc(sizeof(HashTable), 1);
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 1);
}

zend_enum_register_props(ce);
if (type == IS_UNDEF) {
zend_register_functions(
ce, unit_enum_methods, &ce->function_table, EG(current_module)->type);
zend_class_implements(ce, 1, zend_ce_unit_enum);
} else {
zend_register_functions(
ce, backed_enum_methods, &ce->function_table, EG(current_module)->type);
zend_class_implements(ce, 1, zend_ce_backed_enum);
}

return ce;
}

static zend_ast_ref *create_enum_case_ast(
zend_string *class_name, zend_string *case_name, zval *value) {
// TODO: Use custom node type for enum cases?
size_t num_children = value ? 3 : 2;
size_t size = sizeof(zend_ast_ref) + zend_ast_size(num_children)
+ num_children * sizeof(zend_ast_zval);
char *p = pemalloc(size, 1);
zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
GC_SET_REFCOUNT(ref, 1);
GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;

zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
ast->kind = ZEND_AST_CONST_ENUM_INIT;
ast->attr = 0;
ast->lineno = 0;

ast->child[0] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[0]->kind = ZEND_AST_ZVAL;
ast->child[0]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(class_name));
ZVAL_STR(zend_ast_get_zval(ast->child[0]), class_name);

ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[1]->kind = ZEND_AST_ZVAL;
ast->child[1]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);

if (value) {
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[2]->kind = ZEND_AST_ZVAL;
ast->child[2]->attr = 0;
ZEND_ASSERT(!Z_REFCOUNTED_P(value));
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
} else {
ast->child[2] = NULL;
}

return ref;
}

ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
{
if (value) {
ZEND_ASSERT(ce->enum_backing_type == Z_TYPE_P(value));
if (Z_TYPE_P(value) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR_P(value))) {
zval_make_interned_string(value);
}

zval case_name_zv;
ZVAL_STR(&case_name_zv, case_name);
if (Z_TYPE_P(value) == IS_LONG) {
zend_hash_index_add_new(ce->backed_enum_table, Z_LVAL_P(value), &case_name_zv);
} else {
zend_hash_add_new(ce->backed_enum_table, Z_STR_P(value), &case_name_zv);
}
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
}

zval ast_zv;
Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
zend_class_constant *c = zend_declare_class_constant_ex(
ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
}

ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value)
{
zend_string *name_str = zend_string_init_interned(name, strlen(name), 1);
zend_enum_add_case(ce, name_str, value);
zend_string_release(name_str);
}

ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
ZEND_ASSERT(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE);

if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
ZEND_UNREACHABLE();
}
}
ZEND_ASSERT(Z_TYPE(c->value) == IS_OBJECT);
return Z_OBJ(c->value);
}

ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name) {
zend_string *name_str = zend_string_init(name, strlen(name), 0);
zend_object *result = zend_enum_get_case(ce, name_str);
zend_string_release(name_str);
return result;
}
7 changes: 7 additions & 0 deletions Zend/zend_enum.h
Expand Up @@ -34,6 +34,13 @@ void zend_verify_enum(zend_class_entry *ce);
void zend_enum_register_funcs(zend_class_entry *ce);
void zend_enum_register_props(zend_class_entry *ce);

ZEND_API zend_class_entry *zend_register_internal_enum(
const char *name, zend_uchar type, zend_function_entry *functions);
ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value);
ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value);
ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name);
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name);

static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj)
{
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
Expand Down
12 changes: 11 additions & 1 deletion Zend/zend_opcode.c
Expand Up @@ -397,6 +397,9 @@ ZEND_API void destroy_zend_class(zval *zv)
}
break;
case ZEND_INTERNAL_CLASS:
if (ce->backed_enum_table) {
zend_hash_release(ce->backed_enum_table);
}
if (ce->default_properties_table) {
zval *p = ce->default_properties_table;
zval *end = p + ce->default_properties_count;
Expand Down Expand Up @@ -442,7 +445,14 @@ ZEND_API void destroy_zend_class(zval *zv)

ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
if (c->ce == ce) {
zval_internal_ptr_dtor(&c->value);
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
/* We marked this as IMMUTABLE, but do need to free it when the
* class is destroyed. */
ZEND_ASSERT(Z_ASTVAL(c->value)->kind == ZEND_AST_CONST_ENUM_INIT);
free(Z_AST(c->value));
} else {
zval_internal_ptr_dtor(&c->value);
}
if (c->doc_comment) {
zend_string_release_ex(c->doc_comment, 1);
}
Expand Down
21 changes: 21 additions & 0 deletions ext/zend_test/test.c
Expand Up @@ -26,6 +26,7 @@
#include "observer.h"
#include "fiber.h"
#include "zend_attributes.h"
#include "zend_enum.h"
#include "Zend/Optimizer/zend_optimizer.h"

ZEND_DECLARE_MODULE_GLOBALS(zend_test)
Expand All @@ -38,6 +39,8 @@ static zend_class_entry *zend_test_attribute;
static zend_class_entry *zend_test_ns_foo_class;
static zend_class_entry *zend_test_ns2_foo_class;
static zend_class_entry *zend_test_ns2_ns_foo_class;
static zend_class_entry *zend_test_unit_enum;
static zend_class_entry *zend_test_string_enum;
static zend_object_handlers zend_test_class_handlers;

static ZEND_FUNCTION(zend_test_func)
Expand Down Expand Up @@ -227,6 +230,13 @@ static ZEND_FUNCTION(zend_iterable)
ZEND_PARSE_PARAMETERS_END();
}

static ZEND_FUNCTION(zend_get_unit_enum)
{
ZEND_PARSE_PARAMETERS_NONE();

RETURN_OBJ_COPY(zend_enum_get_case_cstr(zend_test_unit_enum, "Foo"));
}

static ZEND_FUNCTION(namespaced_func)
{
ZEND_PARSE_PARAMETERS_NONE();
Expand Down Expand Up @@ -384,6 +394,17 @@ PHP_MINIT_FUNCTION(zend_test)
zend_test_ns2_foo_class = register_class_ZendTestNS2_Foo();
zend_test_ns2_ns_foo_class = register_class_ZendTestNS2_ZendSubNS_Foo();

zend_test_unit_enum = zend_register_internal_enum("ZendTestUnitEnum", IS_UNDEF, NULL);
zend_enum_add_case_cstr(zend_test_unit_enum, "Foo", NULL);
zend_enum_add_case_cstr(zend_test_unit_enum, "Bar", NULL);

zval val;
zend_test_string_enum = zend_register_internal_enum("ZendTestStringEnum", IS_STRING, NULL);
ZVAL_PSTRINGL(&val, "Test1", sizeof("Test1")-1);
zend_enum_add_case_cstr(zend_test_string_enum, "Foo", &val);
ZVAL_PSTRINGL(&val, "Test2", sizeof("Test2")-1);
zend_enum_add_case_cstr(zend_test_string_enum, "Bar", &val);

// Loading via dl() not supported with the observer API
if (type != MODULE_TEMPORARY) {
REGISTER_INI_ENTRIES();
Expand Down
1 change: 1 addition & 0 deletions ext/zend_test/test.stub.php
Expand Up @@ -74,6 +74,7 @@ function zend_string_or_stdclass_or_null($param): stdClass|string|null {}

function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}

function zend_get_unit_enum(): ZendTestUnitEnum {}
}

namespace ZendTestNS {
Expand Down
7 changes: 6 additions & 1 deletion ext/zend_test/test_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 2a1f8ff8205507259ba19bd379a07b390bc525cd */
* Stub hash: 93bb8b9120e510e8c3afc29dc0a5d47cb6b5f10e */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
Expand Down Expand Up @@ -51,6 +51,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_ITERABLE, 1, "null")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_zend_get_unit_enum, 0, 0, ZendTestUnitEnum, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()

Expand Down Expand Up @@ -91,6 +94,7 @@ static ZEND_FUNCTION(zend_string_or_object_or_null);
static ZEND_FUNCTION(zend_string_or_stdclass);
static ZEND_FUNCTION(zend_string_or_stdclass_or_null);
static ZEND_FUNCTION(zend_iterable);
static ZEND_FUNCTION(zend_get_unit_enum);
static ZEND_FUNCTION(namespaced_func);
static ZEND_METHOD(_ZendTestClass, is_object);
static ZEND_METHOD(_ZendTestClass, __toString);
Expand All @@ -117,6 +121,7 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass)
ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null)
ZEND_FE(zend_iterable, arginfo_zend_iterable)
ZEND_FE(zend_get_unit_enum, arginfo_zend_get_unit_enum)
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
ZEND_FE_END
};
Expand Down

3 comments on commit a374230

@shqking
Copy link
Contributor

Choose a reason for hiding this comment

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

Hi. I'm afraid the PHP building with the configuration from community job is broken after this commit.

NTS+DEBUG+ASAN build of PHP. I used the similar configurations with the community job.

./configure --enable-address-sanitizer --enable-debug --enable-option-checking=fatal --prefix=/usr --enable-phpdbg --enable-fpm --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd --with-pgsql --with-pdo-pgsql --with-pdo-sqlite --enable-intl --without-pear --enable-gd --with-jpeg --with-webp --with-freetype --with-xpm --enable-exif --with-zip --with-zlib --with-zlib-dir=/usr --enable-soap --enable-xmlreader --with-xsl --with-tidy --enable-sysvsem --enable-sysvshm --enable-shmop --enable-pcntl --with-readline --enable-mbstring --with-curl --with-gettext --enable-sockets --with-bz2 --with-openssl --with-gmp --enable-bcmath --enable-calendar --enable-ftp --with-pspell=/usr --with-enchant=/usr --with-kerberos --enable-sysvmsg --with-ffi --enable-zend-test --with-ldap --with-ldap-sasl --with-mhash --with-password-argon2 --with-sodium --enable-dba --with-snmp --with-unixODBC --with-imap --with-kerberos --with-imap-ssl --with-pdo-odbc=unixODBC,/usr --with-pdo-firebird --with-pdo-dblib --with-pdo-oci=shared,instantclient,/opt/oracle/instantclient --with-oci8=shared,instantclient,/opt/oracle/instantclient --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d

Here is the ASAN error message:

==7061==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60700000bd00 at pc 0x563ea9943117 bp 0x7ffc99132320 sp 0x7ffc99132310
WRITE of size 4 at 0x60700000bd00 thread T0
    #0 0x563ea9943116 in create_enum_case_ast /tmp/php-src/Zend/zend_enum.c:443
    #1 0x563ea9943893 in zend_enum_add_case /tmp/php-src/Zend/zend_enum.c:479
    #2 0x563ea9943a2b in zend_enum_add_case_cstr /tmp/php-src/Zend/zend_enum.c:488
    #3 0x563ea9412745 in zm_startup_zend_test /tmp/php-src/ext/zend_test/test.c:398
    #4 0x563ea96cedf2 in zend_startup_module_ex /tmp/php-src/Zend/zend_API.c:2186
    #5 0x563ea96ceec5 in zend_startup_module_zval /tmp/php-src/Zend/zend_API.c:2201
    #6 0x563ea96f7bd9 in zend_hash_apply /tmp/php-src/Zend/zend_hash.c:1860
    #7 0x563ea96cfd67 in zend_startup_modules /tmp/php-src/Zend/zend_API.c:2312
    #8 0x563ea954c624 in php_module_startup /tmp/php-src/main/main.c:2240
    #9 0x563ea9a70e19 in php_cli_startup /tmp/php-src/sapi/cli/php_cli.c:409
    #10 0x563ea9a7548e in main /tmp/php-src/sapi/cli/php_cli.c:1334
    #11 0x7f14ff5a00b2 in __libc_start_main (/usr/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #12 0x563ea86648ed in _start (/tmp/php-src/sapi/cli/php+0x8098ed)

0x60700000bd00 is located 0 bytes to the right of 80-byte region [0x60700000bcb0,0x60700000bd00)
allocated by thread T0 here:
    #0 0x7f15015dabc8 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x563ea961fe41 in __zend_malloc /tmp/php-src/Zend/zend_alloc.c:3043
    #2 0x563ea9942b7a in create_enum_case_ast /tmp/php-src/Zend/zend_enum.c:423
    #3 0x563ea9943893 in zend_enum_add_case /tmp/php-src/Zend/zend_enum.c:479
    #4 0x563ea9943a2b in zend_enum_add_case_cstr /tmp/php-src/Zend/zend_enum.c:488
    #5 0x563ea9412745 in zm_startup_zend_test /tmp/php-src/ext/zend_test/test.c:398
    #6 0x563ea96cedf2 in zend_startup_module_ex /tmp/php-src/Zend/zend_API.c:2186
    #7 0x563ea96ceec5 in zend_startup_module_zval /tmp/php-src/Zend/zend_API.c:2201
    #8 0x563ea96f7bd9 in zend_hash_apply /tmp/php-src/Zend/zend_hash.c:1860
    #9 0x563ea96cfd67 in zend_startup_modules /tmp/php-src/Zend/zend_API.c:2312
    #10 0x563ea954c624 in php_module_startup /tmp/php-src/main/main.c:2240
    #11 0x563ea9a70e19 in php_cli_startup /tmp/php-src/sapi/cli/php_cli.c:409
    #12 0x563ea9a7548e in main /tmp/php-src/sapi/cli/php_cli.c:1334
    #13 0x7f14ff5a00b2 in __libc_start_main (/usr/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/php-src/Zend/zend_enum.c:443 in create_enum_case_ast
Shadow bytes around the buggy address:
  0x0c0e7fff9750: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa 00 00
  0x0c0e7fff9760: 00 00 00 00 00 00 00 fa fa fa fa fa fd fd fd fd
  0x0c0e7fff9770: fd fd fd fd fd fd fa fa fa fa fd fd fd fd fd fd
  0x0c0e7fff9780: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c0e7fff9790: fd fd fa fa fa fa 00 00 00 00 00 00 00 00 00 00
=>0x0c0e7fff97a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff97b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff97c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff97d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff97e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff97f0: 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
  Shadow gap:              cc
==7061==ABORTING
make: *** [Makefile:508: ext/phar/phar.php] Error 139

I'm not sure whether you can reproduce it or not. If yes, could you help to take a look at this error? Thanks.

@nikic
Copy link
Member Author

@nikic nikic commented on a374230 Jul 27, 2021

Choose a reason for hiding this comment

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

@shqking Thanks, it should be fixed by 645ef62.

@shqking
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. I just now suspected this line. Thanks for your quick fix.

Please sign in to comment.