Browse Source

Code review:

. fixed integer overflow
  . fixed buffer overflow/underflow
  . added more strict IP validation
  . reimplemented float number validation
experimental/5.2-WITH_DRCP
Dmitry Stogov 20 years ago
parent
commit
67583e7aa5
  1. 33
      ext/filter/filter.c
  2. 28
      ext/filter/filter_private.h
  3. 620
      ext/filter/logical_filters.c
  4. 8
      ext/filter/tests/030.phpt
  5. 30
      ext/filter/tests/045.phpt
  6. 23
      ext/filter/tests/046.phpt
  7. 37
      ext/filter/tests/047.phpt
  8. 41
      ext/filter/tests/048.phpt
  9. 34
      ext/filter/tests/049.phpt
  10. 29
      ext/filter/tests/050.phpt
  11. 11
      ext/filter/tests/051.phpt
  12. 10
      ext/filter/tests/bug7733.phpt

33
ext/filter/filter.c

@ -331,6 +331,7 @@ static void php_zval_filter(zval **value, long filter, long flags, zval *options
if (zend_hash_find(HASH_OF(options), "default", sizeof("default"), (void **)&tmp) == SUCCESS) { if (zend_hash_find(HASH_OF(options), "default", sizeof("default"), (void **)&tmp) == SUCCESS) {
**value = **tmp; **value = **tmp;
zval_copy_ctor(*value); zval_copy_ctor(*value);
INIT_PZVAL(*value);
} }
} }
} }
@ -518,7 +519,6 @@ PHP_FUNCTION(filter_has_var)
long arg; long arg;
char *var; char *var;
int var_len; int var_len;
zval **tmp;
zval *array_ptr = NULL; zval *array_ptr = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) {
@ -527,7 +527,7 @@ PHP_FUNCTION(filter_has_var)
array_ptr = php_filter_get_storage(arg TSRMLS_CC); array_ptr = php_filter_get_storage(arg TSRMLS_CC);
if (array_ptr && HASH_OF(array_ptr) && zend_hash_find(HASH_OF(array_ptr), var, var_len + 1, (void **)&tmp) == SUCCESS) {
if (array_ptr && HASH_OF(array_ptr) && zend_hash_exists(HASH_OF(array_ptr), var, var_len + 1)) {
RETURN_TRUE; RETURN_TRUE;
} }
@ -542,22 +542,27 @@ static void php_filter_call(zval **filtered, long filter, zval **filter_args, co
char *charset = NULL; char *charset = NULL;
if (filter_args && Z_TYPE_PP(filter_args) != IS_ARRAY) { if (filter_args && Z_TYPE_PP(filter_args) != IS_ARRAY) {
convert_to_long_ex(filter_args);
long lval;
PHP_FILTER_GET_LONG_OPT(filter_args, lval);
if (filter != -1) { /* handler for array apply */ if (filter != -1) { /* handler for array apply */
/* filter_args is the filter_flags */ /* filter_args is the filter_flags */
filter_flags = Z_LVAL_PP(filter_args);
filter_flags = lval;
if (!(filter_flags & FILTER_REQUIRE_ARRAY || filter_flags & FILTER_FORCE_ARRAY)) {
filter_flags |= FILTER_REQUIRE_SCALAR;
}
} else { } else {
filter = Z_LVAL_PP(filter_args);
filter = lval;
} }
} else if (filter_args) { } else if (filter_args) {
if (zend_hash_find(HASH_OF(*filter_args), "filter", sizeof("filter"), (void **)&option) == SUCCESS) { if (zend_hash_find(HASH_OF(*filter_args), "filter", sizeof("filter"), (void **)&option) == SUCCESS) {
convert_to_long(*option);
filter = Z_LVAL_PP(option);
PHP_FILTER_GET_LONG_OPT(option, filter);
} }
if (zend_hash_find(HASH_OF(*filter_args), "flags", sizeof("flags"), (void **)&option) == SUCCESS) { if (zend_hash_find(HASH_OF(*filter_args), "flags", sizeof("flags"), (void **)&option) == SUCCESS) {
convert_to_long(*option);
filter_flags = Z_LVAL_PP(option);
PHP_FILTER_GET_LONG_OPT(option, filter_flags);
if (!(filter_flags & FILTER_REQUIRE_ARRAY || filter_flags & FILTER_FORCE_ARRAY)) { if (!(filter_flags & FILTER_REQUIRE_ARRAY || filter_flags & FILTER_FORCE_ARRAY)) {
filter_flags |= FILTER_REQUIRE_SCALAR; filter_flags |= FILTER_REQUIRE_SCALAR;
@ -708,14 +713,15 @@ PHP_FUNCTION(filter_input)
if (Z_TYPE_PP(filter_args) == IS_LONG) { if (Z_TYPE_PP(filter_args) == IS_LONG) {
filter_flags = Z_LVAL_PP(filter_args); filter_flags = Z_LVAL_PP(filter_args);
} else if (Z_TYPE_PP(filter_args) == IS_ARRAY && zend_hash_find(HASH_OF(*filter_args), "flags", sizeof("flags"), (void **)&option) == SUCCESS) { } else if (Z_TYPE_PP(filter_args) == IS_ARRAY && zend_hash_find(HASH_OF(*filter_args), "flags", sizeof("flags"), (void **)&option) == SUCCESS) {
convert_to_long(*option);
filter_flags = Z_LVAL_PP(option);
PHP_FILTER_GET_LONG_OPT(option, filter_flags);
} else if (Z_TYPE_PP(filter_args) == IS_ARRAY && } else if (Z_TYPE_PP(filter_args) == IS_ARRAY &&
zend_hash_find(HASH_OF(*filter_args), "options", sizeof("options"), (void **)&opt) == SUCCESS && zend_hash_find(HASH_OF(*filter_args), "options", sizeof("options"), (void **)&opt) == SUCCESS &&
Z_TYPE_PP(opt) == IS_ARRAY &&
zend_hash_find(HASH_OF(*opt), "default", sizeof("default"), (void **)&def) == SUCCESS zend_hash_find(HASH_OF(*opt), "default", sizeof("default"), (void **)&def) == SUCCESS
) { ) {
*return_value = **def; *return_value = **def;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
INIT_PZVAL(return_value);
return; return;
} }
} }
@ -728,6 +734,7 @@ PHP_FUNCTION(filter_input)
*return_value = **tmp; *return_value = **tmp;
zval_copy_ctor(return_value); /* Watch out for empty strings */ zval_copy_ctor(return_value); /* Watch out for empty strings */
INIT_PZVAL(return_value);
php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC); php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC);
} }
@ -751,6 +758,7 @@ PHP_FUNCTION(filter_var)
*return_value = *data; *return_value = *data;
zval_copy_ctor(data); zval_copy_ctor(data);
INIT_PZVAL(return_value);
php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC); php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC);
} }
@ -782,8 +790,7 @@ PHP_FUNCTION(filter_input_array)
if (Z_TYPE_PP(op) == IS_LONG) { if (Z_TYPE_PP(op) == IS_LONG) {
filter_flags = Z_LVAL_PP(op); filter_flags = Z_LVAL_PP(op);
} else if (Z_TYPE_PP(op) == IS_ARRAY && zend_hash_find(HASH_OF(*op), "flags", sizeof("flags"), (void **)&option) == SUCCESS) { } else if (Z_TYPE_PP(op) == IS_ARRAY && zend_hash_find(HASH_OF(*op), "flags", sizeof("flags"), (void **)&option) == SUCCESS) {
convert_to_long(*option);
filter_flags = Z_LVAL_PP(option);
PHP_FILTER_GET_LONG_OPT(option, filter_flags);
} }
} }
if (filter_flags & FILTER_NULL_ON_FAILURE) { if (filter_flags & FILTER_NULL_ON_FAILURE) {

28
ext/filter/filter_private.h

@ -97,23 +97,29 @@
} \ } \
return; \ return; \
#define PHP_FILTER_TRIM_DEFAULT(p, len, end) { \
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\v' || *p == '\n') { \
#define PHP_FILTER_TRIM_DEFAULT(p, len) { \
while ((len > 0) && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\v' || *p == '\n')) { \
p++; \ p++; \
len--; \ len--; \
} \ } \
if (len < 1) { \
RETURN_VALIDATION_FAILED \
} \
start = p; \
end = p + len - 1; \
while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\v' || *end == '\n') { \
end--; \
if (len < 1) { \
RETURN_VALIDATION_FAILED \
} \
while (p[len-1] == ' ' || p[len-1] == '\t' || p[len-1] == '\r' || p[len-1] == '\v' || p[len-1] == '\n') { \
len--; \
} \ } \
*(end + 1) = '\0'; \
len = (end - p + 1); \
} }
#define PHP_FILTER_GET_LONG_OPT(zv, opt) { \
if (Z_TYPE_PP(zv) != IS_LONG) { \
zval tmp = **zv; \
zval_copy_ctor(&tmp); \
convert_to_long(&tmp); \
opt = Z_LVAL(tmp); \
} else { \
opt = Z_LVAL_PP(zv); \
} \
}
#endif /* FILTER_PRIVATE_H */ #endif /* FILTER_PRIVATE_H */

