Skip to content

Safe timeout interrupt handling (max_execution_time). #1173

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions Zend/zend_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2284,15 +2284,11 @@ ZEND_API void* ZEND_FASTCALL _ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_D
{
void *p;

HANDLE_BLOCK_INTERRUPTIONS();

p = _safe_emalloc(nmemb, size, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memset(p, 0, size * nmemb);
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}

Expand All @@ -2301,33 +2297,25 @@ ZEND_API char* ZEND_FASTCALL _estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_
size_t length;
char *p;

HANDLE_BLOCK_INTERRUPTIONS();

length = strlen(s)+1;
p = (char *) _emalloc(length ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memcpy(p, s, length);
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}

ZEND_API char* ZEND_FASTCALL _estrndup(const char *s, size_t length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
char *p;

HANDLE_BLOCK_INTERRUPTIONS();

p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memcpy(p, s, length);
p[length] = 0;
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}

Expand All @@ -2336,18 +2324,14 @@ ZEND_API char* ZEND_FASTCALL zend_strndup(const char *s, size_t length)
{
char *p;

HANDLE_BLOCK_INTERRUPTIONS();

p = (char *) malloc(length+1);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
if (length) {
memcpy(p, s, length);
}
p[length] = 0;
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}

Expand Down
13 changes: 13 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -2035,13 +2035,25 @@ static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data
}
/* }}} */

#define ZEND_VM_CHECK_INTERRUPT() do { \
if (UNEXPECTED(EG(vm_interrupt))) { \
ZEND_VM_INTERRUPT(); \
} \
} while (0)

#define ZEND_VM_NEXT_OPCODE() \
CHECK_SYMBOL_TABLES() \
ZEND_VM_INC_OPCODE(); \
ZEND_VM_CONTINUE()

#define ZEND_VM_NEXT_OPCODE_EX(new_op) \
CHECK_SYMBOL_TABLES() \
OPLINE = new_op; \
ZEND_VM_CONTINUE()

#define ZEND_VM_SET_OPCODE(new_op) \
CHECK_SYMBOL_TABLES() \
ZEND_VM_CHECK_INTERRUPT(); \
OPLINE = new_op

#define ZEND_VM_SET_RELATIVE_OPCODE(opline, offset) \
Expand All @@ -2050,6 +2062,7 @@ static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data
#define ZEND_VM_JMP(new_op) \
if (EXPECTED(!EG(exception))) { \
ZEND_VM_SET_OPCODE(new_op); \
ZEND_VM_CHECK_INTERRUPT(); \
} else { \
LOAD_OPLINE(); \
} \
Expand Down
29 changes: 8 additions & 21 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ void init_executor(void) /* {{{ */

zend_hash_init(&EG(included_files), 8, NULL, NULL, 0);

EG(vm_interrupt) = 0;
EG(timed_out) = 0;
EG(ticks_count) = 0;

ZVAL_UNDEF(&EG(user_error_handler));
Expand All @@ -172,9 +174,6 @@ void init_executor(void) /* {{{ */
zend_objects_store_init(&EG(objects_store), 1024);

EG(full_tables_cleanup) = 0;
#ifdef ZEND_WIN32
EG(timed_out) = 0;
#endif

EG(exception) = NULL;
EG(prev_exception) = NULL;
Expand Down Expand Up @@ -1160,21 +1159,8 @@ ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name,

ZEND_API void zend_timeout(int dummy) /* {{{ */
{

if (zend_on_timeout) {
#ifdef ZEND_SIGNALS
/*
We got here because we got a timeout signal, so we are in a signal handler
at this point. However, we want to be able to timeout any user-supplied
shutdown functions, so pretend we are not in a signal handler while we are
calling these
*/
SIGG(running) = 0;
#endif
zend_on_timeout(EG(timeout_seconds));
}

zend_error(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
EG(timed_out) = 1;
EG(vm_interrupt) = 1;
}
/* }}} */

Expand All @@ -1189,8 +1175,9 @@ VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out)
return;
}

php_timed_out = (zend_bool *)arg;
*php_timed_out = 1;
php_timed_out = (zend_interrupt *)arg;
php_timed_out[1] = 1;
php_timed_out[0] = 1;
}
#endif

Expand Down Expand Up @@ -1223,7 +1210,7 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
}

