diff --git a/NEWS b/NEWS index 7aada8213bb5..493fcd6e3c7a 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,8 @@ PHP NEWS - PDO_ODBC: . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN carries no credentials). (iliaal) + . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is + longer than the declared maxlen). (iliaal) - Phar: . Fixed inconsistent handling of the magic ".phar" directory. Paths such as diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index c114f721a7a7..4f77f1b1ca75 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -360,6 +360,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p param->driver_data = P; P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuflen = 0; P->outbuf = NULL; P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); @@ -384,6 +385,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p P->len *= 2; } P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); + P->outbuflen = P->len; } } @@ -481,10 +483,16 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p case PDO_ODBC_CONV_FAIL: case PDO_ODBC_CONV_NOT_REQUIRED: P->len = Z_STRLEN_P(parameter); + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); break; case PDO_ODBC_CONV_OK: P->len = ulen; + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } memcpy(P->outbuf, S->convbuf, P->len); break; } @@ -506,6 +514,9 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p zval_ptr_dtor(parameter); if (P->len >= 0) { + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } ZVAL_STRINGL(parameter, P->outbuf, P->len); switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { case PDO_ODBC_CONV_FAIL: diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h index 473d70ff7076..ea93c58b2b60 100644 --- a/ext/pdo_odbc/php_pdo_odbc_int.h +++ b/ext/pdo_odbc/php_pdo_odbc_int.h @@ -153,6 +153,7 @@ typedef struct { typedef struct { SQLLEN len; + SQLLEN outbuflen; SQLSMALLINT paramtype; char *outbuf; unsigned is_unicode:1; diff --git a/ext/pdo_odbc/tests/gh22666.phpt b/ext/pdo_odbc/tests/gh22666.phpt new file mode 100644 index 000000000000..c23f370327b3 --- /dev/null +++ b/ext/pdo_odbc/tests/gh22666.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-22666 (Heap buffer overflow when an output param value exceeds its maxlen) +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +prepare('SELECT ?'); +$stmt->bindParam(1, $value, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4); +$stmt->execute(); + +echo "bounded to maxlen: "; var_dump($value); + +@unlink($file); +?> +--EXPECT-- +bounded to maxlen: string(4) "AAAA"