Commit bba94675f51 for php

commit bba94675f5107e0026c5cd47eea6d0087957d71f
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Oct 19 08:26:44 2026 -0400

    ext/pdo: Validate Oracle null handling modes

    Reject unsupported ATTR_ORACLE_NULLS values before narrowing them into the
    two-bit field, preserving the previous mode when validation fails. The
    constructor and setAttribute() share this validation.

    Closes GH-24431

diff --git a/NEWS b/NEWS
index 8771d8eae54..5f966a6ea0f 100644
--- a/NEWS
+++ b/NEWS
@@ -83,6 +83,8 @@ PHP                                                                        NEWS
     reference check). (ndossche)

 - PDO:
+  . Fixed invalid ATTR_ORACLE_NULLS values being accepted and truncated.
+    (Ilia Alshanetsky)
   . Fixed a duplicate warning when ATTR_STATEMENT_CLASS is rejected on a
     persistent connection. (Ilia Alshanetsky)

diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index a8be45e1558..804cbb64407 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -854,9 +854,16 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /
 			if (!pdo_get_long_param(&lval, value)) {
 				return false;
 			}
-			/* TODO Check for valid value (NULL_NATURAL, NULL_EMPTY_STRING, NULL_TO_STRING)? */
-			dbh->oracle_nulls = lval;
-			return true;
+			switch (lval) {
+				case PDO_NULL_NATURAL:
+				case PDO_NULL_EMPTY_STRING:
+				case PDO_NULL_TO_STRING:
+					dbh->oracle_nulls = lval;
+					return true;
+				default:
+					zend_value_error("PDO::ATTR_ORACLE_NULLS must be one of the PDO::NULL_* constants");
+					return false;
+			}

 		case PDO_ATTR_DEFAULT_FETCH_MODE:
 			if (Z_TYPE_P(value) == IS_ARRAY) {
diff --git a/ext/pdo/tests/pdo_oracle_nulls_invalid.phpt b/ext/pdo/tests/pdo_oracle_nulls_invalid.phpt
new file mode 100644
index 00000000000..30b251e1915
--- /dev/null
+++ b/ext/pdo/tests/pdo_oracle_nulls_invalid.phpt
@@ -0,0 +1,49 @@
+--TEST--
+PDO Common: Invalid Oracle null handling modes do not change the attribute
+--EXTENSIONS--
+pdo
+--SKIPIF--
+<?php
+$dir = getenv('REDIR_TEST_DIR');
+if ($dir === false) {
+    die('skip no driver');
+}
+require_once $dir . 'pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
+$pdo = PDOTest::factory();
+
+foreach ([PDO::NULL_NATURAL, PDO::NULL_EMPTY_STRING, PDO::NULL_TO_STRING] as $value) {
+    echo "Valid mode: ";
+    var_dump($pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, $value));
+    var_dump($pdo->getAttribute(PDO::ATTR_ORACLE_NULLS));
+}
+
+foreach ([-1, 3, 4, PHP_INT_MAX] as $value) {
+    try {
+        $pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, $value);
+    } catch (ValueError $e) {
+        echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+    }
+    echo "Unchanged mode: ";
+    var_dump($pdo->getAttribute(PDO::ATTR_ORACLE_NULLS));
+}
+?>
+--EXPECT--
+Valid mode: bool(true)
+int(0)
+Valid mode: bool(true)
+int(1)
+Valid mode: bool(true)
+int(2)
+ValueError: PDO::ATTR_ORACLE_NULLS must be one of the PDO::NULL_* constants
+Unchanged mode: int(2)
+ValueError: PDO::ATTR_ORACLE_NULLS must be one of the PDO::NULL_* constants
+Unchanged mode: int(2)
+ValueError: PDO::ATTR_ORACLE_NULLS must be one of the PDO::NULL_* constants
+Unchanged mode: int(2)
+ValueError: PDO::ATTR_ORACLE_NULLS must be one of the PDO::NULL_* constants
+Unchanged mode: int(2)