php / php-src

The PHP Interpreter
https://www.php.net
Other
37.99k stars 7.73k forks source link

imagefontwidth and imagefontheight fail on valid GDFont #13082

Closed jonaskohl closed 8 months ago

jonaskohl commented 8 months ago

The following code:

<?php

const FONT_BUILTIN = 5;
const FONT_EXTERNAL = __DIR__ . "/MiscTT_12x20_LE.gdf";

// Uncomment to download sample font:
// copy("http://www.danceswithferrets.org/lab/gdfs/MiscTT_12x20_LE.gdf", FONT_EXTERNAL);
// Note: This is just a sample font. This problem occurs with every valid gdf file.

echo strval(imagefontwidth(FONT_BUILTIN)) . PHP_EOL;
echo strval(imagefontheight(FONT_BUILTIN)) . PHP_EOL;

$font = imageloadfont(FONT_EXTERNAL);
if ($font === false) die("Failed to load font" . PHP_EOL);
if (!($font instanceof GdFont)) die("Font is not of type " . GdFont::class . PHP_EOL);

var_dump($font);

$fontWidth = imagefontwidth($font);
$fontHeight = imagefontheight($font);

echo strval($fontWidth) . PHP_EOL;
echo strval($fontHeight) . PHP_EOL;

Resulted in this output:

9
15
object(GdFont)#1 (0) {
}
PHP Fatal error:  Uncaught TypeError: imagefontwidth(): Argument #1 ($font) must be of type GdFont|int, GdFont given in /Users/jonas/Desktop/sample.php:19
Stack trace:
#0 /Users/jonas/Desktop/sample.php(19): imagefontwidth(Object(GdFont))
#1 {main}
  thrown in /Users/jonas/Desktop/sample.php on line 19

Fatal error: Uncaught TypeError: imagefontwidth(): Argument #1 ($font) must be of type GdFont|int, GdFont given in /Users/jonas/Desktop/sample.php:19
Stack trace:
#0 /Users/jonas/Desktop/sample.php(19): imagefontwidth(Object(GdFont))
#1 {main}
  thrown in /Users/jonas/Desktop/sample.php on line 19

But I expected this output instead:

9
15
object(GdFont)#1 (0) {
}
12
20

The weird thing is that calls to imagestring work as expected. I'm also unable to reproduce this on non-mac systems. But the people over at MacPorts told me to report this here, so...

PHP Version

8.3.1

Operating System

macOS 14.2.1

nielsdos commented 8 months ago

Weird. Just a quick question: does this reproduce if you disable all extensions except gd?

jonaskohl commented 8 months ago

Yes, this also happens with everything except GD disabled

ryandesign commented 8 months ago

But the people over at MacPorts told me to report this here, so...

Right, I asked @jonaskohl to report it here because I could not reproduce the issue on an Intel MacBook Pro running macOS 12.7.2. I'm the maintainer of PHP in MacPorts and to my knowledge we're not doing anything weird in the way that we build PHP, so if there is something to be fixed, I expect it to be in PHP, not in MacPorts. For cross-referencing, here is the MacPorts bug report about this.

Jonas, what machine are you using? If an Apple Silicon Mac, that difference would be something to explore. For example, you could try running the Intel version of PHP in Rosetta 2 emulation and see if it has the same problem. To try that, you could sudo port install php83-gd +universal (which will build and install both Intel and Apple Silicon versions) and then you can try the Intel version with arch -x86_64 php83 sample.php.

ryandesign commented 8 months ago

Another thought: Could this be caused by some setting in your php.ini? I didn't see the problem when I had no php.ini or when it was a copy of php.ini-production or php.ini-development.

jonaskohl commented 8 months ago

I indeed use Apple Silicon (M3 Pro)!

I've tried both things (using the x86_64 version of php83-gd and also resetting my php.ini to php.ini-production) but the issue still appears.

nielsdos commented 8 months ago

So something must be overriding the class entry then somehow...

Can you try to compile PHP yourself, with the options: --enable-gd --enable-debug --enable-address-sanitizer --enable-undefined-sanitizer ?

jonaskohl commented 8 months ago

