Skip to content

Commit 648fbe9

Browse files
committed
Fixed bug #43128 (Very long class name causes segfault)
1 parent 1836daf commit 648fbe9

18 files changed

+98
-57
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ PHP NEWS
6262
- Fixed bug #43136 (possible crash on script execution timeout.
6363
The EG(function_state_ptr) is completely removed,
6464
EG(current_execute_data)->function_state must be used instead). (Dmitry)
65+
- Fixed bug #43128 (Very long class name causes segfault). (Dmitry)
6566
- Fixed bug #42848 (Status: header incorrect under FastCGI). (Dmitry)
6667
- Fixed bug #42773 (WSDL error causes HTTP 500 Response). (Dmitry)
6768
- Fixed bug #42737 (preg_split('//u') triggers a E_NOTICE with newlines). (Nuno)

TSRM/tsrm_config_common.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,17 @@ char *alloca ();
5252
#endif
5353

5454
#if (HAVE_ALLOCA || (defined (__GNUC__) && __GNUC__ >= 2))
55-
# define tsrm_do_alloca(p) alloca(p)
56-
# define tsrm_free_alloca(p)
55+
# define TSRM_ALLOCA_MAX_SIZE 4096
56+
# define TSRM_ALLOCA_FLAG(name) \
57+
int name;
58+
# define tsrm_do_alloca_ex(size, limit, use_heap) \
59+
((use_heap = ((size) > (limit))) ? malloc(size) : alloca(size))
60+
# define tsrm_do_alloca(size, use_heap) \
61+
tsrm_do_alloca_ex(size, TSRM_ALLOCA_MAX_SIZE, use_heap)
62+
# define tsrm_free_alloca(p, use_heap) \
63+
do { if (use_heap) free(p); } while (0)
5764
#else
65+
# define TSRM_ALLOCA_FLAG(name)
5866
# define tsrm_do_alloca(p) malloc(p)
5967
# define tsrm_free_alloca(p) free(p)
6068
#endif

TSRM/tsrm_virtual_cwd.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path
777777
int length = strlen(path);
778778
char *temp;
779779
int retval;
780+
TSRM_ALLOCA_FLAG(use_heap)
780781

781782
if (length == 0) {
782783
return 1; /* Can't cd to empty string */
@@ -793,14 +794,14 @@ CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path
793794
if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
794795
length++;
795796
}
796-
temp = (char *) tsrm_do_alloca(length+1);
797+
temp = (char *) tsrm_do_alloca(length+1, use_heap);
797798
memcpy(temp, path, length);
798799
temp[length] = 0;
799800
#if VIRTUAL_CWD_DEBUG
800801
fprintf (stderr, "Changing directory to %s\n", temp);
801802
#endif
802803
retval = p_chdir(temp TSRMLS_CC);
803-
tsrm_free_alloca(temp);
804+
tsrm_free_alloca(temp, use_heap);
804805
return retval;
805806
}
806807
/* }}} */

Zend/tests/bug43128.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Bug #43128 Very long class name causes segfault
3+
--FILE--
4+
<?php
5+
6+
$a = str_repeat("a", 10 * 1024 * 1024);
7+
8+
# call_user_func($a); // Warning
9+
# $a->$a(); // Fatal error
10+
11+
if ($a instanceof $a); // Segmentation fault
12+
new $a; // Segmentation fault
13+
echo "ok\n";
14+
--EXPECT--
15+
ok

Zend/zend.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,17 @@ char *alloca ();
177177
#endif
178178

179179
#if (HAVE_ALLOCA || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(ZEND_WIN32)) && !(defined(ZTS) && defined(NETWARE)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN)
180-
# define do_alloca(p) alloca(p)
181-
# define free_alloca(p)
180+
# define ZEND_ALLOCA_MAX_SIZE (32 * 1024)
181+
# define ALLOCA_FLAG(name) \
182+
zend_bool name;
183+
# define do_alloca_ex(size, limit, use_heap) \
184+
((use_heap = (UNEXPECTED((size) > (limit)))) ? emalloc(size) : alloca(size))
185+
# define do_alloca(size, use_heap) \
186+
do_alloca_ex(size, ZEND_ALLOCA_MAX_SIZE, use_heap)
187+
# define free_alloca(p, use_heap) \
188+
do { if (UNEXPECTED(use_heap)) efree(p); } while (0)
182189
#else
190+
# define ALLOCA_FLAG(name)
183191
# define do_alloca(p) emalloc(p)
184192
# define free_alloca(p) efree(p)
185193
#endif