/* XXX passing NULL means the default timer queue provided by the system is used */
if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)&EG(timed_out), seconds*1000, 0, WT_EXECUTEONLYONCE)) {
if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)&EG(vm_interrupt), seconds*1000, 0, WT_EXECUTEONLYONCE)) {
EG(timed_out) = 0;
tq_timer = NULL;
zend_error(E_ERROR, "Could not queue new timer");
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ struct _zend_executor_globals {

zend_long precision;

zend_bool vm_interrupt;
zend_bool timed_out;

int ticks_count;

HashTable *in_autoload;
Expand Down
30 changes: 0 additions & 30 deletions Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,8 @@ static void zend_hash_packed_grow(HashTable *ht)
if (ht->nTableSize >= HT_MAX_SIZE) {
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket));
}
HANDLE_BLOCK_INTERRUPTIONS();
ht->nTableSize += ht->nTableSize;
zend_hash_realloc(ht, ht->nTableSize * sizeof(Bucket));
HANDLE_UNBLOCK_INTERRUPTIONS();
}

ZEND_API void zend_hash_real_init(HashTable *ht, zend_bool packed)
Expand All @@ -195,24 +193,20 @@ ZEND_API void zend_hash_real_init(HashTable *ht, zend_bool packed)
ZEND_API void zend_hash_packed_to_hash(HashTable *ht)
{
HT_ASSERT(GC_REFCOUNT(ht) == 1);
HANDLE_BLOCK_INTERRUPTIONS();
ht->u.flags &= ~HASH_FLAG_PACKED;
ht->nTableMask = ht->nTableSize - 1;
zend_hash_realloc(ht, ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)));
ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize);
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
}

ZEND_API void zend_hash_to_packed(HashTable *ht)
{
HT_ASSERT(GC_REFCOUNT(ht) == 1);
HANDLE_BLOCK_INTERRUPTIONS();
ht->u.flags |= HASH_FLAG_PACKED;
ht->nTableMask = 0;
zend_hash_realloc(ht, ht->nTableSize * sizeof(Bucket));
ht->arHash = (uint32_t*)&uninitialized_bucket;
HANDLE_UNBLOCK_INTERRUPTIONS();
}

ZEND_API void _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
Expand All @@ -236,21 +230,17 @@ ZEND_API void zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed)
if (packed) {
ZEND_ASSERT(ht->u.flags & HASH_FLAG_PACKED);
if (nSize > ht->nTableSize) {
HANDLE_BLOCK_INTERRUPTIONS();
ht->nTableSize = zend_hash_check_size(nSize);
zend_hash_realloc(ht, ht->nTableSize * sizeof(Bucket));
HANDLE_UNBLOCK_INTERRUPTIONS();
}
} else {
ZEND_ASSERT(!(ht->u.flags & HASH_FLAG_PACKED));
if (nSize > ht->nTableSize) {
HANDLE_BLOCK_INTERRUPTIONS();
ht->nTableSize = zend_hash_check_size(nSize);
zend_hash_realloc(ht, ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)));
ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize);
ht->nTableMask = ht->nTableSize - 1;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
}
}
}
Expand Down Expand Up @@ -492,20 +482,17 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s
if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
data = Z_INDIRECT_P(data);
}
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pDestructor) {
ht->pDestructor(data);
}
ZVAL_COPY_VALUE(data, pData);
HANDLE_UNBLOCK_INTERRUPTIONS();
return data;
}
}

ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */

add_to_hash:
HANDLE_BLOCK_INTERRUPTIONS();
idx = ht->nNumUsed++;
ht->nNumOfElements++;
if (ht->nInternalPointer == INVALID_IDX) {
Expand All @@ -520,7 +507,6 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s
nIndex = h & ht->nTableMask;
Z_NEXT(p->val) = ht->arHash[nIndex];
ht->arHash[nIndex] = idx;
HANDLE_UNBLOCK_INTERRUPTIONS();

return &p->val;
}
Expand Down Expand Up @@ -662,7 +648,6 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
}