620
ext/filter/logical_filters.c

@ -24,10 +24,14 @@
#include "ext/standard/url.h" #include "ext/standard/url.h"
#include "ext/pcre/php_pcre.h" #include "ext/pcre/php_pcre.h"
#include "zend_multiply.h"
#if HAVE_ARPA_INET_H #if HAVE_ARPA_INET_H
# include <arpa/inet.h> # include <arpa/inet.h>
#endif #endif
#define LONG_SIGN_MASK (1L << (8*sizeof(long)-1))
#ifndef INADDR_NONE #ifndef INADDR_NONE
# define INADDR_NONE ((unsigned long int) -1) # define INADDR_NONE ((unsigned long int) -1)
#endif #endif
@ -39,8 +43,7 @@
var_name##_set = 0; \ var_name##_set = 0; \
if (option_array) { \ if (option_array) { \
if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \ if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
convert_to_long(*option_val); \
var_name = Z_LVAL_PP(option_val); \
PHP_FILTER_GET_LONG_OPT(option_val, var_name); \
var_name##_set = 1; \ var_name##_set = 1; \
} \ } \
} }
@ -53,10 +56,11 @@
var_name##_len = 0; \ var_name##_len = 0; \
if (option_array) { \ if (option_array) { \
if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \ if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
convert_to_string(*option_val); \
var_name = Z_STRVAL_PP(option_val); \
var_name##_set = 1; \
var_name##_len = Z_STRLEN_PP(option_val); \
if (Z_TYPE_PP(option_val) == IS_STRING) { \
var_name = Z_STRVAL_PP(option_val); \
var_name##_len = Z_STRLEN_PP(option_val); \
var_name##_set = 1; \
} \
} \ } \
} }
/* }}} */ /* }}} */
@ -65,14 +69,13 @@
#define FORMAT_IPV6 6 #define FORMAT_IPV6 6
static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
long ctx_value = 0;
long ctx_value;
long sign = 1; long sign = 1;
int error = 0;
const char *end;
const char *end = str + str_len;
double dval;
long overflow;
end = str + str_len;
switch(*str) {
switch (*str) {
case '-': case '-':
sign = -1; sign = -1;
case '+': case '+':
@ -82,88 +85,79 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret
} }
/* must start with 1..9*/ /* must start with 1..9*/
if (*str >= '1' && *str <= '9') {
ctx_value += ((*str) - '0');
str++;
if (str < end && *str >= '1' && *str <= '9') {
ctx_value = ((*(str++)) - '0');
} else { } else {
return -1; return -1;
} }
if (str_len == 1 ) {
*ret = ctx_value;
return 1;
}
while (*str) {
while (str < end) {
if (*str >= '0' && *str <= '9') { if (*str >= '0' && *str <= '9') {
ctx_value *= 10;
ctx_value += ((*str) - '0');
str++;
ZEND_SIGNED_MULTIPLY_LONG(ctx_value, 10, ctx_value, dval, overflow);
if (overflow) {
return -1;
}
ctx_value += ((*(str++)) - '0');
if (ctx_value & LONG_SIGN_MASK) {
return -1;
}
} else { } else {
error = 1;
break;
return -1;
} }
} }
/* state "tail" */
if (!error && *str == '\0' && str == end) {
*ret = ctx_value * sign;
return 1;
} else {
return -1;
}
*ret = ctx_value * sign;
return 1;
} }
/* }}} */ /* }}} */
static int php_filter_parse_octal(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ static int php_filter_parse_octal(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
long ctx_value = 0;
int error = 0;
unsigned long ctx_value = 0;
const char *end = str + str_len;
while (*str) {
while (str < end) {
if (*str >= '0' && *str <= '7') { if (*str >= '0' && *str <= '7') {
ctx_value *= 8;
ctx_value += ((*str) - '0');
str++;
unsigned long n = ((*(str++)) - '0');
if ((ctx_value > ((unsigned long)(~(long)0)) / 8) ||
((ctx_value = ctx_value * 8) > ((unsigned long)(~(long)0)) - n)) {
return -1;
}
ctx_value += n;
} else { } else {
error = 1;
break;
return -1;
} }
} }
if (!error && *str == '\0') {
*ret = ctx_value;
return 1;
} else {
return -1;
}
*ret = (long)ctx_value;
return 1;
} }
/* }}} */ /* }}} */
static int php_filter_parse_hex(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ static int php_filter_parse_hex(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
long ctx_value = 0;
int error = 0;
while (*str) {
if ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) {
ctx_value *= 16;
if (*str >= '0' && *str <= '9') {
ctx_value += ((*str) - '0');
} else if (*str >= 'a' && *str <= 'f') {
ctx_value += 10 + ((*str) - 'a');
} else if (*str >= 'A' && *str <= 'F') {
ctx_value += 10 + ((*str) - 'A');
}
str++;
unsigned long ctx_value = 0;
const char *end = str + str_len;
unsigned long n;
while (str < end) {
if (*str >= '0' && *str <= '9') {
n = ((*(str++)) - '0');
} else if (*str >= 'a' && *str <= 'f') {
n = ((*(str++)) - ('a' - 10));
} else if (*str >= 'A' && *str <= 'F') {
n = ((*(str++)) - ('A' - 10));
} else { } else {
error = 1;
break;
return -1;
} }
if ((ctx_value > ((unsigned long)(~(long)0)) / 16) ||
((ctx_value = ctx_value * 16) > ((unsigned long)(~(long)0)) - n)) {
return -1;
}
ctx_value += n;
} }
if (!error && *str == '\0') {
*ret = ctx_value;
return 1;
} else {
return -1;
}
*ret = (long)ctx_value;
return 1;
} }
/* }}} */ /* }}} */
@ -175,7 +169,7 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
int allow_octal = 0, allow_hex = 0; int allow_octal = 0, allow_hex = 0;
int len, error = 0; int len, error = 0;
long ctx_value; long ctx_value;
char *p, *start, *end;
char *p;
/* Parse options */ /* Parse options */
FETCH_LONG_OPTION(min_range, "min_range"); FETCH_LONG_OPTION(min_range, "min_range");
@ -200,12 +194,12 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
p = Z_STRVAL_P(value); p = Z_STRVAL_P(value);
ctx_value = 0; ctx_value = 0;
PHP_FILTER_TRIM_DEFAULT(p, len, end);
PHP_FILTER_TRIM_DEFAULT(p, len);
if (*p == '0') { if (*p == '0') {
p++;
p++; len--;
if (allow_hex && (*p == 'x' || *p == 'X')) { if (allow_hex && (*p == 'x' || *p == 'X')) {
p++;
p++; len--;
if (php_filter_parse_hex(p, len, &ctx_value TSRMLS_CC) < 0) { if (php_filter_parse_hex(p, len, &ctx_value TSRMLS_CC) < 0) {
error = 1; error = 1;
} }
@ -213,7 +207,7 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
if (php_filter_parse_octal(p, len, &ctx_value TSRMLS_CC) < 0) { if (php_filter_parse_octal(p, len, &ctx_value TSRMLS_CC) < 0) {
error = 1; error = 1;
} }
} else if (len != 1) {
} else if (len != 0) {
error = 1; error = 1;
} }
} else { } else {
@ -236,34 +230,65 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{ {
char *str = Z_STRVAL_P(value); char *str = Z_STRVAL_P(value);
char *start, *end;
int len = Z_STRLEN_P(value); int len = Z_STRLEN_P(value);
int ret;
if (len>0) {
PHP_FILTER_TRIM_DEFAULT(str, len, end);
} else {
RETURN_VALIDATION_FAILED
}
PHP_FILTER_TRIM_DEFAULT(str, len);
/* returns true for "1", "true", "on" and "yes" /* returns true for "1", "true", "on" and "yes"
* returns false for "0", "false", "off", "no", and "" * returns false for "0", "false", "off", "no", and ""
* null otherwise. */ * null otherwise. */
if ((strncasecmp(str, "true", sizeof("true")) == 0) ||
(strncasecmp(str, "yes", sizeof("yes")) == 0) ||
(strncasecmp(str, "on", sizeof("on")) == 0) ||
(strncmp(str, "1", sizeof("1")) == 0))
{
zval_dtor(value);
ZVAL_BOOL(value, 1);
} else if ((strncasecmp(str, "false", sizeof("false")) == 0) ||
(strncasecmp(str, "no", sizeof("no")) == 0) ||
(strncasecmp(str, "off", sizeof("off")) == 0) ||
(strncmp(str, "0", sizeof("0")) == 0))
{
zval_dtor(value);
ZVAL_BOOL(value, 0);
} else {
switch (len) {
case 1:
if (*str == '1') {
ret = 1;
} else if (*str == '0') {
ret = 0;
} else {
ret = -1;
}
break;
case 2:
if (strncasecmp(str, "on", 2) == 0) {
ret = 1;
} else if (strncasecmp(str, "no", 2) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
case 3:
if (strncasecmp(str, "yes", 3) == 0) {
ret = 1;
} else if (strncasecmp(str, "off", 3) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
case 4:
if (strncasecmp(str, "true", 4) == 0) {
ret = 1;
} else {
ret = -1;
}
break;
case 5:
if (strncasecmp(str, "false", 5) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
default:
ret = -1;
}
if (ret == -1) {
RETURN_VALIDATION_FAILED RETURN_VALIDATION_FAILED
} else {
zval_dtor(value);
ZVAL_BOOL(value, ret);
} }
} }
/* }}} */ /* }}} */
@ -271,169 +296,102 @@ void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{ {
int len; int len;
char *str, *start, *end;
char *str, *end;
char *num, *p;
zval **option_val; zval **option_val;
char *decimal; char *decimal;
char dec_sep = '\0';
const char default_decimal[] = ".";
int decimal_set, decimal_len; int decimal_set, decimal_len;
char dec_sep = '.';
char tsd_sep[3] = "',."; char tsd_sep[3] = "',.";
long options_flag;
int options_flag_set;
int sign = 1;
double ret_val = 0;
double factor;
long lval;
double dval;
int exp_value = 0, exp_multiply = 1;
int first, n;
len = Z_STRLEN_P(value); len = Z_STRLEN_P(value);
if (len < 1) {
RETURN_VALIDATION_FAILED
}
str = Z_STRVAL_P(value); str = Z_STRVAL_P(value);
PHP_FILTER_TRIM_DEFAULT(str, len, end);
start = str;
if (len == 1) {
if (*str >= '0' && *str <= '9') {
ret_val = (double)*str - '0';
} else if (*str == 'E' || *str == 'e') {
ret_val = 0;
}
zval_dtor(value);
Z_TYPE_P(value) = IS_DOUBLE;
Z_DVAL_P(value) = ret_val;
return;
}
PHP_FILTER_TRIM_DEFAULT(str, len);
end = str + len;
FETCH_STRING_OPTION(decimal, "decimal"); FETCH_STRING_OPTION(decimal, "decimal");
FETCH_LONG_OPTION(options_flag, "flags");
if (decimal_set) { if (decimal_set) {
if (decimal_len > 1) {
if (decimal_len != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "decimal separator must be one char"); php_error_docref(NULL TSRMLS_CC, E_WARNING, "decimal separator must be one char");
RETURN_VALIDATION_FAILED
} else { } else {
dec_sep = *decimal; dec_sep = *decimal;
} }
} else {
dec_sep = *default_decimal;
}
if (*str == '-') {
sign = -1;
str++;
start = str;
} else if (*str == '+') {
sign = 1;
str++;
start = str;
}
ret_val = 0.0;
while (*str == '0') {
str++;
}
if (*str == dec_sep) {
str++;
goto stateDot;
} }
ret_val = 0;
if (str != start) {
str--;
num = p = emalloc(len+1);
if (str < end && (*str == '+' || *str == '-')) {
*p++ = *str++;
} }
while (*str && *str != dec_sep) {
if ((options_flag & FILTER_FLAG_ALLOW_THOUSAND) && (*str == tsd_sep[0] || *str == tsd_sep[1] || *str == tsd_sep[2])) {
str++;
continue;
}
if (*str == 'e' || *str == 'E') {
goto stateExp;
first = 1;
while (1) {
n = 0;
while (str < end && *str >= '0' && *str <= '9') {
++n;
*p++ = *str++;
} }
if (*str < '0' || *str > '9') {
goto stateError;
}
ret_val *=10; ret_val += (*str - '0');
str++;
}
if (!(*str)) {
goto stateT;
}
str++;
stateDot:
factor = 0.1;
while (*str) {
if (*str == 'e' || *str == 'E') {
goto stateExp;
if (str == end || *str == dec_sep || *str == 'e' || *str == 'E') {
if (!first && n != 3) {
goto error;
}
if (*str == dec_sep) {
*p++ = '.';
str++;
while (str < end && *str >= '0' && *str <= '9') {
*p++ = *str++;
}
}
if (*str == 'e' || *str == 'E') {
*p++ = *str++;
if (str < end && (*str == '+' || *str == '-')) {
*p++ = *str++;
}
while (str < end && *str >= '0' && *str <= '9') {
*p++ = *str++;
}
}
break;
} }
if (*str < '0' || *str > '9') {
goto stateError;
if ((flags & FILTER_FLAG_ALLOW_THOUSAND) && (*str == tsd_sep[0] || *str == tsd_sep[1] || *str == tsd_sep[2])) {
if (first?(n < 1 || n > 3):(n != 3)) {
goto error;
}
first = 0;
str++;
} else {
goto error;
} }
ret_val += factor * (*str - '0');
factor /= 10;
str++;
} }
if (!(*str)) {
goto stateT;
if (str != end) {
goto error;
} }
*p = 0;
stateExp:
str++;
switch (*str) {
case '-':
exp_multiply = -1;
str++;
switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
case IS_LONG:
zval_dtor(value);
Z_TYPE_P(value) = IS_DOUBLE;
Z_DVAL_P(value) = lval;
break; break;
case '+':
exp_multiply = 1;
str++;
}
while (*str) {
if (*str < '0' || *str > '9') {
goto stateError;
}
exp_value *= 10;
exp_value += ((*str) - '0');
str++;
}
stateT:
if ((str -1) != end) {
goto stateError;
}
if (exp_value) {
exp_value *= exp_multiply;
ret_val *= pow(10, exp_value);
case IS_DOUBLE:
zval_dtor(value);
Z_TYPE_P(value) = IS_DOUBLE;
Z_DVAL_P(value) = dval;
break;
default:
error:
efree(num);
RETURN_VALIDATION_FAILED
} }
zval_dtor(value);
Z_TYPE_P(value) = IS_DOUBLE;
Z_DVAL_P(value) = sign * ret_val;
return;
stateError:
RETURN_VALIDATION_FAILED
efree(num);
} }
/* }}} */ /* }}} */
@ -533,179 +491,95 @@ void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
static int _php_filter_validate_ipv4(char *str, int str_len, int *ip) /* {{{ */ static int _php_filter_validate_ipv4(char *str, int str_len, int *ip) /* {{{ */
{ {
unsigned long int i = inet_addr(str);
char ip_chk[16];
int l;
if (i == INADDR_NONE) {
if (!strcmp(str, "255.255.255.255")) {
ip[0] = ip[1] = ip[2] = ip[3] = 255;
return 1;
} else {
const char *end = str + str_len;
int num, m;
int n = 0;
while (str < end) {
if (*str < '0' || *str > '9') {
return 0;
}
m = 1;
num = ((*(str++)) - '0');
while (str < end && (*str >= '0' && *str <= '9')) {
num = num * 10 + ((*(str++)) - '0');
if (num > 255 || ++m > 3) {
return 0;
}
}
ip[n++] = num;
if (n == 4) {
return str == end;
} else if (str >= end || *(str++) != '.') {
return 0; return 0;
} }
} }
ip[0] = i & 0xFF;
ip[1] = (i & 0xFF00) / 256;
ip[2] = (i & 0xFF0000) / 256 / 256;
ip[3] = (i & 0xFF000000) / 256 / 256 / 256;
/* make sure that the input does not have any trailing values */
l = sprintf(ip_chk, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
if (l != str_len || strcmp(ip_chk, str)) {
return 0;
}
return 1;
return 0;
} }
/* }}} */ /* }}} */
#define IS_HEX(s) if (!((s >= '0' && s <= '9') || (s >= 'a' && s <= 'f') ||(s >= 'A' && s <= 'F'))) { \
return 0; \
}
#define IPV6_LOOP_IN(str) \
if (*str == ':') { \
if (hexcode_found > 4) { \
return -134; \
} \
hexcode_found = 0; \
col_fnd++; \
} else { \
IS_HEX(*str); \
hexcode_found++; \
}
static int _php_filter_validate_ipv6_(char *str, int str_len TSRMLS_DC) /* {{{ */
static int _php_filter_validate_ipv6(char *str, int str_len TSRMLS_DC) /* {{{ */
{ {
int hexcode_found = 0;
int compressed_2end = 0;
int col_fnd = 0;
char *start = str;
char *compressed = NULL, *t = str;
char *s2 = NULL, *ipv4=NULL;
int compressed = 0;
int blocks = 8;
int n;
char *ipv4;
char *end;
int ip4elm[4]; int ip4elm[4];
if (!memchr(str, ':', str_len)) { if (!memchr(str, ':', str_len)) {
return 0; return 0;
} }
/* Check for compressed expression. only one is allowed */
compressed = php_memnstr(str, "::", sizeof("::")-1, str+str_len);
if (compressed) {
s2 = php_memnstr(compressed+1, "::", sizeof("::")-1, str + str_len);
if (s2) {
return 0;
}
}
/* check for bundled IPv4 */ /* check for bundled IPv4 */
ipv4 = memchr(str, '.', str_len); ipv4 = memchr(str, '.', str_len);
if (ipv4) { if (ipv4) {
while (*ipv4 != ':' && ipv4 >= start) {
while (ipv4 > str && *(ipv4-1) != ':') {
ipv4--; ipv4--;
} }
/* ::w.x.y.z */
if (compressed && ipv4 == (compressed + 1)) {
compressed_2end = 1;
}
ipv4++;
if (!_php_filter_validate_ipv4(ipv4, (str + str_len - ipv4), ip4elm)) {
if (!_php_filter_validate_ipv4(ipv4, (str_len - (ipv4 - str)), ip4elm)) {
return 0; return 0;
} }
if (compressed_2end) {
return 1;
str_len = (ipv4 - str) - 1;
if (str_len == 1) {
return *str == ':';
} }
blocks = 6;
} }
if (!compressed) {
char *end;
if (ipv4) {
end = ipv4 - 1;
} else {
end = str + str_len;
}
while (*str && str <= end) {
IPV6_LOOP_IN(str);
str++;
}
if (!ipv4) {
if (col_fnd != 7) {
return 0;
} else {
return 1;
}
} else {
if (col_fnd != 6) {
return -1230;
} else {
return 1;
}
}
} else {
if (!ipv4) {
t = compressed - 1;
while (t >= start) {
IPV6_LOOP_IN(t);
t--;
}
if (hexcode_found > 4) {
return 0;
}
t = compressed + 2;
hexcode_found = 0;
while (*t) {
IPV6_LOOP_IN(t);
t++;
}
if (hexcode_found > 4) {
return 0;
}
if (col_fnd > 6) {
return 0;
} else {
return 1;
}
} else {
/* ipv4 part always at the end */
t = ipv4 - 1;
while (t >= (compressed + 2)) {
IPV6_LOOP_IN(t);
t--;
}
if (hexcode_found > 4) {
return 0;
}
hexcode_found = 0;
t = compressed - 1;
while (t >= start) {
IPV6_LOOP_IN(t);
t--;
}
if (hexcode_found > 4) {
end = str + str_len;
while (str < end) {
if (*str == ':') {
if (--blocks == 0) {
return 0; return 0;
}
if (col_fnd > 6) {
}
if (++str >= end) {
return 0; return 0;
} else {
return 1;
} }
if (*str == ':') {
if (compressed || --blocks == 0) {
return 0;
}
if (++str == end) {
return 1;
}
compressed = 1;
}
}
n = 0;
while ((str < end) &&
((*str >= '0' && *str <= '9') ||
(*str >= 'a' && *str <= 'f') ||
(*str >= 'A' && *str <= 'F'))) {
n++;
str++;
}
if (n < 1 || n > 4) {
return 0;
} }
} }
return 0;
return (compressed || blocks == 1);
} }
/* }}} */ /* }}} */
@ -770,7 +644,7 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
case FORMAT_IPV6: case FORMAT_IPV6:
{ {
int res = 0; int res = 0;
res = _php_filter_validate_ipv6_(str, Z_STRLEN_P(value) TSRMLS_CC);
res = _php_filter_validate_ipv6(str, Z_STRLEN_P(value) TSRMLS_CC);
if (res < 1) { if (res < 1) {
RETURN_VALIDATION_FAILED RETURN_VALIDATION_FAILED
} }

8
ext/filter/tests/030.phpt

@ -21,7 +21,13 @@ $ipv6_test = array(
"0:0:0:0:0:FFFF:129.144.52.38" => true, "0:0:0:0:0:FFFF:129.144.52.38" => true,
"0:0:0:0:0:0:13.1.68.3" => true, "0:0:0:0:0:0:13.1.68.3" => true,
"::13.1.68.3" => true, "::13.1.68.3" => true,
"::FFFF:129.144.52.38" => true
"::FFFF:129.144.52.38" => true,
"1:2:3:4:5:6::129.144.52.38" => false,
"::1:2:3:4:5:6:129.144.52.38" => false,
"1:2:3::4:5:6:129.144.52.38" => false,
"1:2:3:4:5:6:7:8::" => false,
"::1:2:3:4:5:6:7:8" => false,
"1:2:3:4::5:6:7:8" => false,
); );
foreach ($ipv6_test as $ip => $exp) { foreach ($ipv6_test as $ip => $exp) {
$out = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); $out = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);

30
ext/filter/tests/045.phpt

@ -0,0 +1,30 @@
--TEST--
Options must not be changed by filter_var()
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
$a = array("flags"=>(string)FILTER_FLAG_ALLOW_HEX, "options" => array("min_range"=>"0", "max_range"=>"1024"));
$ret = filter_var("0xff", FILTER_VALIDATE_INT, $a);
echo ($ret === 255 && $a["options"]["min_range"] === "0")?"ok\n":"bug\n";
echo ($ret === 255 && $a["options"]["max_range"] === "1024")?"ok\n":"bug\n";
echo ($ret === 255 && is_string($a["flags"]) && $a["flags"] == FILTER_FLAG_ALLOW_HEX)?"ok\n":"bug\n";
$a = (string)FILTER_FLAG_ALLOW_HEX;
$ret = filter_var("0xff", FILTER_VALIDATE_INT, $a);
echo ($ret === 255 && is_string($a) && $a == FILTER_FLAG_ALLOW_HEX)?"ok\n":"bug\n";
$a = array("test"=>array("filter"=>(string)FILTER_VALIDATE_INT, "flags"=>(string)FILTER_FLAG_ALLOW_HEX));
$ret = filter_var_array(array("test"=>"0xff"), $a);
echo ($ret["test"] === 255 && is_string($a["test"]["filter"]) && $a["test"]["filter"] == FILTER_VALIDATE_INT)?"ok\n":"bug\n";
echo ($ret["test"] === 255 && is_string($a["test"]["flags"]) && $a["test"]["flags"] == FILTER_FLAG_ALLOW_HEX)?"ok\n":"bug\n";
$a = array("test"=>(string)FILTER_VALIDATE_INT);
$ret = filter_var_array(array("test"=>"255"), $a);
echo ($ret["test"] === 255 && is_string($a["test"]) && $a["test"] == FILTER_VALIDATE_INT)?"ok\n":"bug\n";
?>
--EXPECT--
ok
ok
ok
ok
ok
ok
ok

23
ext/filter/tests/046.phpt

@ -0,0 +1,23 @@
--TEST--
Integer overflow
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
$s = sprintf("%d", PHP_INT_MAX);
var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT)));
$s = sprintf("%.0f", PHP_INT_MAX+1);
var_dump(filter_var($s, FILTER_VALIDATE_INT));
$s = sprintf("%d", -PHP_INT_MAX);
var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT)));
$s = sprintf("%.0f", -(PHP_INT_MAX+1));
var_dump(filter_var($s, FILTER_VALIDATE_INT));
?>
--EXPECT--
bool(true)
bool(false)
bool(true)
bool(false)