Huh, interesting... Building from source actually makes the problem disappear. The output is now the expected:

9
15
object(GdFont)#1 (0) {
}
12
20
./php -i output ``` phpinfo() PHP Version => 8.4.0-dev System => Darwin MBP-von-Jonas.fritz.box 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:54:51 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6030 arm64 Build Date => Jan 7 2024 13:19:35 Build System => Darwin MBP-von-Jonas.fritz.box 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:54:51 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6030 arm64 Configure Command => './configure' '--enable-gd' '--enable-debug' '--enable-address-sanitizer' '--enable-undefined-sanitizer' '--with-iconv=/opt/homebrew/opt/libiconv/' Server API => Command Line Interface Virtual Directory Support => disabled Configuration File (php.ini) Path => /usr/local/lib Loaded Configuration File => (none) Scan this dir for additional .ini files => (none) Additional .ini files parsed => (none) PHP API => 20230901 PHP Extension => 20230901 Zend Extension => 420230901 Zend Extension Build => API420230901,NTS,debug PHP Extension Build => API20230901,NTS,debug PHP Integer Size => 64 bits Debug Build => yes Thread Safety => disabled Zend Signal Handling => enabled Zend Memory Manager => enabled Zend Multibyte Support => disabled Zend Max Execution Timers => disabled IPv6 Support => enabled DTrace Support => disabled Registered PHP Streams => php, file, glob, data, http, ftp, phar Registered Stream Socket Transports => tcp, udp, unix, udg Registered Stream Filters => convert.iconv.*, string.rot13, string.toupper, string.tolower, convert.*, consumed, dechunk This program makes use of the Zend Scripting Language Engine: Zend Engine v4.4.0-dev, Copyright (c) Zend Technologies _______________________________________________________________________ Configuration Core PHP Version => 8.4.0-dev Directive => Local Value => Master Value allow_url_fopen => On => On allow_url_include => Off => Off arg_separator.input => & => & arg_separator.output => & => & auto_append_file => no value => no value auto_globals_jit => On => On auto_prepend_file => no value => no value browscap => no value => no value default_charset => UTF-8 => UTF-8 default_mimetype => text/html => text/html disable_classes => no value => no value disable_functions => no value => no value display_errors => STDOUT => STDOUT display_startup_errors => On => On doc_root => no value => no value docref_ext => no value => no value docref_root => no value => no value enable_dl => On => On enable_post_data_reading => On => On error_append_string => no value => no value error_log => no value => no value error_log_mode => 0644 => 0644 error_prepend_string => no value => no value error_reporting => no value => no value expose_php => On => On extension_dir => /usr/local/lib/php/extensions/debug-non-zts-20230901 => /usr/local/lib/php/extensions/debug-non-zts-20230901 fiber.stack_size => no value => no value file_uploads => On => On hard_timeout => 2 => 2 highlight.comment => #FF8000 => #FF8000 highlight.default => #0000BB => #0000BB highlight.html => #000000 => #000000 highlight.keyword => #007700 => #007700 highlight.string => #DD0000 => #DD0000 html_errors => Off => Off ignore_repeated_errors => Off => Off ignore_repeated_source => Off => Off ignore_user_abort => Off => Off implicit_flush => On => On include_path => .: => .: input_encoding => no value => no value internal_encoding => no value => no value log_errors => Off => Off mail.add_x_header => Off => Off mail.force_extra_parameters => no value => no value mail.log => no value => no value mail.mixed_lf_and_crlf => Off => Off max_execution_time => 0 => 0 max_file_uploads => 20 => 20 max_input_nesting_level => 64 => 64 max_input_time => -1 => -1 max_input_vars => 1000 => 1000 max_multipart_body_parts => -1 => -1 memory_limit => 128M => 128M open_basedir => no value => no value output_buffering => 0 => 0 output_encoding => no value => no value output_handler => no value => no value post_max_size => 8M => 8M precision => 14 => 14 realpath_cache_size => 4096K => 4096K realpath_cache_ttl => 120 => 120 register_argc_argv => On => On report_memleaks => On => On report_zend_debug => Off => Off request_order => no value => no value sendmail_from => no value => no value sendmail_path => /usr/sbin/sendmail -t -i => /usr/sbin/sendmail -t -i serialize_precision => -1 => -1 short_open_tag => On => On SMTP => localhost => localhost smtp_port => 25 => 25 sys_temp_dir => no value => no value syslog.facility => LOG_USER => LOG_USER syslog.filter => no-ctrl => no-ctrl syslog.ident => php => php unserialize_callback_func => no value => no value upload_max_filesize => 2M => 2M upload_tmp_dir => no value => no value user_dir => no value => no value user_ini.cache_ttl => 300 => 300 user_ini.filename => .user.ini => .user.ini variables_order => EGPCS => EGPCS xmlrpc_error_number => 0 => 0 xmlrpc_errors => Off => Off zend.assertions => 1 => 1 zend.detect_unicode => On => On zend.enable_gc => On => On zend.exception_ignore_args => Off => Off zend.exception_string_param_max_len => 15 => 15 zend.max_allowed_stack_size => 0 => 0 zend.multibyte => Off => Off zend.reserved_stack_size => 0 => 0 zend.script_encoding => no value => no value zend.signal_check => On => On ctype ctype functions => enabled date date/time support => enabled timelib version => 2022.10 "Olson" Timezone Database Version => 2023.4 Timezone Database => internal Default timezone => UTC Directive => Local Value => Master Value date.default_latitude => 31.7667 => 31.7667 date.default_longitude => 35.2333 => 35.2333 date.sunrise_zenith => 90.833333 => 90.833333 date.sunset_zenith => 90.833333 => 90.833333 date.timezone => UTC => UTC dom DOM/XML => enabled DOM/XML API Version => 20031129 libxml Version => 2.11.6 HTML Support => enabled XPath Support => enabled XPointer Support => enabled Schema Support => enabled RelaxNG Support => enabled fileinfo fileinfo support => enabled libmagic => 543 filter Input Validation and Filtering => enabled Directive => Local Value => Master Value filter.default => unsafe_raw => unsafe_raw filter.default_flags => no value => no value gd GD Support => enabled GD Version => bundled (2.1.0 compatible) GIF Read Support => enabled GIF Create Support => enabled PNG Support => enabled libPNG Version => 1.6.40 WBMP Support => enabled XBM Support => enabled BMP Support => enabled TGA Read Support => enabled Directive => Local Value => Master Value gd.jpeg_ignore_warning => On => On hash hash support => enabled Hashing Engines => md2 md4 md5 sha1 sha224 sha256 sha384 sha512/224 sha512/256 sha512 sha3-224 sha3-256 sha3-384 sha3-512 ripemd128 ripemd160 ripemd256 ripemd320 whirlpool tiger128,3 tiger160,3 tiger192,3 tiger128,4 tiger160,4 tiger192,4 snefru snefru256 gost gost-crypto adler32 crc32 crc32b crc32c fnv132 fnv1a32 fnv164 fnv1a64 joaat murmur3a murmur3c murmur3f xxh32 xxh64 xxh3 xxh128 haval128,3 haval160,3 haval192,3 haval224,3 haval256,3 haval128,4 haval160,4 haval192,4 haval224,4 haval256,4 haval128,5 haval160,5 haval192,5 haval224,5 haval256,5 iconv iconv support => enabled iconv implementation => libiconv iconv library version => 1.17 Directive => Local Value => Master Value iconv.input_encoding => no value => no value iconv.internal_encoding => no value => no value iconv.output_encoding => no value => no value json json support => enabled libxml libXML support => active libXML Compiled Version => 2.11.6 libXML Loaded Version => 21106 libXML streams => enabled pcre PCRE (Perl Compatible Regular Expressions) Support => enabled PCRE Library Version => 10.42 2022-12-12 PCRE Unicode Version => 14.0.0 PCRE JIT Support => enabled PCRE JIT Target => ARM-64 64bit (little endian + unaligned) Directive => Local Value => Master Value pcre.backtrack_limit => 1000000 => 1000000 pcre.jit => On => On pcre.recursion_limit => 100000 => 100000 PDO PDO support => enabled PDO drivers => sqlite pdo_sqlite PDO Driver for SQLite 3.x => enabled SQLite Library => 3.44.2 Phar Phar: PHP Archive support => enabled Phar API version => 1.1.1 Phar-based phar archives => enabled Tar-based phar archives => enabled ZIP-based phar archives => enabled gzip compression => disabled (install ext/zlib) bzip2 compression => disabled (install ext/bz2) OpenSSL support => disabled (install ext/openssl) Phar based on pear/PHP_Archive, original concept by Davey Shafik. Phar fully realized by Gregory Beaver and Marcus Boerger. Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle. Directive => Local Value => Master Value phar.cache_list => no value => no value phar.readonly => On => On phar.require_hash => On => On posix POSIX support => enabled random Version => 8.4.0-dev Reflection Reflection => enabled session Session Support => enabled Registered save handlers => files user Registered serializer handlers => php_serialize php php_binary Directive => Local Value => Master Value session.auto_start => Off => Off session.cache_expire => 180 => 180 session.cache_limiter => nocache => nocache session.cookie_domain => no value => no value session.cookie_httponly => Off => Off session.cookie_lifetime => 0 => 0 session.cookie_path => / => / session.cookie_samesite => no value => no value session.cookie_secure => Off => Off session.gc_divisor => 100 => 100 session.gc_maxlifetime => 1440 => 1440 session.gc_probability => 1 => 1 session.lazy_write => On => On session.name => PHPSESSID => PHPSESSID session.referer_check => no value => no value session.save_handler => files => files session.save_path => no value => no value session.serialize_handler => php => php session.sid_bits_per_character => 4 => 4 session.sid_length => 32 => 32 session.upload_progress.cleanup => On => On session.upload_progress.enabled => On => On session.upload_progress.freq => 1% => 1% session.upload_progress.min_freq => 1 => 1 session.upload_progress.name => PHP_SESSION_UPLOAD_PROGRESS => PHP_SESSION_UPLOAD_PROGRESS session.upload_progress.prefix => upload_progress_ => upload_progress_ session.use_cookies => On => On session.use_only_cookies => On => On session.use_strict_mode => Off => Off session.use_trans_sid => Off => Off SimpleXML SimpleXML support => enabled Schema support => enabled SPL SPL support => enabled Interfaces => OuterIterator, RecursiveIterator, SeekableIterator, SplObserver, SplSubject Classes => AppendIterator, ArrayIterator, ArrayObject, BadFunctionCallException, BadMethodCallException, CachingIterator, CallbackFilterIterator, DirectoryIterator, DomainException, EmptyIterator, FilesystemIterator, FilterIterator, GlobIterator, InfiniteIterator, InvalidArgumentException, IteratorIterator, LengthException, LimitIterator, LogicException, MultipleIterator, NoRewindIterator, OutOfBoundsException, OutOfRangeException, OverflowException, ParentIterator, RangeException, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveIteratorIterator, RecursiveRegexIterator, RecursiveTreeIterator, RegexIterator, RuntimeException, SplDoublyLinkedList, SplFileInfo, SplFileObject, SplFixedArray, SplHeap, SplMinHeap, SplMaxHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack, SplTempFileObject, UnderflowException, UnexpectedValueException sqlite3 SQLite3 support => enabled SQLite Library => 3.44.2 Directive => Local Value => Master Value sqlite3.defensive => On => On sqlite3.extension_dir => no value => no value standard Dynamic Library Support => enabled Path to sendmail => /usr/sbin/sendmail -t -i Directive => Local Value => Master Value assert.active => On => On assert.bail => Off => Off assert.callback => no value => no value assert.exception => On => On assert.warning => On => On auto_detect_line_endings => Off => Off default_socket_timeout => 60 => 60 from => no value => no value session.trans_sid_hosts => no value => no value session.trans_sid_tags => a=href,area=href,frame=src,form= => a=href,area=href,frame=src,form= unserialize_max_depth => 4096 => 4096 url_rewriter.hosts => no value => no value url_rewriter.tags => form= => form= user_agent => no value => no value tokenizer Tokenizer Support => enabled xml XML Support => active XML Namespace Support => active libxml2 Version => 2.11.6 xmlreader XMLReader => enabled xmlwriter XMLWriter => enabled Additional Modules Module Name Environment Variable => Value TERM_SESSION_ID => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 SSH_AUTH_SOCK => /private/tmp/com.apple.launchd.rCmMRFn0uq/Listeners LC_TERMINAL_VERSION => 3.4.23 COLORFGBG => 7;0 ITERM_PROFILE => Default XPC_FLAGS => 0x0 LANG => de_DE.UTF-8 PWD => /Users/jonas/Entwicklung/Git/php-src/sapi/cli SHELL => /bin/zsh __CFBundleIdentifier => com.googlecode.iterm2 TERM_PROGRAM_VERSION => 3.4.23 TERM_PROGRAM => iTerm.app PATH => /opt/homebrew/bin:/opt/homebrew/sbin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin LC_TERMINAL => iTerm2 COLORTERM => truecolor COMMAND_MODE => unix2003 TERM => xterm-256color HOME => /Users/jonas TMPDIR => /var/folders/hy/hp8jgcyx7x14v_ph1w438w780000gn/T/ USER => jonas XPC_SERVICE_NAME => 0 LOGNAME => jonas ITERM_SESSION_ID => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 __CF_USER_TEXT_ENCODING => 0x1F5:0x0:0x3 SHLVL => 1 OLDPWD => /Users/jonas/Entwicklung/Git/php-src/sapi/cli HOMEBREW_PREFIX => /opt/homebrew HOMEBREW_CELLAR => /opt/homebrew/Cellar HOMEBREW_REPOSITORY => /opt/homebrew MANPATH => /opt/homebrew/share/man:: INFOPATH => /opt/homebrew/share/info: _ => /Users/jonas/Entwicklung/Git/php-src/sapi/cli/./php PHP Variables Variable => Value $_SERVER['TERM_SESSION_ID'] => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 $_SERVER['SSH_AUTH_SOCK'] => /private/tmp/com.apple.launchd.rCmMRFn0uq/Listeners $_SERVER['LC_TERMINAL_VERSION'] => 3.4.23 $_SERVER['COLORFGBG'] => 7;0 $_SERVER['ITERM_PROFILE'] => Default $_SERVER['XPC_FLAGS'] => 0x0 $_SERVER['LANG'] => de_DE.UTF-8 $_SERVER['PWD'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli $_SERVER['SHELL'] => /bin/zsh $_SERVER['__CFBundleIdentifier'] => com.googlecode.iterm2 $_SERVER['TERM_PROGRAM_VERSION'] => 3.4.23 $_SERVER['TERM_PROGRAM'] => iTerm.app $_SERVER['PATH'] => /opt/homebrew/bin:/opt/homebrew/sbin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin $_SERVER['LC_TERMINAL'] => iTerm2 $_SERVER['COLORTERM'] => truecolor $_SERVER['COMMAND_MODE'] => unix2003 $_SERVER['TERM'] => xterm-256color $_SERVER['HOME'] => /Users/jonas $_SERVER['TMPDIR'] => /var/folders/hy/hp8jgcyx7x14v_ph1w438w780000gn/T/ $_SERVER['USER'] => jonas $_SERVER['XPC_SERVICE_NAME'] => 0 $_SERVER['LOGNAME'] => jonas $_SERVER['ITERM_SESSION_ID'] => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 $_SERVER['__CF_USER_TEXT_ENCODING'] => 0x1F5:0x0:0x3 $_SERVER['SHLVL'] => 1 $_SERVER['OLDPWD'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli $_SERVER['HOMEBREW_PREFIX'] => /opt/homebrew $_SERVER['HOMEBREW_CELLAR'] => /opt/homebrew/Cellar $_SERVER['HOMEBREW_REPOSITORY'] => /opt/homebrew $_SERVER['MANPATH'] => /opt/homebrew/share/man:: $_SERVER['INFOPATH'] => /opt/homebrew/share/info: $_SERVER['_'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli/./php $_SERVER['PHP_SELF'] => $_SERVER['SCRIPT_NAME'] => $_SERVER['SCRIPT_FILENAME'] => $_SERVER['PATH_TRANSLATED'] => $_SERVER['DOCUMENT_ROOT'] => $_SERVER['REQUEST_TIME_FLOAT'] => 1704631871.1001 $_SERVER['REQUEST_TIME'] => 1704631871 $_SERVER['argv'] => Array ( ) $_SERVER['argc'] => 0 $_ENV['TERM_SESSION_ID'] => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 $_ENV['SSH_AUTH_SOCK'] => /private/tmp/com.apple.launchd.rCmMRFn0uq/Listeners $_ENV['LC_TERMINAL_VERSION'] => 3.4.23 $_ENV['COLORFGBG'] => 7;0 $_ENV['ITERM_PROFILE'] => Default $_ENV['XPC_FLAGS'] => 0x0 $_ENV['LANG'] => de_DE.UTF-8 $_ENV['PWD'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli $_ENV['SHELL'] => /bin/zsh $_ENV['__CFBundleIdentifier'] => com.googlecode.iterm2 $_ENV['TERM_PROGRAM_VERSION'] => 3.4.23 $_ENV['TERM_PROGRAM'] => iTerm.app $_ENV['PATH'] => /opt/homebrew/bin:/opt/homebrew/sbin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin $_ENV['LC_TERMINAL'] => iTerm2 $_ENV['COLORTERM'] => truecolor $_ENV['COMMAND_MODE'] => unix2003 $_ENV['TERM'] => xterm-256color $_ENV['HOME'] => /Users/jonas $_ENV['TMPDIR'] => /var/folders/hy/hp8jgcyx7x14v_ph1w438w780000gn/T/ $_ENV['USER'] => jonas $_ENV['XPC_SERVICE_NAME'] => 0 $_ENV['LOGNAME'] => jonas $_ENV['ITERM_SESSION_ID'] => w0t1p0:AD66DE1D-8566-46D4-980E-134CCC4526F5 $_ENV['__CF_USER_TEXT_ENCODING'] => 0x1F5:0x0:0x3 $_ENV['SHLVL'] => 1 $_ENV['OLDPWD'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli $_ENV['HOMEBREW_PREFIX'] => /opt/homebrew $_ENV['HOMEBREW_CELLAR'] => /opt/homebrew/Cellar $_ENV['HOMEBREW_REPOSITORY'] => /opt/homebrew $_ENV['MANPATH'] => /opt/homebrew/share/man:: $_ENV['INFOPATH'] => /opt/homebrew/share/info: $_ENV['_'] => /Users/jonas/Entwicklung/Git/php-src/sapi/cli/./php PHP License This program is free software; you can redistribute it and/or modify it under the terms of the PHP License as published by the PHP Group and included in the distribution in the file: LICENSE This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. If you did not receive a copy of the PHP license, or have any questions about PHP licensing, please contact license@php.net. ```
nielsdos commented 8 months ago

