mirror of https://github.com/python/cpython
[security] bpo-13617: Reject embedded null characters in wchar* strings. (#2302)
Based on patch by Victor Stinner. Add private C API function _PyUnicode_AsUnicode() which is similar to PyUnicode_AsUnicode(), but checks for null characters.
This commit is contained in:
parent
592eda1233
commit
f7eae0adfc
|
@ -756,23 +756,27 @@ PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4(
|
|||
PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4Copy(PyObject *unicode);
|
||||
#endif
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
/* Return a read-only pointer to the Unicode object's internal
|
||||
Py_UNICODE buffer.
|
||||
If the wchar_t/Py_UNICODE representation is not yet available, this
|
||||
function will calculate it. */
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
|
||||
PyObject *unicode /* Unicode object */
|
||||
) /* Py_DEPRECATED(3.3) */;
|
||||
#endif
|
||||
|
||||
/* Similar to PyUnicode_AsUnicode(), but raises a ValueError if the string
|
||||
contains null characters. */
|
||||
PyAPI_FUNC(const Py_UNICODE *) _PyUnicode_AsUnicode(
|
||||
PyObject *unicode /* Unicode object */
|
||||
);
|
||||
|
||||
/* Return a read-only pointer to the Unicode object's internal
|
||||
Py_UNICODE buffer and save the length at size.
|
||||
If the wchar_t/Py_UNICODE representation is not yet available, this
|
||||
function will calculate it. */
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicodeAndSize(
|
||||
PyObject *unicode, /* Unicode object */
|
||||
Py_ssize_t *size /* location where to save the length */
|
||||
|
|
|
@ -62,6 +62,8 @@ class LoaderTest(unittest.TestCase):
|
|||
windll["kernel32"].GetModuleHandleW
|
||||
windll.LoadLibrary("kernel32").GetModuleHandleW
|
||||
WinDLL("kernel32").GetModuleHandleW
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, windll.LoadLibrary, "kernel32\0")
|
||||
|
||||
@unittest.skipUnless(os.name == "nt",
|
||||
'test specific to Windows')
|
||||
|
|
|
@ -151,6 +151,8 @@ class BuiltinTest(unittest.TestCase):
|
|||
self.assertRaises(TypeError, __import__, 1, 2, 3, 4)
|
||||
self.assertRaises(ValueError, __import__, '')
|
||||
self.assertRaises(TypeError, __import__, 'sys', name='sys')
|
||||
# embedded null character
|
||||
self.assertRaises(ModuleNotFoundError, __import__, 'string\x00')
|
||||
|
||||
def test_abs(self):
|
||||
# int
|
||||
|
@ -1010,6 +1012,10 @@ class BuiltinTest(unittest.TestCase):
|
|||
self.assertEqual(fp.read(300), 'XXX'*100)
|
||||
self.assertEqual(fp.read(1000), 'YYY'*100)
|
||||
|
||||
# embedded null bytes and characters
|
||||
self.assertRaises(ValueError, open, 'a\x00b')
|
||||
self.assertRaises(ValueError, open, b'a\x00b')
|
||||
|
||||
def test_open_default_encoding(self):
|
||||
old_environ = dict(os.environ)
|
||||
try:
|
||||
|
|
|
@ -81,7 +81,7 @@ class TestCurses(unittest.TestCase):
|
|||
win2 = curses.newwin(15,15, 5,5)
|
||||
|
||||
for meth in [stdscr.addch, stdscr.addstr]:
|
||||
for args in [('a'), ('a', curses.A_BOLD),
|
||||
for args in [('a',), ('a', curses.A_BOLD),
|
||||
(4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
|
||||
with self.subTest(meth=meth.__qualname__, args=args):
|
||||
meth(*args)
|
||||
|
@ -194,6 +194,15 @@ class TestCurses(unittest.TestCase):
|
|||
self.assertRaises(ValueError, stdscr.instr, -2)
|
||||
self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
|
||||
|
||||
def test_embedded_null_chars(self):
|
||||
# reject embedded null bytes and characters
|
||||
stdscr = self.stdscr
|
||||
for arg in ['a', b'a']:
|
||||
with self.subTest(arg=arg):
|
||||
self.assertRaises(ValueError, stdscr.addstr, 'a\0')
|
||||
self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1)
|
||||
self.assertRaises(ValueError, stdscr.insstr, 'a\0')
|
||||
self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1)
|
||||
|
||||
def test_module_funcs(self):
|
||||
"Test module-level functions"
|
||||
|
|
|
@ -50,6 +50,8 @@ class GroupDatabaseTestCase(unittest.TestCase):
|
|||
self.assertRaises(TypeError, grp.getgrgid)
|
||||
self.assertRaises(TypeError, grp.getgrnam)
|
||||
self.assertRaises(TypeError, grp.getgrall, 42)
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, grp.getgrnam, 'a\x00b')
|
||||
|
||||
# try to get some errors
|
||||
bynames = {}
|
||||
|
|
|
@ -314,6 +314,10 @@ class ImportTests(unittest.TestCase):
|
|||
loader.get_data(imp.__file__) # File should be closed
|
||||
loader.get_data(imp.__file__) # Will need to create a newly opened file
|
||||
|
||||
def test_load_source(self):
|
||||
with self.assertRaisesRegex(ValueError, 'embedded null'):
|
||||
imp.load_source(__name__, __file__ + "\0")
|
||||
|
||||
|
||||
class ReloadTests(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -346,9 +346,14 @@ class TestCollation(unittest.TestCase):
|
|||
self.assertLess(locale.strcoll('a', 'b'), 0)
|
||||
self.assertEqual(locale.strcoll('a', 'a'), 0)
|
||||
self.assertGreater(locale.strcoll('b', 'a'), 0)
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, locale.strcoll, 'a\0', 'a')
|
||||
self.assertRaises(ValueError, locale.strcoll, 'a', 'a\0')
|
||||
|
||||
def test_strxfrm(self):
|
||||
self.assertLess(locale.strxfrm('a'), locale.strxfrm('b'))
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, locale.strxfrm, 'a\0')
|
||||
|
||||
|
||||
class TestEnUSCollation(BaseLocalizedTest, TestCollation):
|
||||
|
|
|
@ -126,6 +126,10 @@ class TimeTestCase(unittest.TestCase):
|
|||
except ValueError:
|
||||
self.fail('conversion specifier: %r failed.' % format)
|
||||
|
||||
self.assertRaises(TypeError, time.strftime, b'%S', tt)
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, time.strftime, '%S\0', tt)
|
||||
|
||||
def _bounds_checking(self, func):
|
||||
# Make sure that strftime() checks the bounds of the various parts
|
||||
# of the time tuple (0 is valid for *all* values).
|
||||
|
|
|
@ -96,6 +96,8 @@ class PlaySoundTest(unittest.TestCase):
|
|||
self.assertRaises(TypeError, winsound.PlaySound, "bad",
|
||||
winsound.SND_MEMORY)
|
||||
self.assertRaises(TypeError, winsound.PlaySound, 1, 0)
|
||||
# embedded null character
|
||||
self.assertRaises(ValueError, winsound.PlaySound, 'bad\0', 0)
|
||||
|
||||
def test_keyword_args(self):
|
||||
safe_PlaySound(flags=winsound.SND_ALIAS, sound="SystemExit")
|
||||
|
|
|
@ -1253,10 +1253,11 @@ static PyObject *load_library(PyObject *self, PyObject *args)
|
|||
PyObject *nameobj;
|
||||
PyObject *ignored;
|
||||
HMODULE hMod;
|
||||
if (!PyArg_ParseTuple(args, "O|O:LoadLibrary", &nameobj, &ignored))
|
||||
|
||||
if (!PyArg_ParseTuple(args, "U|O:LoadLibrary", &nameobj, &ignored))
|
||||
return NULL;
|
||||
|
||||
name = PyUnicode_AsUnicode(nameobj);
|
||||
name = _PyUnicode_AsUnicode(nameobj);
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -341,9 +341,11 @@ static int
|
|||
PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj,
|
||||
PyObject **bytes, wchar_t **wstr)
|
||||
{
|
||||
char *str;
|
||||
if (PyUnicode_Check(obj)) {
|
||||
#ifdef HAVE_NCURSESW
|
||||
assert (wstr != NULL);
|
||||
|
||||
*wstr = PyUnicode_AsWideCharString(obj, NULL);
|
||||
if (*wstr == NULL)
|
||||
return 0;
|
||||
|
@ -353,12 +355,20 @@ PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj,
|
|||
*bytes = PyUnicode_AsEncodedString(obj, win->encoding, NULL);
|
||||
if (*bytes == NULL)
|
||||
return 0;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(*bytes, &str, NULL) < 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
else if (PyBytes_Check(obj)) {
|
||||
Py_INCREF(obj);
|
||||
*bytes = obj;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(*bytes, &str, NULL) < 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -271,11 +271,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
|
|||
|
||||
if (fd < 0) {
|
||||
#ifdef MS_WINDOWS
|
||||
Py_ssize_t length;
|
||||
if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
|
||||
return -1;
|
||||
}
|
||||
widename = PyUnicode_AsUnicodeAndSize(stringobj, &length);
|
||||
widename = PyUnicode_AsUnicode(stringobj);
|
||||
if (widename == NULL)
|
||||
return -1;
|
||||
#else
|
||||
|
|
|
@ -252,6 +252,11 @@ PyLocale_strxfrm(PyObject* self, PyObject* args)
|
|||
s = PyUnicode_AsWideCharString(str, &n1);
|
||||
if (s == NULL)
|
||||
goto exit;
|
||||
if (wcslen(s) != (size_t)n1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"embedded null character");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* assume no change in size, first */
|
||||
n1 = n1 + 1;
|
||||
|
|
|
@ -151,6 +151,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)
|
|||
|
||||
if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL)
|
||||
return NULL;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ nis_match (PyObject *self, PyObject *args, PyObject *kwdict)
|
|||
return NULL;
|
||||
if ((bkey = PyUnicode_EncodeFSDefault(ukey)) == NULL)
|
||||
return NULL;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(bkey, &key, &keylen) == -1) {
|
||||
Py_DECREF(bkey);
|
||||
return NULL;
|
||||
|
|
|
@ -3757,7 +3757,7 @@ os__getfinalpathname_impl(PyObject *module, PyObject *path)
|
|||
PyObject *result;
|
||||
const wchar_t *path_wchar;
|
||||
|
||||
path_wchar = PyUnicode_AsUnicode(path);
|
||||
path_wchar = _PyUnicode_AsUnicode(path);
|
||||
if (path_wchar == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -7209,7 +7209,7 @@ win_readlink(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
))
|
||||
return NULL;
|
||||
|
||||
path = PyUnicode_AsUnicode(po);
|
||||
path = _PyUnicode_AsUnicode(po);
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -9002,6 +9002,7 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
|
|||
/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/
|
||||
{
|
||||
const wchar_t *env;
|
||||
Py_ssize_t size;
|
||||
|
||||
/* Search from index 1 because on Windows starting '=' is allowed for
|
||||
defining hidden environment variables. */
|
||||
|
@ -9015,16 +9016,21 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
|
|||
if (unicode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (_MAX_ENV < PyUnicode_GET_LENGTH(unicode)) {
|
||||
|
||||
env = PyUnicode_AsUnicodeAndSize(unicode, &size);
|
||||
if (env == NULL)
|
||||
goto error;
|
||||
if (size > _MAX_ENV) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"the environment variable is longer than %u characters",
|
||||
_MAX_ENV);
|
||||
goto error;
|
||||
}
|
||||
|
||||
env = PyUnicode_AsUnicode(unicode);
|
||||
if (env == NULL)
|
||||
if (wcslen(env) != (size_t)size) {
|
||||
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_wputenv(env)) {
|
||||
posix_error();
|
||||
goto error;
|
||||
|
|
|
@ -158,6 +158,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg)
|
|||
|
||||
if ((bytes = PyUnicode_EncodeFSDefault(arg)) == NULL)
|
||||
return NULL;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
|
||||
goto out;
|
||||
if ((p = getpwnam(name)) == NULL) {
|
||||
|
|
|
@ -134,6 +134,7 @@ spwd_getspnam_impl(PyObject *module, PyObject *arg)
|
|||
|
||||
if ((bytes = PyUnicode_EncodeFSDefault(arg)) == NULL)
|
||||
return NULL;
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
|
||||
goto out;
|
||||
if ((p = getspnam(name)) == NULL) {
|
||||
|
|
|
@ -4133,6 +4133,20 @@ PyUnicode_AsUnicode(PyObject *unicode)
|
|||
return PyUnicode_AsUnicodeAndSize(unicode, NULL);
|
||||
}
|
||||
|
||||
const Py_UNICODE *
|
||||
_PyUnicode_AsUnicode(PyObject *unicode)
|
||||
{
|
||||
Py_ssize_t size;
|
||||
const Py_UNICODE *wstr;
|
||||
|
||||
wstr = PyUnicode_AsUnicodeAndSize(unicode, &size);
|
||||
if (wstr && wcslen(wstr) != (size_t)size) {
|
||||
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
||||
return NULL;
|
||||
}
|
||||
return wstr;
|
||||
}
|
||||
|
||||
|
||||
Py_ssize_t
|
||||
PyUnicode_GetSize(PyObject *unicode)
|
||||
|
|
|
@ -594,8 +594,12 @@ summary_setproperty(msiobj* si, PyObject *args)
|
|||
return NULL;
|
||||
|
||||
if (PyUnicode_Check(data)) {
|
||||
WCHAR *value = _PyUnicode_AsUnicode(data);
|
||||
if (value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR,
|
||||
0, NULL, PyUnicode_AsUnicode(data));
|
||||
0, NULL, value);
|
||||
} else if (PyLong_CheckExact(data)) {
|
||||
long value = PyLong_AsLong(data);
|
||||
if (value == -1 && PyErr_Occurred()) {
|
||||
|
|
|
@ -190,13 +190,13 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
|
|||
{
|
||||
dl_funcptr p;
|
||||
char funcname[258], *import_python;
|
||||
wchar_t *wpathname;
|
||||
const wchar_t *wpathname;
|
||||
|
||||
#ifndef _DEBUG
|
||||
_Py_CheckPython3();
|
||||
#endif
|
||||
|
||||
wpathname = PyUnicode_AsUnicode(pathname);
|
||||
wpathname = _PyUnicode_AsUnicode(pathname);
|
||||
if (wpathname == NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -711,21 +711,32 @@ _Py_stat(PyObject *path, struct stat *statbuf)
|
|||
#ifdef MS_WINDOWS
|
||||
int err;
|
||||
struct _stat wstatbuf;
|
||||
wchar_t *wpath;
|
||||
const wchar_t *wpath;
|
||||
|
||||
wpath = PyUnicode_AsUnicode(path);
|
||||
wpath = _PyUnicode_AsUnicode(path);
|
||||
if (wpath == NULL)
|
||||
return -2;
|
||||
|
||||
err = _wstat(wpath, &wstatbuf);
|
||||
if (!err)
|
||||
statbuf->st_mode = wstatbuf.st_mode;
|
||||
return err;
|
||||
#else
|
||||
int ret;
|
||||
PyObject *bytes = PyUnicode_EncodeFSDefault(path);
|
||||
PyObject *bytes;
|
||||
char *cpath;
|
||||
|
||||
bytes = PyUnicode_EncodeFSDefault(path);
|
||||
if (bytes == NULL)
|
||||
return -2;
|
||||
ret = stat(PyBytes_AS_STRING(bytes), statbuf);
|
||||
|
||||
/* check for embedded null bytes */
|
||||
if (PyBytes_AsStringAndSize(bytes, &cpath, NULL) == -1) {
|
||||
Py_DECREF(bytes);
|
||||
return -2;
|
||||
}
|
||||
|
||||
ret = stat(cpath, statbuf);
|
||||
Py_DECREF(bytes);
|
||||
return ret;
|
||||
#endif
|
||||
|
@ -1080,7 +1091,7 @@ _Py_fopen_obj(PyObject *path, const char *mode)
|
|||
FILE *f;
|
||||
int async_err = 0;
|
||||
#ifdef MS_WINDOWS
|
||||
wchar_t *wpath;
|
||||
const wchar_t *wpath;
|
||||
wchar_t wmode[10];
|
||||
int usize;
|
||||
|
||||
|
@ -1094,7 +1105,7 @@ _Py_fopen_obj(PyObject *path, const char *mode)
|
|||
Py_TYPE(path));
|
||||
return NULL;
|
||||
}
|
||||
wpath = PyUnicode_AsUnicode(path);
|
||||
wpath = _PyUnicode_AsUnicode(path);
|
||||
if (wpath == NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
Loading…
Reference in New Issue