Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image format detection false positives with external GD #12019

Closed
orlitzky opened this issue Aug 22, 2023 · 10 comments
Closed

Image format detection false positives with external GD #12019

orlitzky opened this issue Aug 22, 2023 · 10 comments

Comments

@orlitzky
Copy link
Contributor

Description

To check for supported image types, PHP (ext/gd/config.m4) tries to link against libgd:

AC_DEFUN([PHP_GD_CHECK_VERSION],[
  PHP_CHECK_LIBRARY(gd, gdImageCreateFromPng,          [AC_DEFINE(HAVE_GD_PNG,               1, [ ])], [], [ $GD_SHARED_LIBADD ])
  PHP_CHECK_LIBRARY(gd, gdImageCreateFromAvif,         [AC_DEFINE(HAVE_GD_AVIF,              1, [ ])], [], [ $GD_SHARED_LIBADD ])
  ...
])

However, upstream libgd declares those functions unconditionally. Using AVIF as an example, gdImageCreateFromAvif is declared unconditionally at https://github.com/libgd/libgd/blob/master/src/gd.h#L683, and then implemented (when AVIF support is disabled) in https://github.com/libgd/libgd/blob/master/src/gd_avif.c#L674. As a result, PHP decides that it supports AVIF images when it does not.

In particular this causes imagetypes() to return types that PHP does not actually support. It also results in several PHP functions being made available that should not be, e.g.

#ifdef HAVE_GD_AVIF
/* {{{ Create a new image from AVIF file or URL */
PHP_FUNCTION(imagecreatefromavif)
{
        _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_AVIF, "AVIF", gdImageCreateFromAvif, gdImageCreateFromAvifCtx);
}
/* }}} */
#endif /* HAVE_GD_AVIF */

Finally, those two issues lead to test failures when format-specific are not skipped as they should be:

$ head ./ext/gd/tests/imagecreatefromstring_avif.phpt 
--TEST--
imagecreatefromstring() - AVIF format
--EXTENSIONS--
gd
--SKIPIF--
<?php
if (!(imagetypes() & IMG_AVIF)) {
    die('skip AVIF support required');
}
?>

Thus when some formats are missing from the system gd, there are a few more test failures to add to the list in #11252

PHP Version

git head

Operating System

No response

@orlitzky
Copy link
Contributor Author

When freetype support is missing from gd, PHP actually fails to build. The feature detection looks for a function that's always there, but then PHP tries to call a function that is only defined conditionally:

/var/lib/portage/tmp/portage/dev-lang/php-8.2.9/work/sapis-build/cli/ext/gd/gd.c:369: undefined reference to `gdFontCacheShutdown'
collect2: error: ld returned 1 exit status
make: *** [Makefile:264: sapi/cli/php] Error 1

orlitzky added a commit to orlitzky/php-src that referenced this issue Aug 22, 2023
We currently check for, say, AVIF support by attempting to link a
program that calls libgd's gdImageCreateFromAvif() function. But
perversely, that function always exists in libgd; moreover when AVIF
support is missing it emits a warning and returns normally. Thus
our straightforward link test becomes not so straightforward.

This commit adds a new macro PHP_GD_CHECK_FORMAT that compiles, links,
and runs a test program instead. The test program overrides that "emit
a warning" handler so that the program actually fails if the format
we're looking for is not supported. This fixes detection of AVIF and
the other formats we check for in an external libgd.

Closes phpGH-12019
orlitzky added a commit to orlitzky/php-src that referenced this issue Nov 27, 2023
We currently check for, say, AVIF support by attempting to link a
program that calls libgd's gdImageCreateFromAvif() function. But
perversely, that function always exists in libgd; moreover when AVIF
support is missing it emits a warning and returns normally. Thus
our straightforward link test becomes not so straightforward.

This commit adds a new macro PHP_GD_CHECK_FORMAT that compiles, links,
and runs a test program instead. The test program overrides that "emit
a warning" handler so that the program actually fails if the format
we're looking for is not supported. This fixes detection of AVIF and
the other formats we check for in an external libgd.

Closes phpGH-12019
@petk petk closed this as completed in 85e5635 Dec 27, 2023
@petk petk linked a pull request Dec 27, 2023 that will close this issue
@andypost
Copy link
Contributor

Thank you! it helps #7526

petk pushed a commit to petk/php-src that referenced this issue Feb 25, 2024
- Use gdFontCacheShutdown() to detect freetype
  Currently we look for gdImageStringFT() to determine whether or not gd
  has freetype support... but that function always exists. This leads
  PHP to believe that gd has freetype support when it does not, and can
  lead to build failures.

  The gdFontCacheShutdown() function, on the other hand, is only present
  when gd was built with freetype support. Let's use that instead.

- Fix GD image format detection
  We currently check for, say, AVIF support by attempting to link a
  program that calls libgd's gdImageCreateFromAvif() function. But
  perversely, that function always exists in libgd; moreover when AVIF
  support is missing it emits a warning and returns normally. Thus
  our straightforward link test becomes not so straightforward.

  This commit adds a new macro PHP_GD_CHECK_FORMAT that compiles, links,
  and runs a test program instead. The test program overrides that "emit
  a warning" handler so that the program actually fails if the format
  we're looking for is not supported. This fixes detection of AVIF and
  the other formats we check for in an external libgd.

- ext/gd/tests/bug77391.phpt: skip if gd lacks BMP support
  I don't actually know how to remove BMP support from libgd, but PHP
  has a ./configure test for it, so we should probably treat it as
  optional.

Closes phpGH-12019
petk pushed a commit to petk/php-src that referenced this issue Feb 25, 2024
- Use gdFontCacheShutdown() to detect freetype
  Currently we look for gdImageStringFT() to determine whether or not gd
  has freetype support... but that function always exists. This leads
  PHP to believe that gd has freetype support when it does not, and can
  lead to build failures.

  The gdFontCacheShutdown() function, on the other hand, is only present
  when gd was built with freetype support. Let's use that instead.

- Fix GD image format detection
  We currently check for, say, AVIF support by attempting to link a
  program that calls libgd's gdImageCreateFromAvif() function. But
  perversely, that function always exists in libgd; moreover when AVIF
  support is missing it emits a warning and returns normally. Thus
  our straightforward link test becomes not so straightforward.

  This commit adds a new macro PHP_GD_CHECK_FORMAT that compiles, links,
  and runs a test program instead. The test program overrides that "emit
  a warning" handler so that the program actually fails if the format
  we're looking for is not supported. This fixes detection of AVIF and
  the other formats we check for in an external libgd.

- ext/gd/tests/bug77391.phpt: skip if gd lacks BMP support
  I don't actually know how to remove BMP support from libgd, but PHP
  has a ./configure test for it, so we should probably treat it as
  optional.

Closes phpGH-12019
petk pushed a commit that referenced this issue Feb 26, 2024
- Use gdFontCacheShutdown() to detect freetype
  Currently we look for gdImageStringFT() to determine whether or not gd
  has freetype support... but that function always exists. This leads
  PHP to believe that gd has freetype support when it does not, and can
  lead to build failures.

  The gdFontCacheShutdown() function, on the other hand, is only present
  when gd was built with freetype support. Let's use that instead.

- Fix GD image format detection
  We currently check for, say, AVIF support by attempting to link a
  program that calls libgd's gdImageCreateFromAvif() function. But
  perversely, that function always exists in libgd; moreover when AVIF
  support is missing it emits a warning and returns normally. Thus
  our straightforward link test becomes not so straightforward.

  This commit adds a new macro PHP_GD_CHECK_FORMAT that compiles, links,
  and runs a test program instead. The test program overrides that "emit
  a warning" handler so that the program actually fails if the format
  we're looking for is not supported. This fixes detection of AVIF and
  the other formats we check for in an external libgd.

- ext/gd/tests/bug77391.phpt: skip if gd lacks BMP support
  I don't actually know how to remove BMP support from libgd, but PHP
  has a ./configure test for it, so we should probably treat it as
  optional.

Closes GH-12019
@rs-bc
Copy link

rs-bc commented Mar 15, 2024

This breaks detection of libgd supported graphic formats completely on a FreeBSD-13.2 system:

configure:37620: checking for working gdImageCreateFromWebp in libgd
configure:37655: cc -o conftest -g -O2 -fvisibility=hidden -D_GNU_SOURCE -Wl,-rpath,/usr/local/lib -L/usr/local/lib conftest.c -lutil -lrt -lm -lxml2 -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lgd >&5
conftest.c:250:10: fatal error: 'gd.h' file not found
#include <gd.h>

/usr/local/include is missing from the include path!

@orlitzky
Copy link
Contributor Author

This breaks detection of libgd supported graphic formats completely on a FreeBSD-13.2 system:

configure:37620: checking for working gdImageCreateFromWebp in libgd configure:37655: cc -o conftest -g -O2 -fvisibility=hidden -D_GNU_SOURCE -Wl,-rpath,/usr/local/lib -L/usr/local/lib conftest.c -lutil -lrt -lm -lxml2 -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lgd >&5 conftest.c:250:10: fatal error: 'gd.h' file not found #include <gd.h>

/usr/local/include is missing from the include path!

PHP itself tries to #include <gd.h> in ext/gd/gd.c, so if it isn't in your include path, then the system copy of gd won't be usable.

Are you somehow adding it to your include path after ./configure? We should probably reject the system gd entirely if gd.h isn't found, but it would be nice to understand how this used to work for you first.

@rs-bc
Copy link

rs-bc commented Mar 15, 2024

PHP-8.3.2 compiles fine with external libgd.
GDLIB_CFLAGS are populated correctly for php-8.3.2 and php-8.3.4 in config.log.

The problem here is the test code, which tries to include gd.h without properly set include path (despite having set a correct library path).

@orlitzky
Copy link
Contributor Author

It would be helpful to know what

$ pkg-config --cflags gdlib

returns on this system. I'm starting to suspect that,

PKG_CHECK_MODULES([GDLIB], [gdlib >= 2.1.0])
PHP_EVAL_LIBLINE($GDLIB_LIBS, GD_SHARED_LIBADD)
PHP_EVAL_INCLINE($GDLIB_CFLAGS)
AC_DEFINE(HAVE_LIBGD, 1, [ ])
PHP_GD_CHECK_VERSION

doesn't actually add GDLIB_CFLAGS to CFLAGS (or CPPFLAGS), in which case we just have to add them to the format check explicitly.

@rs-bc
Copy link

rs-bc commented Mar 15, 2024

$ pkg-config --cflags gdlib
-I/usr/local/include -I/usr/local/include/webp -I/usr/local/include/freetype2 -I/usr/local/include/libpng16

@orlitzky
Copy link
Contributor Author

PHP-8.3.2 compiles fine with external libgd. GDLIB_CFLAGS are populated correctly for php-8.3.2 and php-8.3.4 in config.log.

Ok, thanks, my mistake then. I thought the GDLIB_CFLAGS were added at that point. I'll fix it when I get to work.

@petk
Copy link
Member

petk commented Mar 15, 2024

Probably something like this needs to be done in ext/gd/config.m4 at that check:

    CFLAGS_save="$CFLAGS"
    CFLAGS="$INCLUDES $CFLAGS"
    ... 
    CFLAGS="$CFLAGS_save"

orlitzky added a commit to orlitzky/php-src that referenced this issue Mar 15, 2024
In commit 85e5635, a feature test for the various libgd image formats
was added. That test however erroneously omits the GDLIB_CFLAGS (from
pkg-config) during compilation. This can lead to build failuress and
therefore false negatives from the test.

Here, we add $GDLIB_CFLAGS to $CFLAGS for the duration of the test.

Moreover, we replace $GD_SHARED_LIBADD with $GDLIB_LIBS in the same
test. The variable $GD_SHARED_LIBADD is actually empty when we try to
use it. Fortunately(?), the library flags are appended elsewhere, so
this was not causing a problem. But of course it's better to
explicitly do the right thing.

Closes phpGH-12019
orlitzky added a commit to orlitzky/php-src that referenced this issue Mar 15, 2024
In commit 85e5635, a feature test for the various libgd image formats
was added. That test however erroneously omits the GDLIB_CFLAGS (from
pkg-config) during compilation. This can lead to build failures and
therefore false negatives from the test.

Here, we add $GDLIB_CFLAGS to $CFLAGS for the duration of the test.

Moreover, we replace $GD_SHARED_LIBADD with $GDLIB_LIBS in the same
test. The variable $GD_SHARED_LIBADD is actually empty when we try to
use it. Fortunately(?), the library flags are appended elsewhere, so
this was not causing a problem. But of course it's better to
explicitly do the right thing.

Closes phpGH-12019
@orlitzky
Copy link
Contributor Author

Ok, please test if you can: #13724

orlitzky added a commit to orlitzky/php-src that referenced this issue Mar 16, 2024
In commit 85e5635, a feature test for the various libgd image formats
was added. That test however erroneously omits the GDLIB_CFLAGS (from
pkg-config) during compilation. This can lead to build failures and
therefore false negatives from the test.

Here, we add $GDLIB_CFLAGS to $CFLAGS for the duration of the test.

Closes phpGH-12019
petk added a commit that referenced this issue Mar 18, 2024
* PHP-8.2:
  Fix GH-12019: ext/gd/config.m4: don't forget GDLIB_CFLAGS in feature tests
@petk petk closed this as completed in 0079932 Mar 18, 2024
petk added a commit that referenced this issue Mar 18, 2024
* PHP-8.3:
  Fix GH-12019: ext/gd/config.m4: don't forget GDLIB_CFLAGS in feature tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment