diff --git a/ext/zlib/tests/zlib_filter_deflate_param_errors.phpt b/ext/zlib/tests/zlib_filter_deflate_param_errors.phpt new file mode 100644 index 000000000000..6f4ace544c6e --- /dev/null +++ b/ext/zlib/tests/zlib_filter_deflate_param_errors.phpt @@ -0,0 +1,107 @@ +--TEST-- +zlib.deflate filter param errors +--EXTENSIONS-- +zlib +--FILE-- + 'not an int']; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['memory' => 0]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['memory' => 10]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['window' => 'not an int']; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['window' => -16]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['window' => 32]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['level' => 'not an int']; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['level' => -2]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +$param = ['level' => 10]; +var_dump(stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $param)); + +fclose($fp); + +?> +--EXPECTF-- +Warning: stream_filter_append(): Filter parameters for zlib.deflate filter must be of type array|int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Compression level must be between -1 and 9, -2 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Compression level must be between -1 and 9, 10 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Memory level must be between 1 and 9, 0 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Memory level must be between 1 and 9, 10 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be between -15 and 31, -16 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be between -15 and 31, 32 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Compression level must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Compression level must be between -1 and 9, -2 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Compression level must be between -1 and 9, 10 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s on line %d +bool(false) diff --git a/ext/zlib/tests/zlib_filter_inflate_param_errors.phpt b/ext/zlib/tests/zlib_filter_inflate_param_errors.phpt new file mode 100644 index 000000000000..0189f31c7640 --- /dev/null +++ b/ext/zlib/tests/zlib_filter_inflate_param_errors.phpt @@ -0,0 +1,43 @@ +--TEST-- +zlib.inflate filter param errors +--EXTENSIONS-- +zlib +--FILE-- + 'not an int']; +var_dump(stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_WRITE, $param)); + +$param = ['window' => -16]; +var_dump(stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_WRITE, $param)); + +$param = ['window' => 48]; +var_dump(stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_WRITE, $param)); + +fclose($fp); + +?> +--EXPECTF-- +Warning: stream_filter_append(): Filter parameters for zlib.inflate filter must be of type array, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.inflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.inflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be between -15 and 47, -16 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.inflate" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Window size must be between -15 and 47, 48 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.inflate" in %s on line %d +bool(false) diff --git a/ext/zlib/tests/zlib_filter_unknown_errors.phpt b/ext/zlib/tests/zlib_filter_unknown_errors.phpt new file mode 100644 index 000000000000..9f8af0391a30 --- /dev/null +++ b/ext/zlib/tests/zlib_filter_unknown_errors.phpt @@ -0,0 +1,16 @@ +--TEST-- +zlib.deflate filter param errors +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECTF-- +Warning: stream_filter_append(): Unable to create or locate filter "zlib.unknown" in %s on line %d +bool(false) diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index 2d0e4fbb7fa4..acecb8e13c94 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -24,9 +24,9 @@ typedef struct _php_zlib_filter_data { size_t inbuf_len; unsigned char *outbuf; size_t outbuf_len; - int persistent; - bool finished; /* for zlib.deflate: signals that no flush is pending */ int windowBits; + bool persistent; + bool finished; /* for zlib.deflate: signals that no flush is pending */ } php_zlib_filter_data; /* }}} */ @@ -351,22 +351,9 @@ static const php_stream_filter_ops php_zlib_deflate_ops = { }; /* }}} */ - -/* {{{ zlib.* common factory */ - -static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, bool persistent) +static php_zlib_filter_data *php_zlib_filter_data_new(bool persistent) { - const php_stream_filter_ops *fops = NULL; - php_stream_filter_seekable_t write_seekable; - php_zlib_filter_data *data; - int status; - - if (php_stream_filter_parse_write_seek_mode(filterparams, &write_seekable) == FAILURE) { - return NULL; - } - - /* Create this filter */ - data = pecalloc(1, sizeof(php_zlib_filter_data), persistent); + php_zlib_filter_data *data = pecalloc(1, sizeof(php_zlib_filter_data), persistent); if (!data) { php_error_docref(NULL, E_WARNING, "Failed allocating %zd bytes", sizeof(php_zlib_filter_data)); return NULL; @@ -395,115 +382,192 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f data->strm.data_type = Z_ASCII; data->persistent = persistent; + return data; +} - if (strcasecmp(filtername, "zlib.inflate") == 0) { - int windowBits = -MAX_WBITS; +static php_stream_filter *php_zlib_deflate_filter_create(zval *filter_params, bool persistent) +{ + php_stream_filter_seekable_t write_seekable = PSFS_SEEKABLE_ALWAYS; + /* RFC 1951 Deflate */ + int level = Z_DEFAULT_COMPRESSION; + int windowBits = -MAX_WBITS; + int memLevel = MAX_MEM_LEVEL; - if (filterparams) { - zval *tmpzval; - if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) && - (tmpzval = zend_hash_str_find_ind(HASH_OF(filterparams), "window", sizeof("window") - 1))) { - /* log-2 base of history window (9 - 15) */ - zend_long tmp = zval_get_long(tmpzval); - if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 32) { - php_error_docref(NULL, E_WARNING, "Invalid parameter given for window size (" ZEND_LONG_FMT ")", tmp); - } else { - windowBits = tmp; - } - } - } + if (filter_params) { + zend_long level_long; - /* Save configuration for reset */ - data->windowBits = windowBits; + /* filterparams can either be a scalar value to indicate compression level (shortcut method) + Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */ - /* RFC 1951 Inflate */ - data->finished = false; - status = inflateInit2(&(data->strm), windowBits); - fops = &php_zlib_inflate_ops; - } else if (strcasecmp(filtername, "zlib.deflate") == 0) { - /* RFC 1951 Deflate */ - int level = Z_DEFAULT_COMPRESSION; - int windowBits = -MAX_WBITS; - int memLevel = MAX_MEM_LEVEL; - - - if (filterparams) { - zval *tmpzval; - zend_long tmp; - - /* filterparams can either be a scalar value to indicate compression level (shortcut method) - Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */ - - switch (Z_TYPE_P(filterparams)) { - case IS_ARRAY: - case IS_OBJECT: { - HashTable *ht = HASH_OF(filterparams); - - if ((tmpzval = zend_hash_str_find_ind(ht, "memory", sizeof("memory") -1))) { - /* Memory Level (1 - 9) */ - tmp = zval_get_long(tmpzval); - if (tmp < 1 || tmp > MAX_MEM_LEVEL) { - php_error_docref(NULL, E_WARNING, "Invalid parameter given for memory level (" ZEND_LONG_FMT ")", tmp); - } else { - memLevel = tmp; - } - } + switch (Z_TYPE_P(filter_params)) { + case IS_OBJECT: + case IS_ARRAY: { + const HashTable *ht = HASH_OF(filter_params); + if (php_stream_filter_parse_write_seek_mode(filter_params, &write_seekable) == FAILURE) { + return NULL; + } - if ((tmpzval = zend_hash_str_find_ind(ht, "window", sizeof("window") - 1))) { - /* log-2 base of history window (9 - 15) */ - tmp = zval_get_long(tmpzval); - if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 16) { - php_error_docref(NULL, E_WARNING, "Invalid parameter given for window size (" ZEND_LONG_FMT ")", tmp); - } else { - windowBits = tmp; - } + const zval *memory_zv = zend_hash_str_find_ind(ht, ZEND_STRL("memory")); + if (memory_zv) { + bool failed = false; + /* Memory Level (1 - 9) */ + const zend_long memory = zval_try_get_long(memory_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Window size must be of type int, %s given", zend_zval_type_name(memory_zv)); + return NULL; + } else if (memory < 1 || memory > MAX_MEM_LEVEL) { + php_error_docref(NULL, E_WARNING, "Memory level must be between 1 and %d, " ZEND_LONG_FMT " given", MAX_MEM_LEVEL, memory); + return NULL; + } else { + memLevel = (int) memory; } + } - if ((tmpzval = zend_hash_str_find_ind(ht, "level", sizeof("level") - 1))) { - tmp = zval_get_long(tmpzval); + const zval *window_zv = zend_hash_str_find_ind(ht, ZEND_STRL("window")); + if (window_zv) { + bool failed = false; + /* log-2 base of history window (9 - 15) */ + const zend_long window = zval_try_get_long(window_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Window size must be of type int, %s given", zend_zval_type_name(window_zv)); + return NULL; + } else if (window < -MAX_WBITS || window > MAX_WBITS + 16) { + php_error_docref(NULL, E_WARNING, "Window size must be between %d and %d, " ZEND_LONG_FMT " given", -MAX_WBITS, MAX_WBITS + 16, window); + return NULL; + } else { + windowBits = (int) window; + } + } - /* Pseudo pass through to catch level validating code */ - goto factory_setlevel; + const zval *level_zv = zend_hash_str_find_ind(ht, ZEND_STRL("level")); + if (level_zv) { + bool failed = false; + level_long = zval_try_get_long(level_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Compression level must be of type int, %s given", zend_zval_type_name(level_zv)); + return NULL; } - break; + + /* Pseudo pass through to catch level validating code */ + goto factory_setlevel; + } + break; + } + case IS_STRING: + case IS_DOUBLE: + case IS_LONG: { + bool failed = false; + level_long = zval_try_get_long(filter_params, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, + "Filter parameters for zlib.deflate filter must be of type array|int, %s given", + zend_zval_type_name(filter_params) + ); + return NULL; } - case IS_STRING: - case IS_DOUBLE: - case IS_LONG: - tmp = zval_get_long(filterparams); factory_setlevel: - /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */ - if (tmp < -1 || tmp > 9) { - php_error_docref(NULL, E_WARNING, "Invalid compression level specified. (" ZEND_LONG_FMT ")", tmp); - } else { - level = tmp; - } - break; - default: - php_error_docref(NULL, E_WARNING, "Invalid filter parameter, ignored"); + /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */ + if (level_long < -1 || level_long > 9) { + php_error_docref(NULL, E_WARNING, "Compression level must be between -1 and 9, " ZEND_LONG_FMT " given", level_long); + return NULL; + } else { + level = (int) level_long; + } + break; } + + default: + php_error_docref(NULL, E_WARNING, + "Filter parameters for zlib.deflate filter must be of type array|int, %s given", + zend_zval_type_name(filter_params) + ); + return NULL; } + } - /* Save configuration for reset */ - data->windowBits = windowBits; + php_zlib_filter_data *data = php_zlib_filter_data_new(persistent); + /* Save configuration for reset */ + data->windowBits = windowBits; - status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0); - data->finished = true; - fops = &php_zlib_deflate_ops; - } else { - status = Z_DATA_ERROR; + const int status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0); + if (UNEXPECTED(status != Z_OK)) { + /* Unspecified (probably strm) error, let stream-filter error do its own whining */ + pefree(data->strm.next_in, persistent); + pefree(data->strm.next_out, persistent); + pefree(data, persistent); + return NULL; } + data->finished = true; - if (status != Z_OK) { + return php_stream_filter_alloc(&php_zlib_deflate_ops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +} + + +static php_stream_filter *php_zlib_inflate_filter_create(zval *filter_params, bool persistent) +{ + php_stream_filter_seekable_t write_seekable = PSFS_SEEKABLE_ALWAYS; + int windowBits = -MAX_WBITS; + + if (filter_params) { + if (UNEXPECTED(Z_TYPE_P(filter_params) != IS_ARRAY && Z_TYPE_P(filter_params) != IS_OBJECT)) { + php_error_docref(NULL, E_WARNING, + "Filter parameters for zlib.inflate filter must be of type array, %s given", + zend_zval_type_name(filter_params) + ); + return NULL; + } + const HashTable *filter_params_ht = HASH_OF(filter_params); + /* TODO: convert php_stream_filter_parse_write_seek_mode() to take HashTable */ + if (php_stream_filter_parse_write_seek_mode(filter_params, &write_seekable) == FAILURE) { + return NULL; + } + + const zval *window_zv = zend_hash_str_find_ind(filter_params_ht, ZEND_STRL("window")); + if (window_zv) { + bool failed = false; + /* log-2 base of history window (9 - 15) */ + const zend_long window = zval_try_get_long(window_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Window size must be of type int, %s given", zend_zval_type_name(window_zv)); + return NULL; + } else if (window < -MAX_WBITS || window > MAX_WBITS + 32) { + php_error_docref(NULL, E_WARNING, "Window size must be between %d and %d, " ZEND_LONG_FMT " given", -MAX_WBITS, MAX_WBITS + 32, window); + return NULL; + } else { + windowBits = (int) window; + } + } + } + + php_zlib_filter_data *data = php_zlib_filter_data_new(persistent); + /* Save configuration for reset */ + data->windowBits = windowBits; + + const int status = inflateInit2(&(data->strm), windowBits); + if (UNEXPECTED(status != Z_OK)) { /* Unspecified (probably strm) error, let stream-filter error do its own whining */ pefree(data->strm.next_in, persistent); pefree(data->strm.next_out, persistent); pefree(data, persistent); return NULL; } + /* RFC 1951 Inflate */ + data->finished = false; + + return php_stream_filter_alloc(&php_zlib_inflate_ops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +} - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +/* {{{ zlib.* common factory */ +static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, bool persistent) +{ + if (strcasecmp(filtername, "zlib.inflate") == 0) { + return php_zlib_inflate_filter_create(filterparams, persistent); + } else if (strcasecmp(filtername, "zlib.deflate") == 0) { + return php_zlib_deflate_filter_create(filterparams, persistent); + } else { + return NULL; + } } const php_stream_filter_factory php_zlib_filter_factory = {