37
ext/filter/tests/047.phpt

@ -0,0 +1,37 @@
--TEST--
Octal integer overflow
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
function octal_inc($s) {
$len = strlen($s);
while ($len > 0) {
$len--;
if ($s[$len] != '7') {
$s[$len] = $s[$len] + 1;
return $s;
}
$s[$len] = '0';
}
return '1'.$s;
}
$s = sprintf("%o", PHP_INT_MAX);
var_dump(is_long(filter_var('0'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL))));
$s = octal_inc($s);
var_dump(is_long(filter_var('0'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL))));
$s = sprintf("%o", ~0);
var_dump(is_long(filter_var('0'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL))));
$s = octal_inc($s);
var_dump(filter_var('0'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL)));
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(false)

41
ext/filter/tests/048.phpt

@ -0,0 +1,41 @@
--TEST--
Hex integer overflow
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
function hex_inc($s) {
$len = strlen($s);
while ($len > 0) {
$len--;
if ($s[$len] != 'f') {
if ($s[$len] == '9') {
$s[$len] = 'a';
} else {
$s[$len] = $s[$len] + 1;
}
return $s;
}
$s[$len] = '0';
}
return '1'.$s;
}
$s = sprintf("%x", PHP_INT_MAX);
var_dump(is_long(filter_var('0x'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_HEX))));
$s = hex_inc($s);
var_dump(is_long(filter_var('0x'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_HEX))));
$s = sprintf("%x", ~0);
var_dump(is_long(filter_var('0x'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_HEX))));
$s = hex_inc($s);
var_dump(filter_var('0x'.$s, FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_HEX)));
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(false)