Zend/zend_API.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,11 +1863,10 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
18631863
}
18641864
}
18651865
fname_len = strlen(ptr->fname);
1866-
lowercase_name = do_alloca(fname_len+1);
1867-
zend_str_tolower_copy(lowercase_name, ptr->fname, fname_len);
1866+
lowercase_name = zend_str_tolower_dup(ptr->fname, fname_len);
18681867
if (zend_hash_add(target_function_table, lowercase_name, fname_len+1, &function, sizeof(zend_function), (void**)&reg_function) == FAILURE) {
18691868
unload=1;
1870-
free_alloca(lowercase_name);
1869+
efree(lowercase_name);
18711870
break;
18721871
}
18731872
if (scope) {
@@ -1909,7 +1908,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
19091908
}
19101909
ptr++;
19111910
count++;
1912-
free_alloca(lowercase_name);
1911+
efree(lowercase_name);
19131912
}
19141913
if (unload) { /* before unloading, display all remaining bad function in the module */
19151914
if (scope) {

Zend/zend_builtin_functions.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,19 +1034,20 @@ ZEND_FUNCTION(class_exists)
10341034
char *class_name, *lc_name;
10351035
zend_class_entry **ce;
10361036
int class_name_len;
1037-
zend_bool autoload = 1;
10381037
int found;
1038+
zend_bool autoload = 1;
1039+
ALLOCA_FLAG(use_heap)
10391040

10401041
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &class_name, &class_name_len, &autoload) == FAILURE) {
10411042
return;
10421043
}
10431044

10441045
if (!autoload) {
1045-
lc_name = do_alloca(class_name_len + 1);
1046+
lc_name = do_alloca(class_name_len + 1, use_heap);
10461047
zend_str_tolower_copy(lc_name, class_name, class_name_len);
10471048

10481049
found = zend_hash_find(EG(class_table), lc_name, class_name_len+1, (void **) &ce);
1049-
free_alloca(lc_name);
1050+
free_alloca(lc_name, use_heap);
10501051
RETURN_BOOL(found == SUCCESS && !((*ce)->ce_flags & ZEND_ACC_INTERFACE));
10511052
}
10521053

@@ -1065,19 +1066,20 @@ ZEND_FUNCTION(interface_exists)
10651066
char *iface_name, *lc_name;
10661067
zend_class_entry **ce;
10671068
int iface_name_len;
1068-
zend_bool autoload = 1;
10691069
int found;
1070+
zend_bool autoload = 1;
1071+
ALLOCA_FLAG(use_heap)
10701072

10711073
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &iface_name, &iface_name_len, &autoload) == FAILURE) {
10721074
return;
10731075
}
10741076

10751077
if (!autoload) {
1076-
lc_name = do_alloca(iface_name_len + 1);
1078+
lc_name = do_alloca(iface_name_len + 1, use_heap);
10771079
zend_str_tolower_copy(lc_name, iface_name, iface_name_len);
10781080

10791081
found = zend_hash_find(EG(class_table), lc_name, iface_name_len+1, (void **) &ce);
1080-
free_alloca(lc_name);
1082+
free_alloca(lc_name, use_heap);
10811083
RETURN_BOOL(found == SUCCESS && (*ce)->ce_flags & ZEND_ACC_INTERFACE);
10821084
}
10831085

Zend/zend_compile.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
10881088
zend_uint fn_flags;
10891089
char *lcname;
10901090
zend_bool orig_interactive;
1091+
ALLOCA_FLAG(use_heap)
10911092

10921093
if (is_method) {
10931094
if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) {
@@ -1148,7 +1149,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
11481149
}
11491150

11501151
if (!(CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)) {
1151-
short_class_name = do_alloca(short_class_name_length + 1);
1152+
short_class_name = do_alloca(short_class_name_length + 1, use_heap);
11521153
zend_str_tolower_copy(short_class_name, CG(active_class_entry)->name, short_class_name_length);
11531154
/* Improve after RC: cache the lowercase class name */
11541155

@@ -1184,7 +1185,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
11841185
} else if (!(fn_flags & ZEND_ACC_STATIC)) {
11851186
CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC;
11861187
}
1187-
free_alloca(short_class_name);
1188+
free_alloca(short_class_name, use_heap);
11881189
}
11891190

11901191
efree(lcname);

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ struct _zend_execute_data {
299299
union _temp_variable *Ts;
300300
zval ***CVs;
301301
zend_bool original_in_execution;
302+
ALLOCA_FLAG(use_heap)
302303
HashTable *symbol_table;
303304
struct _zend_execute_data *prev_execute_data;
304305
zval *old_error_reporting;

Zend/zend_execute.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,12 +1427,7 @@ ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, int return_v
14271427
}
14281428