Sounds like a build or configuration issue over at Macports then? Then again, even when you replaced the ini you got the issue. I don't have a mac, so I can't really debug this...

devnexen commented 8 months ago

worth noting it also happens with the homebrew package (all versions).

kocsismate commented 8 months ago

I also have an M3 Pro, and I managed to run the above test successfully. Of course, I built PHP from source so I agree that the issue is most likely with Macports.

ryandesign commented 8 months ago

I have now tested in a fresh MacPorts installation on a Mac mini with an M1 Apple Silicon processor running macOS 14.2.1 and I was able to reproduce the issue. This does not mean MacPorts is the problem; it means MacPorts used a different set of configure arguments, environment variables, and patches when it built php from source than the user did, and this difference is what exposes the problem, wherever it may be.

My first guess was optimization flags, and this guess seems to have been correct. Since 2013, MacPorts has built ports with the -Os optimization flag. If I rebuild php83-gd 8.3.1 with any other optimization flag (I tried -O0, -O1, -O2, and -O3) the problem does not occur. The optimization flags used to build php itself don't appear to influence the problem, only the flags used to build php83-gd. This system has Xcode 15.1 which has this version of Apple's fork of the clang compiler:

% clang --version
Apple clang version 15.0.0 (clang-1500.1.0.2.5)
Target: arm64-apple-darwin23.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Xcode 15.2 was released yesterday but its clang version is unchanged so I don't expect different results.

