Skip to content

Fix GH-10737: PHP 8.1.16 segfaults on line 597 of sapi/apache2handler/sapi_apache2.c #10863

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 4 commits 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
114 changes: 70 additions & 44 deletions TSRM/TSRM.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu
return 1;
}/*}}}*/

static void ts_free_resources(tsrm_tls_entry *thread_resources)
{
/* Need to destroy in reverse order to respect dependencies. */
for (int i = thread_resources->count - 1; i >= 0; i--) {
if (!resource_types_table[i].done) {
if (resource_types_table[i].dtor) {
resource_types_table[i].dtor(thread_resources->storage[i]);
}

if (!resource_types_table[i].fast_offset) {
free(thread_resources->storage[i]);
}
}
}

free(thread_resources->storage);
}

/* Shutdown TSRM (call once for the entire process) */
TSRM_API void tsrm_shutdown(void)
Expand All @@ -183,25 +200,13 @@ TSRM_API void tsrm_shutdown(void)
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;

while (p) {
int j;

next_p = p->next;
for (j=0; j<p->count; j++) {
if (p->storage[j]) {
if (resource_types_table) {
if (!resource_types_table[j].done) {
if (resource_types_table[j].dtor) {
resource_types_table[j].dtor(p->storage[j]);
}

if (!resource_types_table[j].fast_offset) {
free(p->storage[j]);
}
}
}
}
if (resource_types_table) {
/* This call will already free p->storage for us */
ts_free_resources(p);
} else {
free(p->storage);
}
free(p->storage);
free(p);
p = next_p;
}
Expand Down Expand Up @@ -367,7 +372,13 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
return *rsrc_id;
}/*}}}*/

static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource)
{
tsrm_tls_set(thread_resource);
TSRMLS_CACHE = thread_resource;
}

/* Must be called with tsmm_mutex held */
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{/*{{{*/
int i;
Expand All @@ -383,8 +394,7 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
(*thread_resources_ptr)->next = NULL;

/* Set thread local storage to this new thread resources structure */
tsrm_tls_set(*thread_resources_ptr);
TSRMLS_CACHE = *thread_resources_ptr;
set_thread_local_storage_resource_to(*thread_resources_ptr);

if (tsrm_new_thread_begin_handler) {
tsrm_new_thread_begin_handler(thread_id);
Expand All @@ -407,17 +417,14 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
if (tsrm_new_thread_end_handler) {
tsrm_new_thread_end_handler(thread_id);
}

tsrm_mutex_unlock(tsmm_mutex);
}/*}}}*/


/* fetches the requested resource for the current thread */
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
{/*{{{*/
THREAD_T thread_id;
int hash_value;
tsrm_tls_entry *thread_resources;
tsrm_tls_entry *thread_resources, **last_thread_resources;

if (!th_id) {
/* Fast path for looking up the resources for the current
Expand Down Expand Up @@ -448,25 +455,55 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)

if (!thread_resources) {
allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
tsrm_mutex_unlock(tsmm_mutex);
return ts_resource_ex(id, &thread_id);
} else {
do {
if (thread_resources->thread_id == thread_id) {
break;
}
last_thread_resources = &tsrm_tls_table[hash_value];
while (thread_resources->thread_id != thread_id) {
last_thread_resources = &thread_resources->next;
if (thread_resources->next) {
thread_resources = thread_resources->next;
} else {
allocate_new_resource(&thread_resources->next, thread_id);
tsrm_mutex_unlock(tsmm_mutex);
return ts_resource_ex(id, &thread_id);
/*
* thread_resources = thread_resources->next;
* break;
*/
}
} while (thread_resources);
}
}

/* It's possible that the current thread resources are requested, and that we get here.
* This means that the TSRM key pointer and cached pointer are NULL, but there is still
* a thread resource associated with this ID in the hashtable. This can occur if a thread
* goes away, but its resources are never cleaned up, and then that thread ID is reused.
* Since we don't always have a way to know when a thread goes away, we can't clean up
* the thread's resources before the new thread spawns.
* To solve this issue, we'll free up the old thread resources gracefully (gracefully
* because there might still be resources open like database connection which need to
* be shut down cleanly). After freeing up, we'll create the new resources for this thread
* as if the stale resources never existed in the first place. From that point forward,
* it is as if that situation never occurred.
* The fact that this situation happens isn't that bad because a child process containing
* threads will eventually be respawned anyway by the SAPI, so the stale threads won't last
* forever. */
TSRM_ASSERT(thread_resources->thread_id == thread_id);
if (thread_id == tsrm_thread_id() && !tsrm_tls_get()) {
tsrm_tls_entry *next = thread_resources->next;
/* In case that extensions don't use the pointer passed from the dtor, but incorrectly
* use the global pointer, we need to setup the global pointer temporarily here. */
set_thread_local_storage_resource_to(thread_resources);
/* Free up the old resource from the old thread instance */
ts_free_resources(thread_resources);
free(thread_resources);
/* Allocate a new resource at the same point in the linked list, and relink the next pointer */
allocate_new_resource(last_thread_resources, thread_id);
thread_resources = *last_thread_resources;
thread_resources->next = next;
/* We don't have to tail-call ts_resource_ex, we can take the fast path to the return
* because we already have the correct pointer. */
}

tsrm_mutex_unlock(tsmm_mutex);

/* Read a specific resource from the thread's resources.
* This is called outside of a mutex, so have to be aware about external
* changes to the structure as we read it.
Expand All @@ -479,7 +516,6 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
void ts_free_thread(void)
{/*{{{*/
tsrm_tls_entry *thread_resources;
int i;
THREAD_T thread_id = tsrm_thread_id();
int hash_value;
tsrm_tls_entry *last=NULL;
Expand All @@ -492,17 +528,7 @@ void ts_free_thread(void)

while (thread_resources) {
if (thread_resources->thread_id == thread_id) {
for (i=0; i<thread_resources->count; i++) {
if (resource_types_table[i].dtor) {
resource_types_table[i].dtor(thread_resources->storage[i]);
}
}
for (i=0; i<thread_resources->count; i++) {
if (!resource_types_table[i].fast_offset) {
free(thread_resources->storage[i]);
}
}
free(thread_resources->storage);
ts_free_resources(thread_resources);
if (last) {
last->next = thread_resources->next;
} else {
Expand Down
2 changes: 1 addition & 1 deletion main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1936,7 +1936,7 @@ static void core_globals_dtor(php_core_globals *core_globals)
free(core_globals->php_binary);
}

php_shutdown_ticks();
php_shutdown_ticks(core_globals);
}
/* }}} */

Expand Down
4 changes: 2 additions & 2 deletions main/php_ticks.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ void php_deactivate_ticks(void)
zend_llist_clean(&PG(tick_functions));
}

void php_shutdown_ticks(void)
void php_shutdown_ticks(php_core_globals *core_globals)
{
zend_llist_destroy(&PG(tick_functions));
zend_llist_destroy(&core_globals->tick_functions);
}

static int php_compare_tick_functions(void *elem1, void *elem2)
Expand Down
2 changes: 1 addition & 1 deletion main/php_ticks.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

int php_startup_ticks(void);
void php_deactivate_ticks(void);
void php_shutdown_ticks(void);
void php_shutdown_ticks(php_core_globals *core_globals);
void php_run_ticks(int count);

BEGIN_EXTERN_C()
Expand Down