Skip to content

Commit 7df3fd9

Browse files
committed
Change stack field to a pointer in fiber context
The variable size of zend_fiber_stack results in the offset of other fields to be variable, which causes compatiblity issues with extensions when php-src is compiled with ASan enabled. This solution was prefered over moving the stack field to be the last member, as inclusion of ZEND_FIBER_CONTEXT_FIELDS into other structs may still result in field offset errors. The definition of zend_fiber_stack was removed from the header to hide it from the ABI. Renamed prior_pointer and prior_size to asan_pointer and asan_size to reflect their current use. Changed context flags type to uint8_t. Renamed valgrind stack id field to valgrind_stack_id and fixed the type to unsigned int.
1 parent d0c43e1 commit 7df3fd9

File tree

2 files changed

+55
-45
lines changed

2 files changed

+55
-45
lines changed

Zend/zend_fibers.c

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@
5959
# include <sanitizer/common_interface_defs.h>
6060
#endif
6161

62+
/* Encapsulates the fiber C stack with extension for debugging tools. */
63+
struct _zend_fiber_stack {
64+
void *pointer;
65+
size_t size;
66+
67+
#ifdef HAVE_VALGRIND
68+
unsigned int valgrind_stack_id;
69+
#endif
70+
71+
#ifdef __SANITIZE_ADDRESS__
72+
const void *asan_pointer;
73+
size_t asan_size;
74+
#endif
75+
};
76+
6277
/* boost_context_data is our customized definition of struct transfer_t as
6378
* provided by boost.context in fcontext.hpp:
6479
*
@@ -102,25 +117,25 @@ static size_t zend_fiber_get_page_size(void)
102117
return page_size;
103118
}
104119

105-
static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
120+
static zend_fiber_stack *zend_fiber_stack_allocate(size_t size)
106121
{
107122
void *pointer;
108123
const size_t page_size = zend_fiber_get_page_size();
109124

110125
ZEND_ASSERT(size >= page_size + ZEND_FIBER_GUARD_PAGES * page_size);
111126

112-
stack->size = (size + page_size - 1) / page_size * page_size;
113-
const size_t msize = stack->size + ZEND_FIBER_GUARD_PAGES * page_size;
127+
const size_t stack_size = (size + page_size - 1) / page_size * page_size;
128+
const size_t alloc_size = stack_size + ZEND_FIBER_GUARD_PAGES * page_size;
114129

115130
#ifdef ZEND_WIN32
116-
pointer = VirtualAlloc(0, msize, MEM_COMMIT, PAGE_READWRITE);
131+
pointer = VirtualAlloc(0, alloc_size, MEM_COMMIT, PAGE_READWRITE);
117132

118133
if (!pointer) {
119134
DWORD err = GetLastError();
120135
char *errmsg = php_win32_error_to_msg(err);
121136
zend_throw_exception_ex(NULL, 0, "Fiber make context failed: VirtualAlloc failed: [0x%08lx] %s", err, errmsg[0] ? errmsg : "Unknown");
122137
php_win32_error_msg_free(errmsg);
123-
return false;
138+
return NULL;
124139
}
125140

126141
# if ZEND_FIBER_GUARD_PAGES
@@ -132,49 +147,48 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
132147
zend_throw_exception_ex(NULL, 0, "Fiber protect stack failed: VirtualProtect failed: [0x%08lx] %s", err, errmsg[0] ? errmsg : "Unknown");
133148
php_win32_error_msg_free(errmsg);
134149
VirtualFree(pointer, 0, MEM_RELEASE);
135-
return false;
150+
return NULL;
136151
}
137152
# endif
138153
#else
139-
pointer = mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
154+
pointer = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
140155

141156
if (pointer == MAP_FAILED) {
142157
zend_throw_exception_ex(NULL, 0, "Fiber make context failed: mmap failed: %s (%d)", strerror(errno), errno);
143-
return false;
158+
return NULL;
144159
}
145160

146161
# if ZEND_FIBER_GUARD_PAGES
147162
if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PROT_NONE) < 0) {
148163
zend_throw_exception_ex(NULL, 0, "Fiber protect stack failed: mmap failed: %s (%d)", strerror(errno), errno);
149-
munmap(pointer, msize);
150-
return false;
164+
munmap(pointer, alloc_size);
165+
return NULL;
151166
}
152167
# endif
153168
#endif
154169

170+
zend_fiber_stack *stack = emalloc(sizeof(zend_fiber_stack));
171+
155172
stack->pointer = (void *) ((uintptr_t) pointer + ZEND_FIBER_GUARD_PAGES * page_size);
173+
stack->size = stack_size;
156174

157175
#ifdef VALGRIND_STACK_REGISTER
158176
uintptr_t base = (uintptr_t) stack->pointer;
159-
stack->valgrind = VALGRIND_STACK_REGISTER(base, base + stack->size);
177+
stack->valgrind_stack_id = VALGRIND_STACK_REGISTER(base, base + stack->size);
160178
#endif
161179

162180
#ifdef __SANITIZE_ADDRESS__
163-
stack->prior_pointer = stack->pointer;
164-
stack->prior_size = stack->size;
181+
stack->asan_pointer = stack->pointer;
182+
stack->asan_size = stack->size;
165183
#endif
166184

167-
return true;
185+
return stack;
168186
}
169187

170188
static void zend_fiber_stack_free(zend_fiber_stack *stack)
171189
{
172-
if (!stack->pointer) {
173-
return;
174-
}
175-
176190
#ifdef VALGRIND_STACK_DEREGISTER
177-
VALGRIND_STACK_DEREGISTER(stack->valgrind);
191+
VALGRIND_STACK_DEREGISTER(stack->valgrind_stack_id);
178192
#endif
179193

180194
const size_t page_size = zend_fiber_get_page_size();
@@ -187,15 +201,15 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
187201
munmap(pointer, stack->size + ZEND_FIBER_GUARD_PAGES * page_size);
188202
#endif
189203

190-
stack->pointer = NULL;
204+
efree(stack);
191205
}
192206

193207
static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
194208
{
195209
zend_fiber_context *from = data.transfer->context;
196210

197211
#ifdef __SANITIZE_ADDRESS__
198-
__sanitizer_finish_switch_fiber(NULL, &from->stack.prior_pointer, &from->stack.prior_size);
212+
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
199213
#endif
200214

201215
/* Get a hold of the context that resumed us and update it's handle to allow for symmetric coroutines. */
@@ -218,14 +232,16 @@ static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
218232

