Merge branch 'PHP-5.6'

* PHP-5.6:
  update NEWS
  fix test
  update NEWS
  Fix bug #70019 - limit extracted files to given directory
  Do not do convert_to_* on unserialize, it messes up references
  Fix #69793 - limit what we accept when unserializing exception
  Fixed bug #70169 (Use After Free Vulnerability in unserialize() with SplDoublyLinkedList)
  Fixed bug #70166 - Use After Free Vulnerability in unserialize() with SPLArrayObject
  ignore signatures for packages too
  Fix bug #70168 - Use After Free Vulnerability in unserialize() with SplObjectStorage
  Fixed bug #69892
  Fix bug #70014 - use RAND_bytes instead of deprecated RAND_pseudo_bytes
  Improved fix for Bug #69441
  Fix bug #70068 (Dangling pointer in the unserialization of ArrayObject items)
  Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
  Fix bug #70081: check types for SOAP variables

Conflicts:
	Zend/zend_exceptions.c
	ext/date/php_date.c
	ext/openssl/openssl.c
	ext/phar/phar_internal.h
	ext/soap/php_http.c
	ext/spl/spl_array.c
	ext/spl/spl_dllist.c
	ext/spl/spl_observer.c
	ext/standard/tests/serialize/bug69152.phpt
	sapi/cli/tests/005.phpt
This commit is contained in:
Stanislav Malyshev 2015-08-04 16:14:24 -07:00
commit 97047e7665
20 changed files with 274 additions and 36 deletions

3
.gitignore vendored
View File

@ -21,6 +21,9 @@
*.tar.gz
*.tar.bz2
*.tar.xz
*.tar.gz.asc
*.tar.bz2.asc
*.tar.xz.asc
.FBCIndex
.FBCLockFolder
.deps

9
Zend/tests/bug70121.phpt Normal file
View File

@ -0,0 +1,9 @@
--TEST--
Bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
--FILE--
<?php
unserialize('O:12:"DateInterval":1:{s:4:"days";O:9:"Exception":7:{s:10:"'."\0".'*'."\0".'message";s:1:"x";s:17:"'."\0".'Exception'."\0".'string";s:1:"A";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";s:1:"a";s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";O:8:"stdClass":0:{}}}');
?>
OK
--EXPECT--
OK

View File