I then returned to my Intel MacBook Pro with macOS 12.7.2 where I had not previously observed the problem. I tried rebuilding php83-gd with various versions of llvm.org's clang compiler which I installed using MacPorts. The problem did not occur when php83-gd was compiled with clang 12.0.1 or 13.0.1 when using -Os but it did occur with clang 14.0.6, 15.0.7, 16.0.6, and 17.0.6 when using -Os.

I also checked php82-gd 8.2.14 and php81-gd 8.1.27 and they are also affected. php80-gd 8.0.30 is not affected. I would be interested to know what changed between 8.0.30 and 8.1.27 that causes this. I may test some more versions to try to find it, unless somebody already has an idea what it might be.

I don't have enough information at this point to say whether we have found a bug in clang or a bug in php.

ryandesign commented 8 months ago

worth noting it also happens with the homebrew package (all versions).

Homebrew has used -Os since 2012.

devnexen commented 8 months ago

Thanks for your input. it can be reproduced on linux too. tried with :

ryandesign commented 8 months ago

Thanks for the fix! I'll add it to MacPorts.

So the problem was uninitialized use of a variable. I'm surprised the compiler didn't warn about that, even if I add -Wall and -Wextra. It does finally warn if I add -Wconditional-uninitialized:

ext/gd/gd.c:2688:26: warning: variable 'font_obj' may be uninitialized when used here [-Wconditional-uninitialized]
        font = php_find_gd_font(font_obj, font_int);
                                ^~~~~~~~