14291429
#define ZEND_VM_EXIT_FROM_EXECUTE_LOOP() \
1430-
free_alloca(EX(CVs)); \
1431-
if (EX(op_array)->T < TEMP_VAR_STACK_LIMIT) { \
1432-
free_alloca(EX(Ts)); \
1433-
} else { \
1434-
efree(EX(Ts)); \
1435-
} \
1430+
free_alloca(EX(CVs), EX(use_heap)); \
14361431
EG(in_execution) = EX(original_in_execution); \
14371432
EG(current_execute_data) = EX(prev_execute_data); \
14381433
EG(opline_ptr) = NULL;

Zend/zend_execute_API.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,15 +1082,16 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, int use_aut
10821082
char *lc_name;
10831083
char *lc_free;
10841084
zval *exception;
1085-
char dummy = 1;
10861085
zend_fcall_info fcall_info;
10871086
zend_fcall_info_cache fcall_cache;
1087+
char dummy = 1;
1088+
ALLOCA_FLAG(use_heap)
10881089

10891090
if (name == NULL || !name_length) {
10901091
return FAILURE;
10911092
}
10921093

1093-
lc_free = lc_name = do_alloca(name_length + 1);
1094+
lc_free = lc_name = do_alloca(name_length + 1, use_heap);
10941095
zend_str_tolower_copy(lc_name, name, name_length);
10951096

10961097
if (lc_name[0] == ':' && lc_name[1] == ':') {
@@ -1099,15 +1100,15 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, int use_aut
10991100
}
11001101

11011102
if (zend_hash_find(EG(class_table), lc_name, name_length + 1, (void **) ce) == SUCCESS) {
1102-
free_alloca(lc_free);
1103+
free_alloca(lc_free, use_heap);
11031104
return SUCCESS;
11041105
}
11051106

11061107
/* The compiler is not-reentrant. Make sure we __autoload() only during run-time
11071108
* (doesn't impact fuctionality of __autoload()
11081109
*/
11091110
if (!use_autoload || zend_is_compiling(TSRMLS_C)) {
1110-
free_alloca(lc_free);
1111+
free_alloca(lc_free, use_heap);
11111112
return FAILURE;
11121113
}
11131114

@@ -1117,7 +1118,7 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, int use_aut
11171118
}
11181119

11191120
if (zend_hash_add(EG(in_autoload), lc_name, name_length + 1, (void**)&dummy, sizeof(char), NULL) == FAILURE) {
1120-
free_alloca(lc_free);
1121+
free_alloca(lc_free, use_heap);
11211122
return FAILURE;
11221123
}
11231124

@@ -1155,12 +1156,12 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, int use_aut
11551156

11561157
if (retval == FAILURE) {
11571158
EG(exception) = exception;
1158-
free_alloca(lc_free);
1159+
free_alloca(lc_free, use_heap);
11591160
return FAILURE;
11601161
}
11611162

11621163
if (EG(exception) && exception) {
1163-
free_alloca(lc_free);
1164+
free_alloca(lc_free, use_heap);
11641165
zend_error(E_ERROR, "Function %s(%s) threw an exception of type '%s'", ZEND_AUTOLOAD_FUNC_NAME, name, Z_OBJCE_P(EG(exception))->name);
11651166
return FAILURE;
11661167
}
@@ -1172,7 +1173,7 @@ ZEND_API int zend_lookup_class_ex(const char *name, int name_length, int use_aut
11721173
}
11731174

11741175
retval = zend_hash_find(EG(class_table), lc_name, name_length + 1, (void **) ce);
1175-
free_alloca(lc_free);
1176+
free_alloca(lc_free, use_heap);
11761177
return retval;
11771178
}
11781179
/* }}} */

