From f3380b669b8ea0b4527ba1f24569ab4c0d7ae5f9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:21:08 +0100 Subject: [PATCH 1/2] Fix GH-20679: finfo_file() doesn't work on remote resources WIP --- ext/fileinfo/fileinfo.c | 23 ++++------------------ ext/fileinfo/libmagic/magic.c | 23 +++++++++++++++------- ext/fileinfo/tests/remote_resource.phpt | 26 +++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 ext/fileinfo/tests/remote_resource.phpt diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index baae757154950..4f2fdb393d699 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -242,16 +242,8 @@ static const char* php_fileinfo_from_path(struct magic_set *magic, const zend_st ZEND_ASSERT(!zend_str_has_nul_byte(path)); ZEND_ASSERT(context != NULL); - /* determine if the file is a local file or remote URL */ - const char *dummy; - php_stream_statbuf ssb; - - const php_stream_wrapper *wrap = php_stream_locate_url_wrapper(ZSTR_VAL(path), &dummy, 0); - if (UNEXPECTED(wrap == NULL)) { - return NULL; - } - #ifdef PHP_WIN32 + php_stream_statbuf ssb; if (php_stream_stat_path_ex(ZSTR_VAL(path), 0, &ssb, context) == SUCCESS) { if (ssb.sb.st_mode & S_IFDIR) { return "directory"; @@ -264,16 +256,9 @@ static const char* php_fileinfo_from_path(struct magic_set *magic, const zend_st return NULL; } - const char *ret_val = NULL; - if (php_stream_stat(stream, &ssb) == SUCCESS) { - if (ssb.sb.st_mode & S_IFDIR) { - ret_val = "directory"; - } else { - ret_val = magic_stream(magic, stream); - if (UNEXPECTED(ret_val == NULL)) { - php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic)); - } - } + const char *ret_val = magic_stream(magic, stream); + if (UNEXPECTED(ret_val == NULL)) { + php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic)); } php_stream_close(stream); diff --git a/ext/fileinfo/libmagic/magic.c b/ext/fileinfo/libmagic/magic.c index e24f22e50d34b..35f628684b003 100644 --- a/ext/fileinfo/libmagic/magic.c +++ b/ext/fileinfo/libmagic/magic.c @@ -195,7 +195,7 @@ magic_stream(struct magic_set *ms, php_stream *stream) file_private const char * file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) { - int rv = -1; + const char *ret = NULL; unsigned char *buf; zend_stat_t sb = {0}; ssize_t nbytes = 0; /* number of bytes read from a datafile */ @@ -218,7 +218,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) case 0: /* nothing found */ break; default: /* matched it and printed type */ - rv = 0; + ret = file_getbuffer(ms); goto done; } @@ -230,7 +230,6 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) if (!stream) { if (unreadable_info(ms, sb.st_mode, inname) == -1) goto done; - rv = -1; goto done; } } @@ -239,7 +238,6 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) if (php_stream_stat(stream, &ssb) < 0) { if (ms->flags & MAGIC_ERROR) { file_error(ms, errno, "cannot stat `%s'", inname); - rv = -1; goto done; } } @@ -248,15 +246,26 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) /* * try looking at the first ms->bytes_max bytes */ + zend_begin_record_errors(); if ((nbytes = php_stream_read(stream, (char *)buf, ms->bytes_max - nbytes)) < 0) { - file_error(ms, errno, "cannot read `%s'", inname); + if (errno == EISDIR) { + EG(record_errors) = false; + ret = "directory"; + } else { + zend_emit_recorded_errors(); + file_error(ms, errno, "cannot read `%s'", inname); + } + zend_free_recorded_errors(); goto done; } + zend_emit_recorded_errors(); + zend_free_recorded_errors(); (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ if (file_buffer(ms, stream, &sb, inname, buf, CAST(size_t, nbytes)) == -1) goto done; - rv = 0; + ret = file_getbuffer(ms); + done: efree(buf); @@ -264,7 +273,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) php_stream_close(stream); } out: - return rv == 0 ? file_getbuffer(ms) : NULL; + return ret; } diff --git a/ext/fileinfo/tests/remote_resource.phpt b/ext/fileinfo/tests/remote_resource.phpt new file mode 100644 index 0000000000000..b443393f826b4 --- /dev/null +++ b/ext/fileinfo/tests/remote_resource.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-20679 (finfo_file() doesn't work on remote resources) +--EXTENSIONS-- +fileinfo +--INI-- +allow_url_fopen=1 +--SKIPIF-- + +--FILE-- + $pid, 'uri' => $uri] = http_server([ + "data://text/plain,HTTP/1.0 200 Ok\r\n\r\nfoo", +], $output); + +$f = finfo_open(); +var_dump(finfo_file($f, $uri)); + +http_server_kill($pid); +?> +--EXPECT-- +string(51) "HTML document, ASCII text, with no line terminators" From fa8fc649ae36f74e1f730d9c182b808fd8db96a8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:24:19 +0100 Subject: [PATCH 2/2] Or like this --- ext/fileinfo/fileinfo.c | 2 -- ext/fileinfo/libmagic/magic.c | 23 +++++++---------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 4f2fdb393d699..f0ba63b683c8a 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -242,14 +242,12 @@ static const char* php_fileinfo_from_path(struct magic_set *magic, const zend_st ZEND_ASSERT(!zend_str_has_nul_byte(path)); ZEND_ASSERT(context != NULL); -#ifdef PHP_WIN32 php_stream_statbuf ssb; if (php_stream_stat_path_ex(ZSTR_VAL(path), 0, &ssb, context) == SUCCESS) { if (ssb.sb.st_mode & S_IFDIR) { return "directory"; } } -#endif php_stream *stream = php_stream_open_wrapper_ex(ZSTR_VAL(path), "rb", REPORT_ERRORS, NULL, context); if (!stream) { diff --git a/ext/fileinfo/libmagic/magic.c b/ext/fileinfo/libmagic/magic.c index 35f628684b003..e24f22e50d34b 100644 --- a/ext/fileinfo/libmagic/magic.c +++ b/ext/fileinfo/libmagic/magic.c @@ -195,7 +195,7 @@ magic_stream(struct magic_set *ms, php_stream *stream) file_private const char * file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) { - const char *ret = NULL; + int rv = -1; unsigned char *buf; zend_stat_t sb = {0}; ssize_t nbytes = 0; /* number of bytes read from a datafile */ @@ -218,7 +218,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) case 0: /* nothing found */ break; default: /* matched it and printed type */ - ret = file_getbuffer(ms); + rv = 0; goto done; } @@ -230,6 +230,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) if (!stream) { if (unreadable_info(ms, sb.st_mode, inname) == -1) goto done; + rv = -1; goto done; } } @@ -238,6 +239,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) if (php_stream_stat(stream, &ssb) < 0) { if (ms->flags & MAGIC_ERROR) { file_error(ms, errno, "cannot stat `%s'", inname); + rv = -1; goto done; } } @@ -246,26 +248,15 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) /* * try looking at the first ms->bytes_max bytes */ - zend_begin_record_errors(); if ((nbytes = php_stream_read(stream, (char *)buf, ms->bytes_max - nbytes)) < 0) { - if (errno == EISDIR) { - EG(record_errors) = false; - ret = "directory"; - } else { - zend_emit_recorded_errors(); - file_error(ms, errno, "cannot read `%s'", inname); - } - zend_free_recorded_errors(); + file_error(ms, errno, "cannot read `%s'", inname); goto done; } - zend_emit_recorded_errors(); - zend_free_recorded_errors(); (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ if (file_buffer(ms, stream, &sb, inname, buf, CAST(size_t, nbytes)) == -1) goto done; - ret = file_getbuffer(ms); - + rv = 0; done: efree(buf); @@ -273,7 +264,7 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) php_stream_close(stream); } out: - return ret; + return rv == 0 ? file_getbuffer(ms) : NULL; }