Skip to content

Commit dfe0ea5

Browse files
committed
stream: fix possible use of wrapper after it is freed
1 parent bdf9600 commit dfe0ea5

File tree

3 files changed

+108
-72
lines changed

3 files changed

+108
-72
lines changed

main/streams/php_stream_errors.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ BEGIN_EXTERN_C()
124124
#define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 161
125125
#define STREAM_ERROR_CODE_USERSPACE_CALL_FAILED 162
126126

127+
/* Wrapper name for PHP errors */
128+
#define PHP_STREAM_ERROR_WRAPPER_NAME(_wrapper) (_wrapper ? _wrapper->wops->label : "unknown")
129+
127130
/* Stored error entry */
128131
typedef struct {
129132
zend_string *message;
@@ -166,9 +169,14 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper
166169
php_stream_context *context, int options, int severity, bool terminal, int code,
167170
const char *param, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9);
168171

172+
PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name,
173+
php_stream_context *context, int code, const char *path, const char *caption);
174+
169175
PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper,
170176
php_stream_context *context, int code, const char *path, const char *caption);
171177

178+
PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name);
179+
172180
PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper);
173181

174182
/* Convenience macros */

main/streams/stream_errors.c

Lines changed: 93 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static void php_stream_error_entry_dtor(void *error)
3131
zend_string_release(entry->message);
3232
pefree(entry->wrapper_name, entry->persistent);
3333
pefree(entry->docref, entry->persistent);
34-
// param is not currently supported for streams so cannot be persistent
34+
// param is not currently supported for streams so cannot be persistent
3535
ZEND_ASSERT(!entry->persistent || entry->param == NULL);
3636
efree(entry->param);
3737
pefree(entry, entry->persistent);
@@ -216,8 +216,8 @@ static void php_stream_process_error(php_stream_context *context, const char *wr
216216

217217
/* Helper to create error entry */
218218
static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code,
219-
const char *wrapper_name, const char *docref, char *param, int severity,
220-
bool terminal, bool persistent)
219+
const char *wrapper_name, const char *docref, char *param, int severity, bool terminal,
220+
bool persistent)
221221
{
222222
if (persistent) {
223223
message = zend_string_dup(message, true);
@@ -240,8 +240,8 @@ static php_stream_error_entry *php_stream_create_error_entry(zend_string *messag
240240

241241
/* Common storage function*/
242242
static void php_stream_store_error_common(php_stream_context *context, php_stream *stream,
243-
zend_string *message, const char *docref, int code,
244-
const char *wrapper_name, char *param, int severity, bool terminal)
243+
zend_string *message, const char *docref, int code, const char *wrapper_name, char *param,
244+
int severity, bool terminal)
245245
{
246246
int error_mode = php_stream_get_error_mode(context);
247247
int store_mode = php_stream_get_error_store_mode(context, error_mode);
@@ -295,23 +295,36 @@ static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name
295295
{
296296
zend_string *message = vstrpprintf(0, fmt, args);
297297

298-
php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message),
299-
param, severity, terminal);
298+
php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), param,
299+
severity, terminal);
300300

301301
php_stream_store_error_common(
302302
context, NULL, message, docref, code, wrapper_name, param, severity, terminal);
303303

304304
zend_string_release(message);
305305
}
306306

307+
static void php_stream_wrapper_error_internal_with_name_variadic(const char *wrapper_name,
308+
php_stream_context *context, const char *docref, int options, int severity, bool terminal,
309+
int code, char *param, const char *fmt, ...)
310+
{
311+
va_list args;
312+
va_start(args, fmt);
313+
314+
php_stream_wrapper_error_internal_with_name(
315+
wrapper_name, context, docref, options, severity, terminal, code, param, fmt, args);
316+
317+
va_end(args);
318+
}
319+
307320
static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper,
308321
php_stream_context *context, const char *docref, int options, int severity, bool terminal,
309322
int code, char *param, const char *fmt, va_list args)
310323
{
311-
const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown";
324+
const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper);
312325

313-
php_stream_wrapper_error_internal_with_name(wrapper_name, context, docref, options, severity,
314-
terminal, code, param, fmt, args);
326+
php_stream_wrapper_error_internal_with_name(
327+
wrapper_name, context, docref, options, severity, terminal, code, param, fmt, args);
315328
}
316329

