Skip to content

Commit

Permalink
[3.12] gh-127208: Reject null character in _imp.create_dynamic() (#12…
Browse files Browse the repository at this point in the history
…7400) (#127419)

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 b14fdad)
  • Loading branch information
vstinner authored Nov 29, 2024
1 parent fc0564b commit e7ab978
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 3 deletions.
3 changes: 3 additions & 0 deletions Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,19 @@ def test_issue105979(self):
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):
Expand Down
12 changes: 12 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 5 additions & 3 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit e7ab978

Please sign in to comment.