summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Weinand <bobwei9@hotmail.com>2015-06-20 18:28:51 +0200
committerBob Weinand <bobwei9@hotmail.com>2015-06-20 18:29:09 +0200
commit7adc0ae631213c7abc0e4102531b273866322ecf (patch)
treec62ba3e7af8de26e275a12138289d9525c5efad6
parent23e1e1295f1aadbbb52af8b30e64bd2f0f37b7fe (diff)
downloadphp-git-7adc0ae631213c7abc0e4102531b273866322ecf.tar.gz
Fix potential writes into wrong memory, ensure vm_stack integrity
Fixes also a segfault on stack frames > 1 << 18 bytes Stack frames, when reallocated, need to be marked as top frame of current stack page
-rw-r--r--Zend/tests/vm_stack_with_arg_extend.phpt17
-rw-r--r--Zend/zend_execute.c4
-rw-r--r--Zend/zend_execute.h13
3 files changed, 33 insertions, 1 deletions
diff --git a/Zend/tests/vm_stack_with_arg_extend.phpt b/Zend/tests/vm_stack_with_arg_extend.phpt
new file mode 100644
index 0000000000..6789830f5d
--- /dev/null
+++ b/Zend/tests/vm_stack_with_arg_extend.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Ensure valid vm_stack even when it needed to be copied to a new page
+--FILE--
+<?php
+
+function f(...$args) {
+ var_dump(count($args));
+}
+(function(){
+ $a = array_fill(0, 1024, true);
+ f(...$a);
+ yield;
+})()->valid();
+
+?>
+--EXPECT--
+1024
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index a62ff55c69..39a8fb242d 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -142,7 +142,7 @@ static const zend_internal_function zend_pass_function = {
((ZEND_VM_STACK_PAGE_SLOTS(gen) - ZEND_VM_STACK_HEADER_SLOTS) * sizeof(zval))
#define ZEND_VM_STACK_PAGE_ALIGNED_SIZE(gen, size) \
- (((size) + (ZEND_VM_STACK_FREE_PAGE_SIZE(gen) - 1)) & ~ZEND_VM_STACK_PAGE_SIZE(gen))
+ (((size) + (ZEND_VM_STACK_FREE_PAGE_SIZE(gen) - 1)) & ~(ZEND_VM_STACK_PAGE_SIZE(gen) - 1))
static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) {
zend_vm_stack page = (zend_vm_stack)emalloc(size);
@@ -2330,6 +2330,8 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
/* copy call frame into new stack segment */
new_call = zend_vm_stack_extend(used_stack * sizeof(zval));
*new_call = *call;
+ ZEND_SET_CALL_INFO(new_call, ZEND_CALL_INFO(new_call) | ZEND_CALL_ALLOCATED);
+
if (passed_args) {
zval *src = ZEND_CALL_ARG(call, 1);
zval *dst = ZEND_CALL_ARG(new_call, 1);
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 8430fc8499..3d470a9e81 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -148,6 +148,9 @@ struct _zend_vm_stack {
#define ZEND_VM_STACK_ELEMETS(stack) \
(((zval*)(stack)) + ZEND_VM_STACK_HEADER_SLOTS)
+#define ZEND_ASSERT_VM_STACK(stack) ZEND_ASSERT(stack->top > (zval *) stack && stack->end > (zval *) stack && stack->top <= stack->end)
+#define ZEND_ASSERT_VM_STACK_GLOBAL ZEND_ASSERT(EG(vm_stack_top) > (zval *) EG(vm_stack) && EG(vm_stack_end) > (zval *) EG(vm_stack) && EG(vm_stack_top) <= EG(vm_stack_end))
+
ZEND_API void zend_vm_stack_init(void);
ZEND_API void zend_vm_stack_destroy(void);
ZEND_API void* zend_vm_stack_extend(size_t size);
@@ -156,6 +159,8 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
{
zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);
+ ZEND_ASSERT_VM_STACK_GLOBAL;
+
if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
ZEND_SET_CALL_INFO(call, call_info | ZEND_CALL_ALLOCATED);
@@ -163,6 +168,9 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
EG(vm_stack_top) = (zval*)((char*)call + used_stack);
ZEND_SET_CALL_INFO(call, call_info);
}
+
+ ZEND_ASSERT_VM_STACK_GLOBAL;
+
call->func = func;
Z_OBJ(call->This) = object;
ZEND_CALL_NUM_ARGS(call) = num_args;
@@ -236,6 +244,8 @@ static zend_always_inline void zend_vm_stack_free_args(zend_execute_data *call)
static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_info, zend_execute_data *call)
{
+ ZEND_ASSERT_VM_STACK_GLOBAL;
+
if (UNEXPECTED(call_info & ZEND_CALL_ALLOCATED)) {
zend_vm_stack p = EG(vm_stack);
@@ -245,9 +255,12 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in
EG(vm_stack_end) = prev->end;
EG(vm_stack) = prev;
efree(p);
+
} else {
EG(vm_stack_top) = (zval*)call;
}
+
+ ZEND_ASSERT_VM_STACK_GLOBAL;
}
static zend_always_inline void zend_vm_stack_free_call_frame(zend_execute_data *call)