ext/gd/gd.c:2680:23: note: initialize the variable 'font_obj' to silence this warning
        zend_object *font_obj;
                             ^
                              = NULL
ext/gd/gd.c:2688:36: warning: variable 'font_int' may be uninitialized when used here [-Wconditional-uninitialized]
        font = php_find_gd_font(font_obj, font_int);
                                          ^~~~~~~~
ext/gd/gd.c:2681:20: note: initialize the variable 'font_int' to silence this warning
        zend_long font_int;
                          ^
                           = 0

Maybe php should be using this warning flag by default. Using it exposes a lot of other instances of this as well, and I've only looked at gd, not the rest of php, though I don't know if the other potential uninitialized uses of variables are a problem or not.

I noticed you committed the fix to master and the 8.3 and 8.2 branches but not the 8.1 branch even though it is also affected. I realize php 8.1 is receiving security fixes only at this point. Is the use of an uninitialized variable that leads to undefined behavior not considered a security issue?

devnexen commented 8 months ago

I noticed you committed the fix to master and the 8.3 and 8.2 branches but not the 8.1 branch even though it is also affected. I realize php 8.1 is receiving security fixes only at this point. Is the use of an uninitialized variable that leads to undefined behavior not considered a security issue?

This is up to release managers, beside I do not know much macports but a patch could be written for 8.1 I think.

ryandesign commented 8 months ago

This is up to release managers,

Do I need to bring it to the attention of the release managers manually (if so, how?) or do they review fixes for potential backporting automatically?

beside I do not know much macports but a patch could be written for 8.1 I think.

Yes, I did add the patch for php 8.1, 8.2, and 8.3 in MacPorts. But I try to get patches incorporated upstream wherever possible since that way the fix reaches the largest possible audience and I don't have to maintain a patch forever.

devnexen commented 8 months ago

Do not get me wrong, I m not dismissing you :) I ve notified the proper persons but ultimately they will decide what to do. Thanks.

smalyshev commented 8 months ago

It indeed does not initialize the values if parameter parsing fails, but it should not get to php_find_gd_font if parameter parsing fails. So I don't really understand how initialization can be a problem there. Also, I am not sure I understand what this error means:

PHP Fatal error:  Uncaught TypeError: imagefontwidth(): Argument #1 ($font) must be of type GdFont|int, GdFont given in /Users/jonas/Desktop/sample.php:19

It needs GdFont and it got GdFont, so what's wrong there?

nielsdos commented 8 months ago

It needs GdFont and it got GdFont, so what's wrong there?

Looking at the assembly, the compiler removed a lot of code because the branching depended on an uninitialized value which is UB. So the assembly is bogus. In general we can't predict what a compiler is going to emit under such circumstances.