@ -203,7 +203,7 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type,
array_init(&trace);
}
Z_SET_REFCOUNT(trace, 0);
base_ce = i_get_exception_base(&obj);
if (EXPECTED(class_type != zend_ce_parse_error || !(filename = zend_get_compiled_filename()))) {
@ -279,6 +279,32 @@ ZEND_METHOD(exception, __construct)
}
/* }}} */
/* {{{ proto Exception::__wakeup()
Exception unserialize checks */
#define CHECK_EXC_TYPE(name, type) \
zend_read_property(i_get_exception_base(object), (object), name, sizeof(name) - 1, 1, &value); \
if(value && Z_TYPE_P(value) != type) { \
zval *tmp; \
MAKE_STD_ZVAL(tmp); \
ZVAL_STRINGL(tmp, name, sizeof(name)-1, 1); \
Z_OBJ_HANDLER_P(object, unset_property)(object, tmp, 0 TSRMLS_CC); \
zval_ptr_dtor(&tmp); \
}
ZEND_METHOD(exception, __wakeup)
{
zval value;
zval *object = getThis();
CHECK_EXC_TYPE("message", IS_STRING);
CHECK_EXC_TYPE("string", IS_STRING);
CHECK_EXC_TYPE("code", IS_LONG);
CHECK_EXC_TYPE("file", IS_STRING);
CHECK_EXC_TYPE("line", IS_LONG);
CHECK_EXC_TYPE("trace", IS_ARRAY);
CHECK_EXC_TYPE("previous", IS_OBJECT);
}
/* }}} */
/* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Throwable previous]]])
ErrorException constructor */
ZEND_METHOD(error_exception, __construct)
@ -608,7 +634,7 @@ ZEND_METHOD(exception, getTraceAsString)
uint32_t num = 0;
DEFAULT_0_PARAMS;
object = getThis();
base_ce = i_get_exception_base(object);
@ -782,6 +808,7 @@ ZEND_END_ARG_INFO()
static const zend_function_entry default_exception_functions[] = {
ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
ZEND_ME(exception, __wakeup, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
@ -812,7 +839,7 @@ static const zend_function_entry error_exception_functions[] = {
void zend_register_default_exception(void) /* {{{ */
{
zend_class_entry ce;
REGISTER_MAGIC_INTERFACE(throwable, Throwable);
memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
@ -868,7 +895,7 @@ void zend_register_default_exception(void) /* {{{ */
/* }}} */
/* {{{ Deprecated - Use zend_ce_exception directly instead */
ZEND_API zend_class_entry *zend_exception_get_default(void)
ZEND_API zend_class_entry *zend_exception_get_default(void)
{
return zend_ce_exception;
}

View File

@ -14,17 +14,17 @@ var_dump($di);
--EXPECTF--
object(DateInterval)#%d (15) {
["y"]=>
int(2)
int(-1)
["m"]=>
int(0)
int(-1)
["d"]=>
int(0)
int(-1)
["h"]=>
int(6)
int(-1)
["i"]=>
int(8)
int(-1)
["s"]=>
int(0)
int(-1)
["weekday"]=>
int(10)
["weekday_behavior"]=>

View File

@ -14,17 +14,17 @@ var_dump($di);
--EXPECTF--
object(DateInterval)#%d (15) {
["y"]=>
int(2)
int(-1)
["m"]=>
int(0)
int(-1)
["d"]=>
int(0)
int(-1)
["h"]=>
int(6)
int(-1)
["i"]=>
int(8)
int(-1)
["s"]=>
int(0)
int(-1)
["weekday"]=>
int(10)
["weekday_behavior"]=>

View File

@ -5428,7 +5428,6 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
zend_long buffer_length;
zend_string *buffer = NULL;
zval *zstrong_result_returned = NULL;
int strong_result = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z/", &buffer_length, &zstrong_result_returned) == FAILURE) {
return;
@ -5446,7 +5445,6 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
buffer = zend_string_alloc(buffer_length, 0);
#ifdef PHP_WIN32
strong_result = 1;
/* random/urandom equivalent on Windows */
if (php_win32_get_random_bytes((unsigned char*)buffer->val, (size_t) buffer_length) == FAILURE){
zend_string_release(buffer);
@ -5456,7 +5454,7 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
RETURN_FALSE;
}
#else
if ((strong_result = RAND_pseudo_bytes((unsigned char*)ZSTR_VAL(buffer), buffer_length)) < 0) {
if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), buffer_length) <= 0) {
zend_string_release(buffer);
if (zstrong_result_returned) {
ZVAL_FALSE(zstrong_result_returned);
@ -5469,7 +5467,7 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
RETVAL_STR(buffer);
if (zstrong_result_returned) {
ZVAL_BOOL(zstrong_result_returned, strong_result);
ZVAL_BOOL(zstrong_result_returned, 1);
}
}
/* }}} */

View File

@ -524,14 +524,17 @@ static inline void phar_set_inode(phar_entry_info *entry) /* {{{ */
{
char tmp[MAXPATHLEN];
int tmp_len;
size_t len;
size_t len1, len2;
tmp_len = MIN(MAXPATHLEN, entry->filename_len + entry->phar->fname_len);
len = MIN(entry->phar->fname_len, tmp_len);
memcpy(tmp, entry->phar->fname, len);
len = MIN(tmp_len - len, entry->filename_len);
memcpy(tmp + entry->phar->fname_len, entry->filename, len);
entry->inode = (unsigned short)zend_hash_func(tmp, tmp_len);
len1 = MIN(entry->phar->fname_len, tmp_len);
memcpy(tmp, entry->phar->fname, len1);
len2 = MIN(tmp_len - len1, entry->filename_len);
memcpy(tmp + len1, entry->filename, len2);
entry->inode = (unsigned short) zend_hash_func(tmp, tmp_len);
}
/* }}} */

View File

@ -4068,6 +4068,9 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
char *fullpath;
const char *slash;
mode_t mode;
cwd_state new_state;
char *filename;
size_t filename_len;
if (entry->is_mounted) {
/* silently ignore mounted entries */
@ -4077,8 +4080,39 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) {
return SUCCESS;
}
/* strip .. from path and restrict it to be under dest directory */
new_state.cwd = (char*)malloc(2);
new_state.cwd[0] = DEFAULT_SLASH;
new_state.cwd[1] = '\0';
new_state.cwd_length = 1;
if (virtual_file_ex(&new_state, entry->filename, NULL, CWD_EXPAND TSRMLS_CC) != 0 ||
new_state.cwd_length <= 1) {
if (EINVAL == errno && entry->filename_len > 50) {
char *tmp = estrndup(entry->filename, 50);
spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, dest);
efree(tmp);
} else {
spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
}
free(new_state.cwd);
return FAILURE;
}
filename = new_state.cwd + 1;
filename_len = new_state.cwd_length - 1;
#ifdef PHP_WIN32
/* unixify the path back, otherwise non zip formats might be broken */
{
int cnt = filename_len;
len = spprintf(&fullpath, 0, "%s/%s", dest, entry->filename);
do {
if ('\\' == filename[cnt]) {
filename[cnt] = '/';
}
} while (cnt-- >= 0);
}
#endif
len = spprintf(&fullpath, 0, "%s/%s", dest, filename);
if (len >= MAXPATHLEN) {
char *tmp;
@ -4092,18 +4126,21 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s...\", extracted filename is too long for filesystem", entry->filename, fullpath);
}
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
if (!len) {
spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
if (PHAR_OPENBASEDIR_CHECKPATH(fullpath)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
@ -4111,14 +4148,15 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
if (!overwrite && SUCCESS == php_stream_stat_path(fullpath, &ssb)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", path already exists", entry->filename, fullpath);
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
/* perform dirname */
slash = zend_memrchr(entry->filename, '/', entry->filename_len);
slash = zend_memrchr(filename, '/', filename_len);
if (slash) {
fullpath[dest_len + (slash - entry->filename) + 1] = '\0';
fullpath[dest_len + (slash - filename) + 1] = '\0';
} else {
fullpath[dest_len] = '\0';
}
@ -4128,23 +4166,27 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK, PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
} else {
if (!php_stream_mkdir(fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
efree(fullpath);
free(new_state.cwd);
return FAILURE;
}
}
}
if (slash) {
fullpath[dest_len + (slash - entry->filename) + 1] = '/';
fullpath[dest_len + (slash - filename) + 1] = '/';
} else {
fullpath[dest_len] = '/';
}
filename = NULL;
free(new_state.cwd);
/* it is a standalone directory, job done */
if (entry->is_dir) {
efree(fullpath);

View File

@ -0,0 +1,22 @@
--TEST--
Bug #70019 Files extracted from archive may be placed outside of destination directory
--FILE--
<?php
$dir = __DIR__."/bug70019";
$phar = new PharData(__DIR__."/bug70019.zip");
if(!is_dir($dir)) {
mkdir($dir);
}
$phar->extractTo($dir);
var_dump(file_exists("$dir/ThisIsATestFile.txt"));
?>
===DONE===
--CLEAN--
<?php
$dir = __DIR__."/bug70019";
unlink("$dir/ThisIsATestFile.txt");
rmdir($dir);
?>
--EXPECTF--
bool(true)
===DONE===

BIN
ext/phar/tests/bug70019.zip Normal file

Binary file not shown.

View File

@ -815,10 +815,11 @@ try_again:
zend_hash_internal_pointer_reset(Z_ARRVAL_P(cookies));
smart_str_append_const(&soap_headers, "Cookie: ");
for (i = 0; i < n; i++) {
ulong numindx;
int res = zend_hash_get_current_key(Z_ARRVAL_P(cookies), &key, &numindx);
data = zend_hash_get_current_data(Z_ARRVAL_P(cookies));
zend_hash_get_current_key(Z_ARRVAL_P(cookies), &key, NULL);
if (Z_TYPE_P(data) == IS_ARRAY) {
if (res == HASH_KEY_IS_STRING && Z_TYPE_P(data) == IS_ARRAY) {
zval *value;
if ((value = zend_hash_index_find(Z_ARRVAL_P(data), 0)) != NULL &&

View File

@ -1741,6 +1741,7 @@ SPL_METHOD(Array, unserialize)
goto outexcept;
}
var_push_dtor(&var_hash, &pflags);
--p; /* for ';' */
flags = Z_LVAL(zflags);
/* flags needs to be verified and we also need to verify whether the next
@ -1763,6 +1764,7 @@ SPL_METHOD(Array, unserialize)
if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash)) {
goto outexcept;
}
var_push_dtor(&var_hash, &intern->array);
}
if (*p != ';') {
goto outexcept;
@ -1781,6 +1783,7 @@ SPL_METHOD(Array, unserialize)
goto outexcept;
}
var_push_dtor(&var_hash, &pmembers);
/* copy members */
object_properties_load(&intern->std, Z_ARRVAL(members));
zval_ptr_dtor(&members);
@ -1788,6 +1791,9 @@ SPL_METHOD(Array, unserialize)
/* done reading $serialized */
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if (pflags) {
zval_ptr_dtor(&pflags);
}
return;
outexcept:

View File

@ -809,6 +809,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
goto outexcept;
}
var_push_dtor(&var_hash, &pcount);
--p; /* for ';' */
count = Z_LVAL(pcount);
@ -879,6 +880,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
goto outexcept;
}
var_push_dtor(&var_hash, &pmembers);
/* copy members */
if (!intern->std.properties) {
rebuild_object_properties(&intern->std);

View File

@ -0,0 +1,9 @@
--TEST--
Bug #70068 (Dangling pointer in the unserialization of ArrayObject items)
--FILE--
<?php
$a = unserialize('a:3:{i:0;C:11:"ArrayObject":20:{x:i:0;r:3;;m:a:0:{};}i:1;d:11;i:2;S:31:"AAAAAAAABBBBCCCC\01\00\00\00\04\00\00\00\00\00\00\00\00\00\00";}');
?>
OK
--EXPECT--
OK

View File

@ -0,0 +1,29 @@
--TEST--
SPL: Bug #70166 Use After Free Vulnerability in unserialize() with SPLArrayObject
--FILE--
<?php
$inner = 'x:i:1;a:0:{};m:a:0:{}';
$exploit = 'a:2:{i:0;C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}i:1;R:5;}';
$data = unserialize($exploit);
for($i = 0; $i < 5; $i++) {
$v[$i] = 'hi'.$i;
}
var_dump($data);
?>
===DONE===
--EXPECTF--
array(2) {
[0]=>
object(ArrayObject)#%d (1) {
["storage":"ArrayObject":private]=>
array(0) {
}
}
[1]=>
array(0) {
}
}
===DONE===

View File

@ -0,0 +1,36 @@
--TEST--
SPL: Bug #70168 Use After Free Vulnerability in unserialize() with SplObjectStorage
--FILE--
<?php
$inner = 'x:i:1;O:8:"stdClass":0:{};m:a:0:{}';
$exploit = 'a:2:{i:0;C:16:"SplObjectStorage":'.strlen($inner).':{'.$inner.'}i:1;R:3;}';
$data = unserialize($exploit);
for($i = 0; $i < 5; $i++) {
$v[$i] = 'hi'.$i;
}
var_dump($data);
?>
===DONE===
--EXPECTF--
array(2) {
[0]=>
object(SplObjectStorage)#%d (1) {
["storage":"SplObjectStorage":private]=>
array(1) {
["%s"]=>
array(2) {
["obj"]=>
object(stdClass)#2 (0) {
}
["inf"]=>
NULL
}
}
}
[1]=>
int(1)
}
===DONE===

View File

@ -0,0 +1,30 @@
--TEST--
SPL: Bug #70169 Use After Free Vulnerability in unserialize() with SplDoublyLinkedList
--FILE--
<?php
$inner = 'i:1;';
$exploit = 'a:2:{i:0;C:19:"SplDoublyLinkedList":'.strlen($inner).':{'.$inner.'}i:1;R:3;}';
$data = unserialize($exploit);
for($i = 0; $i < 5; $i++) {
$v[$i] = 'hi'.$i;
}
var_dump($data);
?>
===DONE===
--EXPECTF--
array(2) {
[0]=>
object(SplDoublyLinkedList)#%d (2) {
["flags":"SplDoublyLinkedList":private]=>
int(1)
["dllist":"SplDoublyLinkedList":private]=>
array(0) {
}
}
[1]=>
int(1)
}
===DONE===

View File

@ -9,6 +9,7 @@ $x->test();
?>
--EXPECTF--
Notice: Undefined property: Exception::$previous in %s on line %d
Exception in %s:%d
Stack trace:
#0 {main}

View File

@ -0,0 +1,17 @@
--TEST--
Bug #69793: Remotely triggerable stack exhaustion via recursive method calls
--FILE--
<?php
$e = unserialize('O:9:"Exception":7:{s:17:"'."\0".'Exception'."\0".'string";s:1:"a";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";R:1;s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";i:10;s:10:"'."\0".'*'."\0".'message";N;}');
var_dump($e."");
?>
--EXPECTF--
Notice: Undefined property: Exception::$message in %s/bug69793.php on line %d
Notice: Undefined property: Exception::$file in %s/bug69793.php on line %d
Notice: Undefined property: Exception::$previous in %s/bug69793.php on line %d
string(53) "exception 'Exception' in :1337
Stack trace:
#0 {main}"

View File

@ -4,7 +4,7 @@ show information about class
<?php
include "skipif.inc";
if (!extension_loaded("reflection")) {
die("skip reflection extension required");
die("skip reflection extension required");
}
?>
--FILE--
@ -18,7 +18,7 @@ var_dump(`"$php" -n --rc exception`);
echo "Done\n";
?>
--EXPECTF--
--EXPECTF--
string(40) "Exception: Class unknown does not exist
"
string(183) "Class [ <internal:Core> class stdClass ] {
@ -61,7 +61,7 @@ string(1544) "Class [ <internal:Core> class Exception implements Throwable ] {
Property [ <default> private $previous ]
}
- Methods [10] {
- Methods [11] {
Method [ <internal:Core> final private method __clone ] {
}
@ -74,6 +74,9 @@ string(1544) "Class [ <internal:Core> class Exception implements Throwable ] {
}
}
Method [ <internal:Core, prototype Throwable> final public method __wakeup ] {
}
Method [ <internal:Core, prototype Throwable> final public method getMessage ] {
}