317330
PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name,
@@ -347,7 +360,7 @@ PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stre
347360
if (options & REPORT_ERRORS) {
348361
va_list args;
349362
va_start(args, fmt);
350-
char *param_copy = param ? estrdup(param): NULL;
363+
char *param_copy = param ? estrdup(param) : NULL;
351364
php_stream_wrapper_error_internal(
352365
wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args);
353366
va_end(args);
@@ -364,8 +377,8 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper,
364377

365378
va_list args;
366379
va_start(args, fmt);
367-
php_stream_wrapper_error_internal(
368-
wrapper, context, docref, options, severity, terminal, code, combined_param, fmt, args);
380+
php_stream_wrapper_error_internal(wrapper, context, docref, options, severity, terminal,
381+
code, combined_param, fmt, args);
369382
va_end(args);
370383
}
371384
}
@@ -375,7 +388,7 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper,
375388
static void php_stream_wrapper_log_store_error(zend_string *message, int code,
376389
const char *wrapper_name, const char *param, int severity, bool terminal)
377390
{
378-
char *param_copy = param ? estrdup(param): NULL;
391+
char *param_copy = param ? estrdup(param) : NULL;
379392
php_stream_error_entry *entry = php_stream_create_error_entry(
380393
message, code, wrapper_name, NULL, param_copy, severity, terminal, false);
381394

@@ -384,8 +397,8 @@ static void php_stream_wrapper_log_store_error(zend_string *message, int code,
384397
zend_hash_init(FG(wrapper_logged_errors), 8, NULL, php_stream_error_list_dtor, 0);
385398
}
386399

387-
zend_llist *list = zend_hash_str_find_ptr(
388-
FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name));
400+
zend_llist *list
401+
= zend_hash_str_find_ptr(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name));
389402

390403
if (!list) {
391404
zend_llist new_list;
@@ -403,16 +416,15 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap
403416
char *param, const char *fmt, va_list args)
404417
{
405418
zend_string *message = vstrpprintf(0, fmt, args);
406-
const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown";
419+
const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper);
407420

408421
if (options & REPORT_ERRORS) {
409422
/* Report immediately using standard error functions */
410423
php_stream_wrapper_error_internal_with_name(
411424
wrapper_name, context, NULL, options, severity, terminal, code, param, fmt, args);
412425
} else {
413426
/* Store for later display in FG(wrapper_logged_errors) */
414-
php_stream_wrapper_log_store_error(
415-
message, code, wrapper_name, param, severity, terminal);
427+
php_stream_wrapper_log_store_error(message, code, wrapper_name, param, severity, terminal);
416428
}
417429
zend_string_release(message);
418430
}
@@ -434,7 +446,7 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper
434446
{
435447
va_list args;
436448
va_start(args, fmt);
437-
char *param_copy = param ? estrdup(param): NULL;
449+
char *param_copy = param ? estrdup(param) : NULL;
438450
php_stream_wrapper_log_error_internal(
439451
wrapper, context, options, severity, terminal, code, param_copy, fmt, args);
440452
va_end(args);
@@ -450,7 +462,7 @@ static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name)
450462
}
451463
}
452464

453-
void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper,
465+
PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name,
454466
php_stream_context *context, int code, const char *path, const char *caption)
455467
{
456468
char *msg;
@@ -462,73 +474,84 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper,
462474
return;
463475
}
464476

465-
const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown";
466477
char *tmp = estrdup(path);
467-
if (wrapper) {
468-
zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name);
469-
if (err_list) {
470-
size_t l = 0;
471-
int brlen;
472-
int i;
473-
int count = (int) zend_llist_count(err_list);
474-
const char *br;
475-
php_stream_error_entry **err_entry_p;
476-
zend_llist_position pos;
477-
478-
if (PG(html_errors)) {
479-
brlen = 7;
480-
br = "<br />\n";
481-
} else {
482-
brlen = 1;
483-
br = "\n";
484-
}
478+
zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name);
479+
if (err_list) {
480+
size_t l = 0;
481+
int brlen;
482+
int i;
483+
int count = (int) zend_llist_count(err_list);
484+
const char *br;
485+
php_stream_error_entry **err_entry_p;
486+
zend_llist_position pos;
487+
488+
if (PG(html_errors)) {
489+
brlen = 7;
490+
br = "<br />\n";
491+
} else {
492+
brlen = 1;
493+
br = "\n";
494+
}
485495

486-
for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p;
487-
err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) {
488-
l += ZSTR_LEN((*err_entry_p)->message);
489-
if (i < count - 1) {
490-
l += brlen;
491-
}
496+
for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p;
497+
err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) {
498+
l += ZSTR_LEN((*err_entry_p)->message);
499+
if (i < count - 1) {
500+
l += brlen;
492501
}
493-
msg = emalloc(l + 1);
494-
msg[0] = '\0';
495-
for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p;
496-
err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) {
497-
strcat(msg, ZSTR_VAL((*err_entry_p)->message));
498-
if (i < count - 1) {
499-
strcat(msg, br);
500-
}
502+
}
503+
msg = emalloc(l + 1);
504+
msg[0] = '\0';
505+
for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p;
506+
err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) {
507+
strcat(msg, ZSTR_VAL((*err_entry_p)->message));
508+
if (i < count - 1) {
509+
strcat(msg, br);
501510
}
511+
}
502512