smalyshev commented 8 months ago

because the branching depended on an uninitialized value which is UB

Which branching though? The code isn't supposed to touch font_obj on parameter parse error, it's just supposed to return.

smalyshev commented 8 months ago

Tried to step through the compiled code with -Os and it definitely looks like some kind of compiler bug, because I am seeing this:

  * frame #0: 0x000000010028a6c0 cliphp`instanceof_function_slow(instance_ce=0x000000013ae1dc70, ce=0x000000013ae1dc70) at zend_operators.c:2464 [opt]
    frame #1: 0x00000001000ee394 cliphp`php_imagefontsize [inlined] instanceof_function(instance_ce=<unavailable>, ce=<unavailable>) at zend_operators.h:72:30 [opt]

Why is this wrong? Because instanceof_function_slow should never be called with the same arguments:

static zend_always_inline bool instanceof_function(
        const zend_class_entry *instance_ce, const zend_class_entry *ce) {
    return instance_ce == ce || instanceof_function_slow(instance_ce, ce);
}

and

    ZEND_ASSERT(instance_ce != ce && "Should have been checked already");

Note this has nothing to do with the font_ variables - it's all about function argument parsing yet. Indeed, if I enable that assertion, the reproduction code now ends up in:

Assertion failed: (instance_ce != ce), function instanceof_function_slow, file zend_operators.c, line 2465.

So the problem here is that the compiler somehow killed the instance_ce == ce clause - which, again, does not deal with any undefined variables at all.

smalyshev commented 8 months ago

For illustration, this is the correct assembly code for it:

    0x1000ee358 <+64>:  cmp    w8, #0x8                  ;; <= this checks for IS_OBJECT 
    0x1000ee35c <+68>:  b.ne   0x1000ee400               ; <+232> [inlined] zend_parse_arg_obj_or_long + 4 at zend_API.h:2413:13
    0x1000ee360 <+72>:  ldr    x1, [x23, #0x770]
    0x1000ee364 <+76>:  ldr    x0, [x21]
    0x1000ee368 <+80>:  ldr    x8, [x0, #0x10]
    0x1000ee36c <+84>:  cmp    x8, x1                    ;; <= this compares classes
    0x1000ee370 <+88>:  b.eq   0x1000ee38c               ; <+116> at zend_API.h
    0x1000ee374 <+92>:  mov    x0, x8
    0x1000ee378 <+96>:  bl     0x10028a6c0               ; instanceof_function_slow at zend_operators.c:2464
    0x1000ee37c <+100>: cbz    w0, 0x1000ee3fc           ; <+228> [inlined] zval_get_type at zend_types.h:648:18

and this is the buggy one:

    0x1000ee378 <+48>:  cmp    w8, #0x8                  ;; <= this checks for IS_OBJECT 
    0x1000ee37c <+52>:  b.ne   0x1000ee398               ; <+80> at gd.c
    0x1000ee380 <+56>:  adrp   x8, 3675
    0x1000ee384 <+60>:  ldr    x1, [x8, #0x770]
    0x1000ee388 <+64>:  ldr    x8, [x21, #0x50]
    0x1000ee38c <+68>:  ldr    x0, [x8, #0x10]
    0x1000ee390 <+72>:  bl     0x10028a6c0               ; instanceof_function_slow at zend_operators.c:2464

Note how class comparison is missing and it's going directly for instanceof_function_slow - which obviously will fail because it's not supposed to get this type of arguments.

ranvis commented 8 months ago

@smalyshev The cause of the undefined behavior that I could explain is the expression php_find_gd_font(font_obj, font_int). Before the fix, the pushed variable font_int is uninitialized when font_obj is set to non-null by zend_parse_arg_obj_or_long().

SakiTakamachi commented 8 months ago

I don't know if this is related because I haven't looked closely, but it seems like the test is failing on Travis.

https://app.travis-ci.com/github/php/php-src/builds/268436286#L2882

devnexen commented 8 months ago

The sample file used for the test is endian dependant.