add_to_packed:
HANDLE_BLOCK_INTERRUPTIONS();
/* incremental initialization of empty Buckets */
if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) == (HASH_ADD_NEW|HASH_ADD_NEXT)) {
ht->nNumUsed = h + 1;
Expand All @@ -688,8 +673,6 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
p->key = NULL;
ZVAL_COPY_VALUE(&p->val, pData);

HANDLE_UNBLOCK_INTERRUPTIONS();

return &p->val;

convert_to_hash:
Expand All @@ -701,12 +684,10 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
return NULL;
}
ZEND_ASSERT(&p->val != pData);
HANDLE_BLOCK_INTERRUPTIONS();
if (ht->pDestructor) {
ht->pDestructor(&p->val);
}
ZVAL_COPY_VALUE(&p->val, pData);
HANDLE_UNBLOCK_INTERRUPTIONS();
if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
}
Expand All @@ -717,7 +698,6 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */

add_to_hash:
HANDLE_BLOCK_INTERRUPTIONS();
idx = ht->nNumUsed++;
ht->nNumOfElements++;
if (ht->nInternalPointer == INVALID_IDX) {
Expand All @@ -734,7 +714,6 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
ZVAL_COPY_VALUE(&p->val, pData);
Z_NEXT(p->val) = ht->arHash[nIndex];
ht->arHash[nIndex] = idx;
HANDLE_UNBLOCK_INTERRUPTIONS();

return &p->val;
}
Expand Down Expand Up @@ -776,17 +755,13 @@ static void zend_hash_do_resize(HashTable *ht)
HT_ASSERT(GC_REFCOUNT(ht) == 1);

if (ht->nNumUsed > ht->nNumOfElements) {
HANDLE_BLOCK_INTERRUPTIONS();
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
} else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
HANDLE_BLOCK_INTERRUPTIONS();
ht->nTableSize += ht->nTableSize;
zend_hash_realloc(ht, ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)));
ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize);
ht->nTableMask = ht->nTableSize - 1;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
} else {
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket));
}
Expand Down Expand Up @@ -850,7 +825,6 @@ ZEND_API int zend_hash_rehash(HashTable *ht)

static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, Bucket *p, Bucket *prev)
{
HANDLE_BLOCK_INTERRUPTIONS();
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
if (prev) {
Z_NEXT(prev->val) = Z_NEXT(p->val);
Expand Down Expand Up @@ -892,7 +866,6 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx,
} else {
ZVAL_UNDEF(&p->val);
}
HANDLE_UNBLOCK_INTERRUPTIONS();
}

static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint32_t idx, Bucket *p)
Expand Down Expand Up @@ -2002,7 +1975,6 @@ ZEND_API int zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t c
(swap_func_t)(renumber? zend_hash_bucket_renum_swap :
((ht->u.flags & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));

HANDLE_BLOCK_INTERRUPTIONS();
ht->nNumUsed = i;
ht->nInternalPointer = 0;

Expand Down Expand Up @@ -2033,8 +2005,6 @@ ZEND_API int zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t c
}
}

HANDLE_UNBLOCK_INTERRUPTIONS();

return SUCCESS;
}

Expand Down
6 changes: 0 additions & 6 deletions Zend/zend_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,15 @@ static zend_string *zend_new_interned_string_int(zend_string *str)
uint32_t *h = (uint32_t *) perealloc_recoverable(CG(interned_strings).arHash, (CG(interned_strings).nTableSize << 1) * sizeof(uint32_t), 1);

if (d && h) {
HANDLE_BLOCK_INTERRUPTIONS();
CG(interned_strings).arData = d;
CG(interned_strings).arHash = h;
CG(interned_strings).nTableSize = (CG(interned_strings).nTableSize << 1);
CG(interned_strings).nTableMask = CG(interned_strings).nTableSize - 1;
zend_hash_rehash(&CG(interned_strings));
HANDLE_UNBLOCK_INTERRUPTIONS();
}
}
}

HANDLE_BLOCK_INTERRUPTIONS();

idx = CG(interned_strings).nNumUsed++;
CG(interned_strings).nNumOfElements++;
p = CG(interned_strings).arData + idx;
Expand All @@ -135,8 +131,6 @@ static zend_string *zend_new_interned_string_int(zend_string *str)
Z_NEXT(p->val) = CG(interned_strings).arHash[nIndex];
CG(interned_strings).arHash[nIndex] = idx;

HANDLE_UNBLOCK_INTERRUPTIONS();

return str;
#else
return str;
Expand Down
Loading