mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix bug #79075: FFI header parser chokes on comments
The directives for FFI should be first in the file, which is fine, however sometimes there can be comments or whitespace before or between these defines. One practical example is for license information or when a user adds newlines "by accident". In these cases, it's quite confusing that the directives do not work properly. To solve this, make the zend_ffi_parse_directives() aware of comments. Closes GH-17082.
This commit is contained in:
3
NEWS
3
NEWS
@@ -5,6 +5,9 @@ PHP NEWS
|
||||
- DBA:
|
||||
. Skip test if inifile is disabled. (orlitzky)
|
||||
|
||||
- FFI:
|
||||
. Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos)
|
||||
|
||||
- Iconv:
|
||||
. Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos)
|
||||
|
||||
|
||||
131
ext/ffi/ffi.c
131
ext/ffi/ffi.c
@@ -4958,38 +4958,85 @@ ZEND_METHOD(FFI_CType, getFuncParameterType) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static char *zend_ffi_skip_ws_and_comments(char *p, bool allow_standalone_newline)
|
||||
{
|
||||
while (true) {
|
||||
if (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
} else if (allow_standalone_newline && (*p == '\r' || *p == '\n' || *p == '\f' || *p == '\v')) {
|
||||
p++;
|
||||
} else if (allow_standalone_newline && *p == '/' && p[1] == '/') {
|
||||
p += 2;
|
||||
while (*p && *p != '\r' && *p != '\n') {
|
||||
p++;
|
||||
}
|
||||
} else if (*p == '/' && p[1] == '*') {
|
||||
p += 2;
|
||||
while (*p && (*p != '*' || p[1] != '/')) {
|
||||
p++;
|
||||
}
|
||||
if (*p == '*') {
|
||||
p++;
|
||||
if (*p == '/') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, bool preload) /* {{{ */
|
||||
{
|
||||
char *p;
|
||||
|
||||
code_pos = zend_ffi_skip_ws_and_comments(code_pos, true);
|
||||
|
||||
*scope_name = NULL;
|
||||
*lib = NULL;
|
||||
while (*code_pos == '#') {
|
||||
if (strncmp(code_pos, "#define FFI_SCOPE", sizeof("#define FFI_SCOPE") - 1) == 0
|
||||
&& (code_pos[sizeof("#define FFI_SCOPE") - 1] == ' '
|
||||
|| code_pos[sizeof("#define FFI_SCOPE") - 1] == '\t')) {
|
||||
p = code_pos + sizeof("#define FFI_SCOPE");
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
if (strncmp(code_pos, ZEND_STRL("#define")) == 0) {
|
||||
p = zend_ffi_skip_ws_and_comments(code_pos + sizeof("#define") - 1, false);
|
||||
|
||||
char **target = NULL;
|
||||
const char *target_name = NULL;
|
||||
if (strncmp(p, ZEND_STRL("FFI_SCOPE")) == 0) {
|
||||
p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_SCOPE") - 1, false);
|
||||
target = scope_name;
|
||||
target_name = "FFI_SCOPE";
|
||||
} else if (strncmp(p, ZEND_STRL("FFI_LIB")) == 0) {
|
||||
p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_LIB") - 1, false);
|
||||
target = lib;
|
||||
target_name = "FFI_LIB";
|
||||
} else {
|
||||
while (*p && *p != '\n' && *p != '\r') {
|
||||
p++;
|
||||
}
|
||||
code_pos = zend_ffi_skip_ws_and_comments(p, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*p != '"') {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
if (*scope_name) {
|
||||
if (*target) {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', %s defined twice", filename, target_name);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', %s defined twice", filename, target_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
*scope_name = p;
|
||||
*target = p;
|
||||
while (1) {
|
||||
if (*p == '\"') {
|
||||
*p = 0;
|
||||
@@ -4997,68 +5044,16 @@ static char *zend_ffi_parse_directives(const char *filename, char *code_pos, cha
|
||||
break;
|
||||
} else if (*p <= ' ') {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
while (*p == '\r' || *p == '\n') {
|
||||
p++;
|
||||
}
|
||||
code_pos = p;
|
||||
} else if (strncmp(code_pos, "#define FFI_LIB", sizeof("#define FFI_LIB") - 1) == 0
|
||||
&& (code_pos[sizeof("#define FFI_LIB") - 1] == ' '
|
||||
|| code_pos[sizeof("#define FFI_LIB") - 1] == '\t')) {
|
||||
p = code_pos + sizeof("#define FFI_LIB");
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
if (*p != '"') {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
if (*lib) {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
*lib = p;
|
||||
while (1) {
|
||||
if (*p == '\"') {
|
||||
*p = 0;
|
||||
p++;
|
||||
break;
|
||||
} else if (*p <= ' ') {
|
||||
if (preload) {
|
||||
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
while (*p == '\r' || *p == '\n') {
|
||||
p++;
|
||||
}
|
||||
code_pos = p;
|
||||
|
||||
code_pos = zend_ffi_skip_ws_and_comments(p, true);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
12
ext/ffi/tests/bug79075.h
Normal file
12
ext/ffi/tests/bug79075.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Multiline comment
|
||||
*/
|
||||
// whitespace line
|
||||
|
||||
#define ignore_this_line 1
|
||||
//
|
||||
#define/* inline */FFI_SCOPE /* multi-
|
||||
line */ "bug79075" /* end
|
||||
*/
|
||||
|
||||
int printf(const char *format, ...);
|
||||
3
ext/ffi/tests/bug79075.inc
Normal file
3
ext/ffi/tests/bug79075.inc
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
FFI::load(__DIR__ . "/bug79075.h");
|
||||
25
ext/ffi/tests/bug79075.phpt
Normal file
25
ext/ffi/tests/bug79075.phpt
Normal file
@@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Bug #79075 (FFI header parser chokes on comments)
|
||||
--EXTENSIONS--
|
||||
ffi
|
||||
opcache
|
||||
posix
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows');
|
||||
if (posix_geteuid() == 0) die('skip Cannot run test as root.');
|
||||
?>
|
||||
--INI--
|
||||
ffi.enable=1
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.optimization_level=-1
|
||||
opcache.preload={PWD}/bug79075.inc
|
||||
opcache.file_cache_only=0
|
||||
--FILE--
|
||||
<?php
|
||||
$ffi = FFI::scope("bug79075");
|
||||
$ffi->printf("Hello World from %s!\n", "PHP");
|
||||
?>
|
||||
--EXPECT--
|
||||
Hello World from PHP!
|
||||
Reference in New Issue
Block a user