Skip to content

Fix bug #79075: FFI header parser chokes on comments #17082

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 61 additions & 66 deletions ext/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -4958,107 +4958,102 @@ 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 (*p != '"') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
return NULL;
}
p++;
if (*scope_name) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
}
return NULL;
}
*scope_name = p;
while (1) {
if (*p == '\"') {
*p = 0;
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++;
break;
} else if (*p <= ' ') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
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++;
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_LIB 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_LIB define", filename);
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name);
}
return NULL;
}
p++;
if (*lib) {
if (*target) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB 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_LIB defined twice", filename);
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', %s defined twice", filename, target_name);
}
return NULL;
}
*lib = p;
*target = 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);
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_LIB 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;

code_pos = zend_ffi_skip_ws_and_comments(p, true);
} else {
break;
}
Expand Down
12 changes: 12 additions & 0 deletions ext/ffi/tests/bug79075.h
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions ext/ffi/tests/bug79075.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

FFI::load(__DIR__ . "/bug79075.h");
25 changes: 25 additions & 0 deletions ext/ffi/tests/bug79075.phpt
Original file line number Diff line number Diff line change
@@ -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!
Loading