219233
ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size)
220234
{
221-
if (UNEXPECTED(!zend_fiber_stack_allocate(&context->stack, stack_size))) {
235+
context->stack = zend_fiber_stack_allocate(stack_size);
236+
237+
if (UNEXPECTED(!context->stack)) {
222238
return false;
223239
}
224240

225241
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
226-
void *stack = (void *) ((uintptr_t) context->stack.pointer + context->stack.size);
242+
void *stack = (void *) ((uintptr_t) context->stack->pointer + context->stack->size);
227243

228-
context->handle = make_fcontext(stack, context->stack.size, zend_fiber_trampoline);
244+
context->handle = make_fcontext(stack, context->stack->size, zend_fiber_trampoline);
229245
ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL");
230246

231247
context->kind = kind;
@@ -236,7 +252,7 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
236252

237253
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
238254
{
239-
zend_fiber_stack_free(&context->stack);
255+
zend_fiber_stack_free(context->stack);
240256
}
241257

242258
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
@@ -278,8 +294,8 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
278294
void *fake_stack = NULL;
279295
__sanitizer_start_switch_fiber(
280296
from->status != ZEND_FIBER_STATUS_DEAD ? &fake_stack : NULL,
281-
to->stack.prior_pointer,
282-
to->stack.prior_size);
297+
to->stack->asan_pointer,
298+
to->stack->asan_size);
283299
#endif
284300

285301
boost_context_data data = jump_fcontext(to->handle, transfer);
@@ -292,7 +308,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
292308
to->handle = data.handle;
293309

294310
#ifdef __SANITIZE_ADDRESS__
295-
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
311+
__sanitizer_finish_switch_fiber(fake_stack, &to->stack->asan_pointer, &to->stack->asan_size);
296312
#endif
297313

298314
EG(current_fiber_context) = from;
@@ -741,6 +757,11 @@ void zend_fiber_init(void)
741757
{
742758
zend_fiber_context *context = ecalloc(1, sizeof(zend_fiber_context));
743759

760+
#ifdef __SANITIZE_ADDRESS__
761+
// Main fiber context stack is only accessed if ASan is enabled.
762+
context->stack = emalloc(sizeof(zend_fiber_stack));
763+
#endif
764+
744765
context->status = ZEND_FIBER_STATUS_RUNNING;
745766

746767
EG(main_fiber_context) = context;
@@ -750,5 +771,8 @@ void zend_fiber_init(void)
750771

751772
void zend_fiber_shutdown(void)
752773
{
774+
#ifdef __SANITIZE_ADDRESS__
775+
efree(EG(main_fiber_context)->stack);
776+
#endif
753777
efree(EG(main_fiber_context));
754778
}

Zend/zend_fibers.h

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,9 @@ void zend_fiber_shutdown(void);
5353

5454
extern ZEND_API zend_class_entry *zend_ce_fiber;
5555

56-
/* Encapsulates the fiber C stack with extension for debugging tools. */
57-
typedef struct _zend_fiber_stack {
58-
void *pointer;
59-
size_t size;
60-
61-
#ifdef HAVE_VALGRIND
62-
int valgrind;
63-
#endif
64-
65-
#ifdef __SANITIZE_ADDRESS__
66-
const void *prior_pointer;
67-
size_t prior_size;
68-
#endif
69-
} zend_fiber_stack;
70-
7156
typedef struct _zend_fiber zend_fiber;
7257
typedef struct _zend_fiber_context zend_fiber_context;
58+
typedef struct _zend_fiber_stack zend_fiber_stack;
7359

7460
/* Encapsulates data needed for a context switch. */
7561
typedef struct _zend_fiber_transfer {
@@ -92,9 +78,9 @@ typedef void (*zend_fiber_coroutine)(zend_fiber_transfer *transfer);
9278
/* Pointer that identifies the fiber type. */ \
9379
void *kind; \
9480
zend_fiber_coroutine function; \
95-
zend_fiber_stack stack; \
81+
zend_fiber_stack *stack; \
9682
zend_fiber_status status; \
97-
zend_uchar flags
83+
uint8_t flags
9884

9985
/* Standalone context (used primarily as pointer type). */
10086
struct _zend_fiber_context {

0 commit comments

Comments
 (0)