Zend/zend_object_handlers.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -776,14 +776,15 @@ static union _zend_function *zend_std_get_method(zval **object_ptr, char *method
776776
zend_function *fbc;
777777
char *lc_method_name;
778778
zval *object = *object_ptr;
779+
ALLOCA_FLAG(use_heap)
779780

780-
lc_method_name = do_alloca(method_len+1);
781+
lc_method_name = do_alloca(method_len+1, use_heap);
781782
/* Create a zend_copy_str_tolower(dest, src, src_length); */
782783
zend_str_tolower_copy(lc_method_name, method_name, method_len);
783784

784785
zobj = Z_OBJ_P(object);
785786
if (zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&fbc) == FAILURE) {
786-
free_alloca(lc_method_name);
787+
free_alloca(lc_method_name, use_heap);
787788
if (zobj->ce->__call) {
788789
zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
789790
call_user_call->type = ZEND_INTERNAL_FUNCTION;
@@ -838,7 +839,7 @@ static union _zend_function *zend_std_get_method(zval **object_ptr, char *method
838839
}
839840
}
840841

841-
free_alloca(lc_method_name);
842+
free_alloca(lc_method_name, use_heap);
842843
return fbc;
843844
}
844845
/* }}} */

Zend/zend_vm_execute.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC)
4545
EX(called_scope) = NULL;
4646
EX(object) = NULL;
4747
EX(old_error_reporting) = NULL;
48-
if (op_array->T < TEMP_VAR_STACK_LIMIT) {
49-
EX(Ts) = (temp_variable *) do_alloca(sizeof(temp_variable) * op_array->T);
48+
if (EXPECTED(op_array->T < TEMP_VAR_STACK_LIMIT && op_array->last_var < TEMP_VAR_STACK_LIMIT)) {
49+
EX(CVs) = (zval***)do_alloca(sizeof(zval**) * op_array->last_var + sizeof(temp_variable) * op_array->T, EX(use_heap));
5050
} else {
51-
EX(Ts) = (temp_variable *) safe_emalloc(sizeof(temp_variable), op_array->T, 0);
51+
EX(use_heap) = 1;
52+
EX(CVs) = (zval***)safe_emalloc(sizeof(temp_variable), op_array->T, sizeof(zval**) * op_array->last_var);
5253
}
53-
EX(CVs) = (zval***)do_alloca(sizeof(zval**) * op_array->last_var);
54+
EX(Ts) = (temp_variable *)(EX(CVs) + op_array->last_var);
5455
memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var);
5556
EX(op_array) = op_array;
5657
EX(original_in_execution) = EG(in_execution);

Zend/zend_vm_execute.skl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC)
1616
EX(called_scope) = NULL;
1717
EX(object) = NULL;
1818
EX(old_error_reporting) = NULL;
19-
if (op_array->T < TEMP_VAR_STACK_LIMIT) {
20-
EX(Ts) = (temp_variable *) do_alloca(sizeof(temp_variable) * op_array->T);
19+
if (EXPECTED(op_array->T < TEMP_VAR_STACK_LIMIT && op_array->last_var < TEMP_VAR_STACK_LIMIT)) {
20+
EX(CVs) = (zval***)do_alloca(sizeof(zval**) * op_array->last_var + sizeof(temp_variable) * op_array->T, EX(use_heap));
2121
} else {
22-
EX(Ts) = (temp_variable *) safe_emalloc(sizeof(temp_variable), op_array->T, 0);
22+
EX(use_heap) = 1;
23+
EX(CVs) = (zval***)safe_emalloc(sizeof(temp_variable), op_array->T, sizeof(zval**) * op_array->last_var);
2324
}
24-
EX(CVs) = (zval***)do_alloca(sizeof(zval**) * op_array->last_var);
25+
EX(Ts) = (temp_variable *)(EX(CVs) + op_array->last_var);
2526
memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var);
2627
EX(op_array) = op_array;
2728
EX(original_in_execution) = EG(in_execution);

ext/interbase/ibase_query.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,7 @@ PHP_FUNCTION(ibase_execute)
18431843
zval *query, ***args = NULL;
18441844
ibase_query *ib_query;
18451845
ibase_result *result = NULL;
1846+
ALLOCA_FLAG(use_heap)
18461847

18471848
RESET_ERRMSG;
18481849

@@ -1866,7 +1867,7 @@ PHP_FUNCTION(ibase_execute)
18661867
}
18671868

18681869
} else if (bind_n > 0) { /* have variables to bind */
1869-
args = (zval ***) do_alloca(ZEND_NUM_ARGS() * sizeof(zval **));
1870+
args = (zval ***) do_alloca(ZEND_NUM_ARGS() * sizeof(zval **), use_heap);
18701871

18711872
if (FAILURE == zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args)) {
18721873
break;
@@ -1907,7 +1908,7 @@ PHP_FUNCTION(ibase_execute)
19071908
} while (0);
19081909

19091910
if (args) {
1910-
free_alloca(args);
1911+
free_alloca(args, use_heap);
19111912
}
19121913
}
19131914
/* }}} */

0 commit comments

Comments
 (0)