503-
free_msg = 1;
513+
free_msg = 1;
514+
} else {
515+
if (!strcmp(wrapper_name, php_plain_files_wrapper.wops->label)) {
516+
msg = php_socket_strerror_s(errno, errstr, sizeof(errstr));
504517
} else {
505-
if (wrapper == &php_plain_files_wrapper) {
506-
msg = php_socket_strerror_s(errno, errstr, sizeof(errstr));
507-
} else {
508-
msg = "operation failed";
509-
}
518+
msg = "operation failed";
510519
}
511-
} else {
512-
msg = "no suitable wrapper could be found";
513520
}
514521

515522
php_strip_url_passwd(tmp);
516-
php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, code, tmp,
517-
"%s: %s", caption, msg);
518-
efree(tmp);
523+
php_stream_wrapper_error_internal_with_name_variadic(wrapper_name, context, NULL, REPORT_ERRORS,
524+
E_WARNING, true, code, tmp, "%s: %s", caption, msg);
525+
519526
if (free_msg) {
520527
efree(msg);
521528
}
522529
}
523530

524-
void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper)
531+
PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper,
532+
php_stream_context *context, int code, const char *path, const char *caption)
525533
{
526-
if (wrapper && FG(wrapper_logged_errors)) {
527-
const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown";
534+
if (wrapper) {
535+
const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper);
536+
php_stream_display_wrapper_errors(wrapper_name, context, code, path, caption);
537+
}
538+
}
539+
540+
PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name)
541+
{
542+
if (FG(wrapper_logged_errors)) {
528543
zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name));
529544
}
530545
}
531546

547+
PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper)
548+
{
549+
if (wrapper) {
550+
const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper);
551+
php_stream_tidy_wrapper_name_error_log(wrapper_name);
552+
}
553+
}
554+
532555
/* Stream error reporting */
533556

534557
PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, bool terminal,
@@ -549,8 +572,8 @@ PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severit
549572
severity, terminal);
550573

551574
/* Store error */
552-
php_stream_store_error_common(context, stream, message, docref, code, wrapper_name, NULL,
553-
severity, terminal);
575+
php_stream_store_error_common(
576+
context, stream, message, docref, code, wrapper_name, NULL, severity, terminal);
554577

555578
zend_string_release(message);
556579
}

main/streams/streams.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod
21512151
return NULL;
21522152
}
21532153

2154+
/* wrapper name needs to be stored as wrapper can be removed in opener (user stream) */
2155+
char *wrapper_name = pestrdup(PHP_STREAM_ERROR_WRAPPER_NAME(wrapper), persistent);
21542156
if (wrapper) {
21552157
if (!wrapper->wops->stream_opener) {
21562158
php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS,
@@ -2205,6 +2207,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod
22052207
if (resolved_path) {
22062208
zend_string_release_ex(resolved_path, 0);
22072209
}
2210+
pefree(wrapper_name, persistent);
22082211
return stream;
22092212
case PHP_STREAM_RELEASED:
22102213
if (newstream->orig_path) {
@@ -2214,6 +2217,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod
22142217
if (resolved_path) {
22152218
zend_string_release_ex(resolved_path, 0);
22162219
}
2220+
pefree(wrapper_name, persistent);
22172221
return newstream;
22182222
default:
22192223
php_stream_close(stream);
@@ -2241,14 +2245,15 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod
22412245
}
22422246

22432247
if (stream == NULL && (options & REPORT_ERRORS)) {
2244-
php_stream_display_wrapper_errors(wrapper, context, STREAM_ERROR_CODE_OPEN_FAILED, path,
2248+
php_stream_display_wrapper_name_errors(wrapper_name, context, STREAM_ERROR_CODE_OPEN_FAILED, path,
22452249
"Failed to open stream");
22462250
if (opened_path && *opened_path) {
22472251
zend_string_release_ex(*opened_path, 0);
22482252
*opened_path = NULL;
22492253
}
22502254
}
2251-
php_stream_tidy_wrapper_error_log(wrapper);
2255+
php_stream_tidy_wrapper_name_error_log(wrapper_name);
2256+
pefree(wrapper_name, persistent);
22522257
if (resolved_path) {
22532258
zend_string_release_ex(resolved_path, 0);
22542259
}

0 commit comments

Comments
 (0)