Skip to content

Commit 7c947b5

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Partially backport GH-13782 to stable branches
2 parents ba7b305 + c815cdc commit 7c947b5

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ PHP NEWS
1616
- DOM:
1717
. Fixed bug GH-14343 (Memory leak in xml and dom). (nielsdos)
1818

19+
- MySQLnd:
20+
. Partially fix bug GH-10599 (Apache crash on Windows when using a
21+
self-referencing anonymous function inside a class with an active
22+
mysqli connection). (nielsdos)
23+
1924
- Opcache:
2025
. Fixed bug GH-14267 (opcache.jit=off does not allow enabling JIT at runtime).
2126
(ilutov)

ext/mysqlnd/mysqlnd_vio.c

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,27 @@ MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_u
112112
}
113113
/* }}} */
114114

115+
static void mysqlnd_fixup_regular_list(php_stream *net_stream)
116+
{
117+
/*
118+
Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
119+
be registered as resource (in EG(regular_list). So far, so good. However, it won't be
120+
unregistered until the script ends. So, we need to take care of that.
121+
*/
122+
dtor_func_t origin_dtor = EG(regular_list).pDestructor;
123+
EG(regular_list).pDestructor = NULL;
124+
zend_hash_index_del(&EG(regular_list), net_stream->res->handle);
125+
EG(regular_list).pDestructor = origin_dtor;
126+
efree(net_stream->res);
127+
net_stream->res = NULL;
128+
}
115129

116130
/* {{{ mysqlnd_vio::open_pipe */
117131
static php_stream *
118132
MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const bool persistent,
119133
MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
120134
{
121135
unsigned int streams_options = 0;
122-
dtor_func_t origin_dtor;
123136
php_stream * net_stream = NULL;
124137

125138
DBG_ENTER("mysqlnd_vio::open_pipe");
@@ -132,16 +145,34 @@ MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CS
132145
SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown error while connecting");
133146
DBG_RETURN(NULL);
134147
}
135-
/*
136-
Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
137-
be registered as resource (in EG(regular_list). So far, so good. However, it won't be
138-
unregistered until the script ends. So, we need to take care of that.
139-
*/
140-
origin_dtor = EG(regular_list).pDestructor;
141-
EG(regular_list).pDestructor = NULL;
142-
zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
143-
EG(regular_list).pDestructor = origin_dtor;
144-
net_stream->res = NULL;
148+
149+
if (persistent) {
150+
/* This is a similar hack as for mysqlnd_vio::open_tcp_or_unix.
151+
* The main difference here is that we have no access to the hashed key.
152+
* We can however perform a loop over the persistent resource list to find
153+
* which one corresponds to our newly allocated stream.
154+
* This loop is pretty cheap because it will normally either be the last entry or second to last entry
155+
* in the list, depending on whether the socket connection itself is persistent or not.
156+
* That's why we use a reverse loop. */
157+
Bucket *bucket;
158+
/* Use a bucket loop to make deletion cheap. */
159+
ZEND_HASH_MAP_REVERSE_FOREACH_BUCKET(&EG(persistent_list), bucket) {
160+
zend_resource *current_res = Z_RES(bucket->val);
161+
if (current_res->ptr == net_stream) {
162+
dtor_func_t origin_dtor = EG(persistent_list).pDestructor;
163+
EG(persistent_list).pDestructor = NULL;
164+
zend_hash_del_bucket(&EG(persistent_list), bucket);
165+
EG(persistent_list).pDestructor = origin_dtor;
166+
pefree(current_res, 1);
167+
break;
168+
}
169+
} ZEND_HASH_FOREACH_END();
170+
#if ZEND_DEBUG
171+
php_stream_auto_cleanup(net_stream);
172+
#endif
173+
}
174+
175+
mysqlnd_fixup_regular_list(net_stream);
145176

146177
DBG_RETURN(net_stream);
147178
}
@@ -205,6 +236,7 @@ MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYS
205236
zend_resource *le;
206237

207238
if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
239+
ZEND_ASSERT(le->ptr == net_stream);
208240
origin_dtor = EG(persistent_list).pDestructor;
209241
/*
210242
in_free will let streams code skip destructing - big HACK,
@@ -218,22 +250,12 @@ MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYS
218250
}
219251
#if ZEND_DEBUG
220252
/* Shut-up the streams, they don't know what they are doing */
221-
net_stream->__exposed = 1;
253+
php_stream_auto_cleanup(net_stream);
222254
#endif
223255
mnd_sprintf_free(hashed_details);
224256
}
225257

226-
/*
227-
Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
228-
be registered as resource (in EG(regular_list). So far, so good. However, it won't be
229-
unregistered until the script ends. So, we need to take care of that.
230-
*/
231-
origin_dtor = EG(regular_list).pDestructor;
232-
EG(regular_list).pDestructor = NULL;
233-
zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
234-
efree(net_stream->res);
235-
net_stream->res = NULL;
236-
EG(regular_list).pDestructor = origin_dtor;
258+
mysqlnd_fixup_regular_list(net_stream);
237259
DBG_RETURN(net_stream);
238260
}
239261
/* }}} */

0 commit comments

Comments
 (0)