diff --git a/NEWS b/NEWS index 7aada8213bb5..12cc1f8efbb7 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,10 @@ PHP NEWS . Fixed IntlChar methods leaving stale global error state after successful calls. (Xuyang Zhang) +- ODBC: + . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the + driver-reported display size). (iliaal) + - OpenSSL: . Fixed timeout for supplemental read at end of a blocking stream in SSL stream wrapper. (ilutov) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 692841be10e1..16617078d3c1 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -680,6 +680,7 @@ void odbc_bindcols(odbc_result *result) for(i = 0; i < result->numcols; i++) { charextraalloc = 0; + result->values[i].value_max_len = 0; colfieldid = SQL_COLUMN_DISPLAY_SIZE; rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), PHP_ODBC_SQL_DESC_NAME, @@ -706,6 +707,7 @@ void odbc_bindcols(odbc_result *result) #ifdef HAVE_ADABAS case SQL_TIMESTAMP: result->values[i].value = (char *)emalloc(27); + result->values[i].value_max_len = 26; SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value, 27, &result->values[i].vallen); break; @@ -775,6 +777,7 @@ void odbc_bindcols(odbc_result *result) displaysize *= 4; } result->values[i].value = (char *)emalloc(displaysize + 1); + result->values[i].value_max_len = displaysize; rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value, displaysize + 1, &result->values[i].vallen); break; @@ -1492,7 +1495,11 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) ZVAL_FALSE(&tmp); break; } - ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen); + SQLLEN str_len = result->values[i].vallen; + if (str_len > result->values[i].value_max_len) { + str_len = result->values[i].value_max_len; + } + ZVAL_STRINGL(&tmp, result->values[i].value, str_len); break; } @@ -1660,7 +1667,11 @@ PHP_FUNCTION(odbc_fetch_into) ZVAL_FALSE(&tmp); break; } - ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen); + SQLLEN str_len = result->values[i].vallen; + if (str_len > result->values[i].value_max_len) { + str_len = result->values[i].value_max_len; + } + ZVAL_STRINGL(&tmp, result->values[i].value, str_len); break; } zend_hash_index_update(Z_ARRVAL_P(pv_res_arr), i, &tmp); @@ -1910,7 +1921,11 @@ PHP_FUNCTION(odbc_result) php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", field_ind + 1); RETURN_FALSE; } else { - RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen); + SQLLEN str_len = result->values[field_ind].vallen; + if (str_len > result->values[field_ind].value_max_len) { + str_len = result->values[field_ind].value_max_len; + } + RETURN_STRINGL(result->values[field_ind].value, str_len); } break; } diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h index 59404ecd266b..1c267c9e6b42 100644 --- a/ext/odbc/php_odbc_includes.h +++ b/ext/odbc/php_odbc_includes.h @@ -205,6 +205,7 @@ typedef struct odbc_result_value { char name[256]; char *value; SQLLEN vallen; + SQLLEN value_max_len; SQLLEN coltype; } odbc_result_value; diff --git a/ext/odbc/tests/gh22668.phpt b/ext/odbc/tests/gh22668.phpt new file mode 100644 index 000000000000..b9f8bb9aa66c --- /dev/null +++ b/ext/odbc/tests/gh22668.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-22668 (Heap buffer over-read when a column value exceeds the bound buffer) +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +clamped to buffer: bool(true) +only value bytes: bool(true)