summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_ps_codec.c
diff options
context:
space:
mode:
authorAndrey Hristov <andrey@php.net>2010-09-22 11:38:49 +0000
committerAndrey Hristov <andrey@php.net>2010-09-22 11:38:49 +0000
commit1de4cfb791f098d69368b4fab53157024f49b4fa (patch)
treef5b2dc94a2ea3618a6a27ee85ff0495505c57097 /ext/mysqlnd/mysqlnd_ps_codec.c
parent047f0e4d883fda046af18de1489ab6b15f341f5e (diff)
downloadphp-git-1de4cfb791f098d69368b4fab53157024f49b4fa.tar.gz
Fix for
Bug #52891 Wrong data inserted with mysqli/mysqlnd when using bind_param,value>LONG_MAX
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps_codec.c')
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c126
1 files changed, 94 insertions, 32 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index dd6f7a620c..dc09bb03b4 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -608,12 +608,46 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
size_t data_size = 0;
zval **copies = NULL;/* if there are different types */
enum_func_status ret = FAIL;
+ int resend_types_next_time = 0;
DBG_ENTER("mysqlnd_stmt_execute_store_params");
/* 1. Store type information */
- if (stmt->send_types_to_server) {
+ /*
+ check if need to send the types even if stmt->send_types_to_server is 0. This is because
+ if we send "i" (42) then the type will be int and the server will expect int. However, if next
+ time we try to send > LONG_MAX, the conversion to string will send a string and the server
+ won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
+ occur, and force resend for the next execution.
+
+ */
+ for (i = 0; i < stmt->param_count; i++) {
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL &&
+ (stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG))
+ {
+ /* always copy the var, because we do many conversions */
+ if (PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(stmt->error_info);
+ goto end;
+ }
+ /*
+ if it doesn't fit in a long send it as a string.
+ Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ */
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ convert_to_double_ex(&tmp_data);
+ if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
+ stmt->send_types_to_server = resend_types_next_time = 1;
+ }
+ }
+ }
+ }
+ int1store(*p, stmt->send_types_to_server);
+ (*p)++;
+
+ if (stmt->send_types_to_server) {
/* 2 bytes per type, and leave 20 bytes for future use */
if (left < ((stmt->param_count * 2) + 20)) {
unsigned int offset = *p - *buf;
@@ -631,16 +665,40 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
*p = *buf + offset;
}
for (i = 0; i < stmt->param_count; i++) {
+ short current_type = stmt->param_bind[i].type;
/* our types are not unsigned */
#if SIZEOF_LONG==8
- if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) {
- stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG;
+ if (current_type == MYSQL_TYPE_LONG) {
+ current_type = MYSQL_TYPE_LONGLONG;
}
#endif
- int2store(*p, stmt->param_bind[i].type);
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ /*
+ if it doesn't fit in a long send it as a string.
+ Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ */
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ convert_to_double_ex(&tmp_data);
+ if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
+ convert_to_string_ex(&tmp_data);
+ current_type = MYSQL_TYPE_VAR_STRING;
+ /*
+ don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
+ we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
+ if the type is however not long, then we will do a goto in the next switch.
+ We want to preserve the original bind type given by the user. Thus, we do these hacks.
+ */
+ } else {
+ convert_to_long_ex(&tmp_data);
+ }
+ }
+ }
+ int2store(*p, current_type);
*p+= 2;
}
}
+ stmt->send_types_to_server = resend_types_next_time;
/* 2. Store data */
/* 2.1 Calculate how much space we need */
@@ -654,9 +712,11 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
for (j = i + 1; j < stmt->param_count; j++) {
if (stmt->param_bind[j].zv == the_var) {
/* Double binding of the same zval, make a copy */
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
- SET_OOM_ERROR(stmt->error_info);
- goto end;
+ if (!copies || !copies[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(stmt->error_info);
+ goto end;
+ }
}
break;
}
@@ -674,23 +734,25 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
}
}
break;
-#if SIZEOF_LONG==8
case MYSQL_TYPE_LONGLONG:
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ if (Z_TYPE_P(tmp_data) == IS_STRING) {
+ goto use_string;
+ }
+ convert_to_long_ex(&tmp_data);
+ }
data_size += 8;
-#elif SIZEOF_LONG==4
+ break;
case MYSQL_TYPE_LONG:
- data_size += 4;
-#else
-#error "Should not happen"
-#endif
- if (Z_TYPE_P(the_var) != IS_LONG) {
- if (!copies || !copies[i]) {
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
- SET_OOM_ERROR(stmt->error_info);
- goto end;
- }
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ if (Z_TYPE_P(tmp_data) == IS_STRING) {
+ goto use_string;
}
+ convert_to_long_ex(&tmp_data);
}
+ data_size += 4;
break;
case MYSQL_TYPE_LONG_BLOB:
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@@ -703,6 +765,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
}
break;
case MYSQL_TYPE_VAR_STRING:
+use_string:
data_size += 8; /* max 8 bytes for size */
#if MYSQLND_UNICODE
if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
@@ -757,7 +820,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
/* 2.3 Store the actual data */
for (i = 0; i < stmt->param_count; i++) {
- zval *data = copies && copies[i]? copies[i]: stmt->param_bind[i].zv;
+ zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
/* Handle long data */
if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
@@ -768,21 +831,22 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
float8store(*p, Z_DVAL_P(data));
(*p) += 8;
break;
-#if SIZEOF_LONG==8
case MYSQL_TYPE_LONGLONG:
- convert_to_long_ex(&data);
+ if (Z_TYPE_P(data) == IS_STRING) {
+ goto send_string;
+ }
+ /* data has alreade been converted to long */
int8store(*p, Z_LVAL_P(data));
(*p) += 8;
break;
-#elif SIZEOF_LONG==4
case MYSQL_TYPE_LONG:
- convert_to_long_ex(&data);
+ if (Z_TYPE_P(data) == IS_STRING) {
+ goto send_string;
+ }
+ /* data has alreade been converted to long */
int4store(*p, Z_LVAL_P(data));
(*p) += 4;
break;
-#else
-#error "Should not happen"
-#endif
case MYSQL_TYPE_LONG_BLOB:
if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
@@ -791,7 +855,9 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
*p = php_mysqlnd_net_store_length(*p, 0);
}
break;
- case MYSQL_TYPE_VAR_STRING:{
+ case MYSQL_TYPE_VAR_STRING:
+send_string:
+ {
unsigned int len = Z_STRLEN_P(data);
/* to is after p. The latter hasn't been moved */
*p = php_mysqlnd_net_store_length(*p, len);
@@ -855,10 +921,6 @@ mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** requ
memset(p, 0, null_count);
p += null_count;
-
- int1store(p, stmt->send_types_to_server);
- p++;
-
ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC);
*free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);