Merge branch 'PHP-8.3'

* PHP-8.3:
  Fix GH-10344: imagettfbbox(): Could not find/open font UNC path
  Fix GH-13037: PharData incorrectly extracts zip file
This commit is contained in:
Niels Dossche 2024-01-25 20:07:52 +01:00
commit b8ff8c04f8
5 changed files with 97 additions and 19 deletions

View file

@ -401,7 +401,17 @@ static void *fontFetch (char **error, void *key)
#ifdef NETWARE #ifdef NETWARE
if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) { if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) {
#else #else
if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) { /* Actual length doesn't matter, just the minimum does up to length 2. */
unsigned int min_length = 0;
if (name[0] != '\0') {
if (name[1] != '\0') {
min_length = 2;
} else {
min_length = 1;
}
}
ZEND_IGNORE_VALUE(min_length); /* On Posix systems this may be unused */
if (IS_ABSOLUTE_PATH(name, min_length)) {
#endif #endif
snprintf(fullname, sizeof(fullname) - 1, "%s", name); snprintf(fullname, sizeof(fullname) - 1, "%s", name);
if (access(fullname, R_OK) == 0) { if (access(fullname, R_OK) == 0) {

28
ext/gd/tests/gh10344.phpt Normal file
View file

@ -0,0 +1,28 @@
--TEST--
GH-10344 (imagettfbbox(): Could not find/open font UNC path)
--EXTENSIONS--
gd
--SKIPIF--
<?php
if (PHP_OS_FAMILY != 'Windows') {
die("skip windows only test");
}
if (!GD_BUNDLED) {
die("skip Not (yet) fixed in libgd itself");
}
if (!function_exists("imagettfbbox")) {
die("skip ttf support unavailable");
}
if (!is_readable("\\\\localhost\\c$\\Windows\\Fonts\\arial.ttf")) {
die("skip font not readable");
}
?>
--FILE--
<?php
$font = '\\\\localhost\\c$\\Windows\\Fonts\\arial.ttf';
var_dump(count(imagettfbbox(10, 0, $font, 'hello')));
?>
--EXPECT--
int(8)

Binary file not shown.

View file

@ -0,0 +1,17 @@
--TEST--
GH-13037 (PharData incorrectly extracts zip file)
--EXTENSIONS--
phar
--FILE--
<?php
$phar = new PharData(__DIR__ . '/files/gh13037.zip');
$phar->extractTo(__DIR__ . '/out_gh13037/');
echo file_get_contents(__DIR__ . '/out_gh13037/test');
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/out_gh13037/test');
@rmdir(__DIR__ . '/out_gh13037');
?>
--EXPECT--
hello

View file

@ -416,8 +416,6 @@ foundit:
entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp); entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
entry.flags = PHAR_ENT_PERM_DEF_FILE; entry.flags = PHAR_ENT_PERM_DEF_FILE;
entry.header_offset = PHAR_GET_32(zipentry.offset); entry.header_offset = PHAR_GET_32(zipentry.offset);
entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
PHAR_GET_16(zipentry.extra_len);
if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) { if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
PHAR_ZIP_FAIL("Cannot process encrypted zip files"); PHAR_ZIP_FAIL("Cannot process encrypted zip files");
@ -447,6 +445,42 @@ foundit:
entry.is_dir = 0; entry.is_dir = 0;
} }
phar_zip_file_header local; /* Warning: only filled in when the entry is not a directory! */
if (!entry.is_dir) {
/* A file has a central directory entry, and a local file header. Both of these contain the filename
* and the extra field data. However, at least the extra field data does not have to match between the two!
* This happens for example for the "Extended Timestamp extra field" where the local header has 2 extra fields
* in comparison to the central header. So we have to use the local header to find the right offset to the file
* contents, otherwise we're reading some garbage bytes before reading the actual file contents. */
zend_off_t current_central_dir_pos = php_stream_tell(fp);
php_stream_seek(fp, entry.header_offset, SEEK_SET);
if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
pefree(entry.filename, entry.is_persistent);
PHAR_ZIP_FAIL("phar error: internal corruption (cannot read local file header)");
}
php_stream_seek(fp, current_central_dir_pos, SEEK_SET);
/* verify local header
* Note: normally I'd check the crc32, and file sizes too here, but that breaks tests zip/bug48791.phpt & zip/odt.phpt,
* suggesting that something may be wrong with those files or the assumption doesn't hold. Anyway, the other checks
* _are_ performed for the alias file as was done in the past too. */
if (entry.filename_len != PHAR_GET_16(local.filename_len)) {
pefree(entry.filename, entry.is_persistent);
PHAR_ZIP_FAIL("phar error: internal corruption (local file header does not match central directory)");
}
entry.offset = entry.offset_abs = entry.header_offset
+ sizeof(phar_zip_file_header)
+ entry.filename_len
+ PHAR_GET_16(local.extra_len);
} else {
entry.offset = entry.offset_abs = entry.header_offset
+ sizeof(phar_zip_file_header)
+ entry.filename_len
+ PHAR_GET_16(zipentry.extra_len);
}
if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
size_t read; size_t read;
php_stream *sigfile; php_stream *sigfile;
@ -474,7 +508,7 @@ foundit:
if (metadata) { if (metadata) {
php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len)); php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
} }
php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET); php_stream_seek(fp, entry.offset, SEEK_SET);
sig = (char *) emalloc(entry.uncompressed_filesize); sig = (char *) emalloc(entry.uncompressed_filesize);
read = php_stream_read(fp, sig, entry.uncompressed_filesize); read = php_stream_read(fp, sig, entry.uncompressed_filesize);
if (read != entry.uncompressed_filesize || read <= 8) { if (read != entry.uncompressed_filesize || read <= 8) {
@ -592,28 +626,17 @@ foundit:
if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
php_stream_filter *filter; php_stream_filter *filter;
zend_off_t saveloc;
/* verify local file header */
phar_zip_file_header local;
/* archive alias found */ /* archive alias found */
saveloc = php_stream_tell(fp);
php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
pefree(entry.filename, entry.is_persistent);
PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
}
/* verify local header */ /* verify local header */
if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) { ZEND_ASSERT(!entry.is_dir);
if (entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
pefree(entry.filename, entry.is_persistent); pefree(entry.filename, entry.is_persistent);
PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)"); PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
} }
/* construct actual offset to file start - local extra_len can be different from central extra_len */ zend_off_t restore_pos = php_stream_tell(fp);
entry.offset = entry.offset_abs =
sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
php_stream_seek(fp, entry.offset, SEEK_SET); php_stream_seek(fp, entry.offset, SEEK_SET);
/* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */ /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
fp->writepos = 0; fp->writepos = 0;
@ -709,7 +732,7 @@ foundit:
} }
/* return to central directory parsing */ /* return to central directory parsing */
php_stream_seek(fp, saveloc, SEEK_SET); php_stream_seek(fp, restore_pos, SEEK_SET);
} }
phar_set_inode(&entry); phar_set_inode(&entry);