]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-127208: Reject null character in _imp.create_dynamic() (#127400) (#127419)
authorVictor Stinner <vstinner@python.org>
Fri, 29 Nov 2024 16:03:24 +0000 (17:03 +0100)
committerGitHub <noreply@github.com>
Fri, 29 Nov 2024 16:03:24 +0000 (17:03 +0100)
gh-127208: Reject null character in _imp.create_dynamic() (#127400)

_imp.create_dynamic() now rejects embedded null characters in the
path and in the module name.

Backport also the _PyUnicode_AsUTF8NoNUL() function.

(cherry picked from commit b14fdadc6c620875a20b7ccc3c9b069e85d8557a)

Include/internal/pycore_unicodeobject.h
Lib/test/test_import/__init__.py
Objects/unicodeobject.c
Python/import.c

index 44eccdea55beb0deaf7b976ccfdaafd14c8ac280..cecdabe41550636c420081baa7876610542b6ec6 100644 (file)
@@ -76,6 +76,9 @@ struct _Py_unicode_state {
 
 extern void _PyUnicode_ClearInterned(PyInterpreterState *interp);
 
+// Like PyUnicode_AsUTF8(), but check for embedded null characters.
+extern const char* _PyUnicode_AsUTF8NoNUL(PyObject *);
+
 
 #ifdef __cplusplus
 }
index c6accc4183a67fa32347ad97902379a7af309355..81b2f33a840f1d399f4f4f75eab883c18228e90d 100644 (file)
@@ -787,6 +787,19 @@ class ImportTests(unittest.TestCase):
         self.assertIn("Frozen object named 'x' is invalid",
                       str(cm.exception))
 
+    def test_create_dynamic_null(self):
+        with self.assertRaisesRegex(ValueError, 'embedded null character'):
+            class Spec:
+                name = "a\x00b"
+                origin = "abc"
+            _imp.create_dynamic(Spec())
+
+        with self.assertRaisesRegex(ValueError, 'embedded null character'):
+            class Spec2:
+                name = "abc"
+                origin = "a\x00b"
+            _imp.create_dynamic(Spec2())
+
 
 @skip_if_dont_write_bytecode
 class FilePermissionTests(unittest.TestCase):
index 7bd4b221c83cf7d1396270568467b8fefc1f48ee..5f3dd26ad7b762de7d2abcdf92594769e20a54b5 100644 (file)
@@ -3991,6 +3991,18 @@ PyUnicode_AsUTF8(PyObject *unicode)
     return PyUnicode_AsUTF8AndSize(unicode, NULL);
 }
 
+const char *
+_PyUnicode_AsUTF8NoNUL(PyObject *unicode)
+{
+    Py_ssize_t size;
+    const char *s = PyUnicode_AsUTF8AndSize(unicode, &size);
+    if (s && strlen(s) != (size_t)size) {
+        PyErr_SetString(PyExc_ValueError, "embedded null character");
+        return NULL;
+    }
+    return s;
+}
+
 /*
 PyUnicode_GetSize() has been deprecated since Python 3.3
 because it returned length of Py_UNICODE.
index 4d0b7fd95569b3a8da692dbf5ab8dd58b443a9c8..66391c04a0baaae9a8e0bff2fececcf8753cb500 100644 (file)
@@ -917,12 +917,14 @@ extensions_lock_release(void)
 static void *
 hashtable_key_from_2_strings(PyObject *str1, PyObject *str2, const char sep)
 {
-    Py_ssize_t str1_len, str2_len;
-    const char *str1_data = PyUnicode_AsUTF8AndSize(str1, &str1_len);
-    const char *str2_data = PyUnicode_AsUTF8AndSize(str2, &str2_len);
+    const char *str1_data = _PyUnicode_AsUTF8NoNUL(str1);
+    const char *str2_data = _PyUnicode_AsUTF8NoNUL(str2);
     if (str1_data == NULL || str2_data == NULL) {
         return NULL;
     }
+    Py_ssize_t str1_len = strlen(str1_data);
+    Py_ssize_t str2_len = strlen(str2_data);
+
     /* Make sure sep and the NULL byte won't cause an overflow. */
     assert(SIZE_MAX - str1_len - str2_len > 2);
     size_t size = str1_len + 1 + str2_len + 1;