diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000000..410c0f9b7cd9 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude + +Go read `../AGENTS.md`. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..2cf3915754b1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,7 @@ +# Agents + +## Vulnerability reports + +When scanning php-src for vulnerabilities, please respect our security policy +summarized in `./SECURITY.md`. You can find the full policy +[here](https://raw.githubusercontent.com/php/policies/refs/heads/main/security-classification.rst). diff --git a/NEWS b/NEWS index f02dbf699c8b..0bc09de808bd 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.6.0beta3 +?? ??? ????, PHP 8.6.0RC1 + + +10 Sep 2026, PHP 8.6.0beta3 - BCMath: . Fixed out-of-bounds read in bc_is_zero_for_scale() when scale exceeds diff --git a/SECURITY.md b/SECURITY.md index 24801b3b4e43..b6337a80bdc4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -36,6 +36,13 @@ are not limited to): - Malicious `unserialize()` inputs. +- Memory exhaustion from a size the input declares, where `memory_limit` + refuses the allocation and only the current request dies. + +When creating reports, please **skip** the theatrics. Drop the impact essay, +send a short reproducer with the few lines that matter, and make each point +once. This allows us to triage and respond to your report quickly. + # Vulnerability Policy Our full policy is described at diff --git a/docs/release-process.md b/docs/release-process.md index e65a85c101a2..2a53a00a2775 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -447,7 +447,7 @@ slightly different steps. We'll call attention where the steps differ. # . # git add -p - git add archive/entries/*.xml + git add public/archive/entries/*.xml git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.0RCn" git push upstream master ``` @@ -758,9 +758,9 @@ slightly different steps. We'll call attention where the steps differ. ./bin/createReleaseEntry -v X.Y.Z -r # --security for security releases ``` - This will create a release file (i.e., `releases/X_Y_Z.php`) and a news entry - file (i.e., `archive/entries/YYYY-MM-DD-n.xml`), while also updating - `archive/archive.xml`. + This will create a release file (i.e., `public/releases/X_Y_Z.php`) and a news + entry file (i.e., `public/archive/entries/YYYY-MM-DD-n.xml`), while also + updating `public/archive/archive.xml`. Within these files, it will generate standard messages for the new version. You may edit the generated files to expand on the base message, if needed. @@ -776,7 +776,7 @@ slightly different steps. We'll call attention where the steps differ. for PHP 8.4) added above the first anchor of the previous version. ```shell - ./bin/news2html 'https://github.com/php/php-src/raw/php-X.Y.Z/NEWS' 'X.Y.Z' 'ChangeLog-X.php' + ./bin/news2html 'https://github.com/php/php-src/raw/php-X.Y.Z/NEWS' 'X.Y.Z' 'public/ChangeLog-X.php' ``` 8. Update the information in the `$QA_RELEASES` array in `include/release-qa.php`. @@ -792,7 +792,7 @@ slightly different steps. We'll call attention where the steps differ. ```shell git add -p - git add archive/entries/*.xml releases/*.php + git add public/archive/entries/*.xml public/releases/*.php git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.Z" git push upstream master ``` @@ -884,8 +884,8 @@ If you choose to create a patch-level release, follow these steps: * Call `php bin/createReleaseEntry -v [ --security ]` in your local web-php checkout. -4. Commit all the changes (`include/version.inc`, `archive/archive.xml`, - `archive/entries/YYYY-MM-DD-N.xml`). +4. Commit all the changes (`include/version.inc`, `public/archive/archive.xml`, + `public/archive/entries/YYYY-MM-DD-N.xml`). 5. Wait an hour or two, then send a mail to php-announce@lists.php.net, php-general@lists.php.net and internals@lists.php.net with a text similar to diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 4b05d8dc4455..642307935c77 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -576,11 +576,31 @@ PHP_MINFO_FUNCTION(odbc) } /* }}} */ +static SQLRETURN odbc_diag_rec(ODBC_SQL_ENV_T henv, ODBC_SQL_CONN_T conn, ODBC_SQL_STMT_T stmt, + char *state, char *errormsg, SQLSMALLINT errormsg_size) +{ + SQLINTEGER native_error; + SQLSMALLINT handle_type; + SQLHANDLE handle; + + if (stmt != SQL_NULL_HSTMT) { + handle_type = SQL_HANDLE_STMT; + handle = (SQLHANDLE) stmt; + } else if (conn != SQL_NULL_HDBC) { + handle_type = SQL_HANDLE_DBC; + handle = (SQLHANDLE) conn; + } else { + handle_type = SQL_HANDLE_ENV; + handle = (SQLHANDLE) henv; + } + + return SQLGetDiagRec(handle_type, handle, 1, (SQLCHAR *) state, &native_error, + (SQLCHAR *) errormsg, errormsg_size, NULL); +} + /* {{{ odbc_sql_error */ void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const char *func, ...) { - SQLINTEGER error; /* Not used */ - SQLSMALLINT errormsgsize; /* Not used */ RETCODE rc; ODBC_SQL_ENV_T henv; ODBC_SQL_CONN_T conn; @@ -593,12 +613,7 @@ void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const conn = SQL_NULL_HDBC; } - /* This leads to an endless loop in many drivers! - * - while(henv != SQL_NULL_HENV){ - do { - */ - rc = SQLError(henv, conn, stmt, (SQLCHAR *) ODBCG(laststate), &error, (SQLCHAR *) ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg))-1, &errormsgsize); + rc = odbc_diag_rec(henv, conn, stmt, ODBCG(laststate), ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg))-1); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { snprintf(ODBCG(laststate), sizeof(ODBCG(laststate)), "HY000"); snprintf(ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg)), "Failed to fetch error message"); @@ -619,10 +634,6 @@ void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const } else { php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s", ODBCG(lasterrormsg), ODBCG(laststate)); } - /* - } while (SQL_SUCCEEDED(rc)); - } - */ } /* }}} */ @@ -1161,14 +1172,16 @@ PHP_FUNCTION(odbc_cursor) cursorname = emalloc(max_len + 1); rc = SQLGetCursorName(result->stmt, (SQLCHAR *) cursorname, (SQLSMALLINT)max_len, &len); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - char state[6]; /* Not used */ - SQLINTEGER error; /* Not used */ + char state[6]; char errormsg[SQL_MAX_MESSAGE_LENGTH]; - SQLSMALLINT errormsgsize; /* Not used */ + SQLRETURN diag_rc; - SQLError( result->conn_ptr->henv, result->conn_ptr->hdbc, - result->stmt, (SQLCHAR *) state, &error, (SQLCHAR *) errormsg, - sizeof(errormsg)-1, &errormsgsize); + diag_rc = odbc_diag_rec(result->conn_ptr->henv, result->conn_ptr->hdbc, result->stmt, + state, errormsg, sizeof(errormsg)-1); + if (diag_rc != SQL_SUCCESS && diag_rc != SQL_SUCCESS_WITH_INFO) { + snprintf(state, sizeof(state), "HY000"); + snprintf(errormsg, sizeof(errormsg), "Failed to fetch error message"); + } if (!strncmp(state,"S1015",5)) { snprintf(cursorname, max_len+1, "php_curs_" ZEND_ULONG_FMT, (zend_ulong)result->stmt); if (SQLSetCursorName(result->stmt, (SQLCHAR *) cursorname, SQL_NTS) != SQL_SUCCESS) { diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 3e1051ca3379..6d8f31a8b7ed 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -495,9 +495,12 @@ typedef struct _ir_sparse_set { IR_ALWAYS_INLINE void ir_sparse_set_init(ir_sparse_set *set, uint32_t size) { + size_t alloc_size = (size_t)size * 2 * sizeof(*set->data); + set->size = size; set->len = 0; - set->data = (uint32_t*)ir_mem_malloc(sizeof(uint32_t) * 2 * size) + size; + IR_ASSERT(!size || alloc_size / size == 2 * sizeof(*set->data)); + set->data = (uint32_t*)ir_mem_malloc(alloc_size) + size; #ifdef IR_DEBUG /* initialize sparse part to avoid valgrind warnings */ memset(&IR_SPARSE_SET_SPARSE(set, size - 1), 0, size * sizeof(uint32_t));