34
ext/filter/tests/049.phpt

@ -0,0 +1,34 @@
--TEST--
filter_var() and doubles with thousend separators
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
$test = array(
'0' => 0.0,
'12345678900.1234567165' => 12345678900.1234567165,
'1,234,567,890.1234567165' => 1234567890.1234567165,
'-1,234,567,890.1234567165' => -1234567890.1234567165,
'1234,567,890.1234567165' => false,
'1,234,567,89.1234567165' => false,
'1,234,567,8900.1234567165' => false,
'1.234.567.890.1234567165' => false,
'1,234,567,8900.123,456' => false,
);
foreach ($test as $src => $dst) {
$out = filter_var($src, FILTER_VALIDATE_FLOAT, array("flags"=>FILTER_FLAG_ALLOW_THOUSAND));
if ($dst !== $out) {
if ($out === false) {
echo "$src -> false != $dst\n";
} elseif ($dst === false) {
echo "$src -> $out != false\n";
} else {
echo "$src -> $out != $dst\n";
}
}
}
echo "Ok\n";
?>
--EXPECT--
Ok

29
ext/filter/tests/050.phpt

@ -0,0 +1,29 @@
--TEST--
filter_var() and double overflow/underflow
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
$test = array(
'1e+308' => 1e+308,
'1e+309' => false,
'1e-323' => 1e-323,
'1e-324' => false,
);
foreach ($test as $src => $dst) {
$out = filter_var($src, FILTER_VALIDATE_FLOAT);
if ($dst !== $out) {
if ($out === false) {
echo "$src -> false != $dst\n";
} elseif ($dst === false) {
echo "$src -> $out != false\n";
} else {
echo "$src -> $out != $dst\n";
}
}
}
echo "Ok\n";
?>
--EXPECT--
Ok

11
ext/filter/tests/051.phpt

@ -0,0 +1,11 @@
--TEST--
filter_var() and default values
--SKIPIF--
<?php if (!extension_loaded("filter")) die("skip"); ?>
--FILE--
<?php
$tmp = $default = 321;
var_dump(filter_var("123asd", FILTER_VALIDATE_INT, array("options"=>array("default"=>$default))));
?>
--EXPECT--
int(321)

10
ext/filter/tests/bug7733.phpt

@ -17,13 +17,13 @@ var_dump($out);
--EXPECTF-- --EXPECTF--
array(5) { array(5) {
[0]=> [0]=>
float(0)
bool(false)
[1]=> [1]=>
float(10)
bool(false)
[2]=> [2]=>
float(2)
bool(false)
[3]=> [3]=>
float(0)
bool(false)
[4]=> [4]=>
float(0)
bool(false)
} }
Loading…
Cancel
Save