diff options
author | Larry Hastings <larry@hastings.org> | 2013-08-08 07:19:50 (GMT) |
---|---|---|
committer | Larry Hastings <larry@hastings.org> | 2013-08-08 07:19:50 (GMT) |
commit | a27b83ad2d3480ba7c20286d719025ef32100f75 (patch) | |
tree | 6e6849505b4c796e8a50bbbdeb9744d76a64f33e | |
parent | 7533137f4e1c78fda6041175f220bf1f77ee95a5 (diff) | |
download | cpython-a27b83ad2d3480ba7c20286d719025ef32100f75.zip cpython-a27b83ad2d3480ba7c20286d719025ef32100f75.tar.gz cpython-a27b83ad2d3480ba7c20286d719025ef32100f75.tar.bz2 |
Issue #15301: Parsing fd, uid, and gid parameters for builtins
in Modules/posixmodule.c is now far more robust.
-rw-r--r-- | Lib/test/test_os.py | 12 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Modules/posixmodule.c | 269 |
3 files changed, 205 insertions, 81 deletions
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 8916305..13ff18f 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -24,6 +24,8 @@ import itertools import stat import locale import codecs +import decimal +import fractions try: import threading except ImportError: @@ -865,6 +867,16 @@ class MakedirTests(unittest.TestCase): os.makedirs(path, mode=mode, exist_ok=True) os.umask(old_mask) + def test_chown_uid_gid_arguments_must_be_index(self): + stat = os.stat(support.TESTFN) + uid = stat.st_uid + gid = stat.st_gid + for value in (-1.0, -1j, decimal.Decimal(-1), fractions.Fraction(-2, 2)): + self.assertRaises(TypeError, os.chown, support.TESTFN, value, gid) + self.assertRaises(TypeError, os.chown, support.TESTFN, uid, value) + self.assertIsNone(os.chown(support.TESTFN, uid, gid)) + self.assertIsNone(os.chown(support.TESTFN, -1, -1)) + def test_exist_ok_s_isgid_directory(self): path = os.path.join(support.TESTFN, 'dir1') S_ISGID = stat.S_ISGID @@ -10,8 +10,11 @@ Projected Release date: 2013-09-08 Core and Builtins ----------------- +- Issue #15301: Parsing fd, uid, and gid parameters for builtins + in Modules/posixmodule.c is now far more robust. + - Issue #18368: PyOS_StdioReadline() no longer leaks memory when realloc() - fail + fail. - Issue #17934: Add a clear() method to frame objects, to help clean up expensive details (local variables) and break reference cycles. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e5d7ae1..5145e3a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -417,108 +417,213 @@ _PyLong_FromGid(gid_t gid) int _Py_Uid_Converter(PyObject *obj, void *p) { + uid_t uid; + PyObject *index; int overflow; long result; - if (PyFloat_Check(obj)) { - PyErr_SetString(PyExc_TypeError, - "integer argument expected, got float"); + unsigned long uresult; + + index = PyNumber_Index(obj); + if (index == NULL) { + PyErr_Format(PyExc_TypeError, + "uid should be integer, not %.200s", + Py_TYPE(obj)->tp_name); return 0; } - result = PyLong_AsLongAndOverflow(obj, &overflow); - if (overflow < 0) - goto OverflowDown; - if (!overflow && result == -1) { - /* error or -1 */ - if (PyErr_Occurred()) - return 0; - *(uid_t *)p = (uid_t)-1; - } - else { - /* unsigned uid_t */ - unsigned long uresult; - if (overflow > 0) { - uresult = PyLong_AsUnsignedLong(obj); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) - goto OverflowUp; - return 0; - } - if ((uid_t)uresult == (uid_t)-1) - goto OverflowUp; - } else { - if (result < 0) - goto OverflowDown; - uresult = result; + + /* + * Handling uid_t is complicated for two reasons: + * * Although uid_t is (always?) unsigned, it still + * accepts -1. + * * We don't know its size in advance--it may be + * bigger than an int, or it may be smaller than + * a long. + * + * So a bit of defensive programming is in order. + * Start with interpreting the value passed + * in as a signed long and see if it works. + */ + + result = PyLong_AsLongAndOverflow(index, &overflow); + + if (!overflow) { + uid = (uid_t)result; + + if (result == -1) { + if (PyErr_Occurred()) + goto fail; + /* It's a legitimate -1, we're done. */ + goto success; } + + /* Any other negative number is disallowed. */ + if (result < 0) + goto underflow; + + /* Ensure the value wasn't truncated. */ if (sizeof(uid_t) < sizeof(long) && - (unsigned long)(uid_t)uresult != uresult) - goto OverflowUp; - *(uid_t *)p = (uid_t)uresult; + (long)uid != result) + goto underflow; + goto success; + } + + if (overflow < 0) + goto underflow; + + /* + * Okay, the value overflowed a signed long. If it + * fits in an *unsigned* long, it may still be okay, + * as uid_t may be unsigned long on this platform. + */ + uresult = PyLong_AsUnsignedLong(index); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + goto overflow; + goto fail; } + + uid = (uid_t)uresult; + + /* + * If uid == (uid_t)-1, the user actually passed in ULONG_MAX, + * but this value would get interpreted as (uid_t)-1 by chown + * and its siblings. That's not what the user meant! So we + * throw an overflow exception instead. (We already + * handled a real -1 with PyLong_AsLongAndOverflow() above.) + */ + if (uid == (uid_t)-1) + goto overflow; + + /* Ensure the value wasn't truncated. */ + if (sizeof(uid_t) < sizeof(long) && + (unsigned long)uid != uresult) + goto overflow; + /* fallthrough */ + +success: + Py_DECREF(index); + *(uid_t *)p = uid; return 1; -OverflowDown: +underflow: PyErr_SetString(PyExc_OverflowError, - "user id is less than minimum"); - return 0; + "uid is less than minimum"); + goto fail; -OverflowUp: +overflow: PyErr_SetString(PyExc_OverflowError, - "user id is greater than maximum"); + "uid is greater than maximum"); + /* fallthrough */ + +fail: + Py_DECREF(index); return 0; } int _Py_Gid_Converter(PyObject *obj, void *p) { + gid_t gid; + PyObject *index; int overflow; long result; - if (PyFloat_Check(obj)) { - PyErr_SetString(PyExc_TypeError, - "integer argument expected, got float"); + unsigned long uresult; + + index = PyNumber_Index(obj); + if (index == NULL) { + PyErr_Format(PyExc_TypeError, + "gid should be integer, not %.200s", + Py_TYPE(obj)->tp_name); return 0; } - result = PyLong_AsLongAndOverflow(obj, &overflow); - if (overflow < 0) - goto OverflowDown; - if (!overflow && result == -1) { - /* error or -1 */ - if (PyErr_Occurred()) - return 0; - *(gid_t *)p = (gid_t)-1; - } - else { - /* unsigned gid_t */ - unsigned long uresult; - if (overflow > 0) { - uresult = PyLong_AsUnsignedLong(obj); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) - goto OverflowUp; - return 0; - } - if ((gid_t)uresult == (gid_t)-1) - goto OverflowUp; - } else { - if (result < 0) - goto OverflowDown; - uresult = result; + + /* + * Handling gid_t is complicated for two reasons: + * * Although gid_t is (always?) unsigned, it still + * accepts -1. + * * We don't know its size in advance--it may be + * bigger than an int, or it may be smaller than + * a long. + * + * So a bit of defensive programming is in order. + * Start with interpreting the value passed + * in as a signed long and see if it works. + */ + + result = PyLong_AsLongAndOverflow(index, &overflow); + + if (!overflow) { + gid = (gid_t)result; + + if (result == -1) { + if (PyErr_Occurred()) + goto fail; + /* It's a legitimate -1, we're done. */ + goto success; + } + + /* Any other negative number is disallowed. */ + if (result < 0) { + goto underflow; } + + /* Ensure the value wasn't truncated. */ if (sizeof(gid_t) < sizeof(long) && - (unsigned long)(gid_t)uresult != uresult) - goto OverflowUp; - *(gid_t *)p = (gid_t)uresult; + (long)gid != result) + goto underflow; + goto success; + } + + if (overflow < 0) + goto underflow; + + /* + * Okay, the value overflowed a signed long. If it + * fits in an *unsigned* long, it may still be okay, + * as gid_t may be unsigned long on this platform. + */ + uresult = PyLong_AsUnsignedLong(index); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + goto overflow; + goto fail; } + + gid = (gid_t)uresult; + + /* + * If gid == (gid_t)-1, the user actually passed in ULONG_MAX, + * but this value would get interpreted as (gid_t)-1 by chown + * and its siblings. That's not what the user meant! So we + * throw an overflow exception instead. (We already + * handled a real -1 with PyLong_AsLongAndOverflow() above.) + */ + if (gid == (gid_t)-1) + goto overflow; + + /* Ensure the value wasn't truncated. */ + if (sizeof(gid_t) < sizeof(long) && + (unsigned long)gid != uresult) + goto overflow; + /* fallthrough */ + +success: + Py_DECREF(index); + *(gid_t *)p = gid; return 1; -OverflowDown: +underflow: PyErr_SetString(PyExc_OverflowError, - "group id is less than minimum"); - return 0; + "gid is less than minimum"); + goto fail; -OverflowUp: +overflow: PyErr_SetString(PyExc_OverflowError, - "group id is greater than maximum"); + "gid is greater than maximum"); + /* fallthrough */ + +fail: + Py_DECREF(index); return 0; } #endif /* MS_WINDOWS */ @@ -541,25 +646,29 @@ static int _fd_converter(PyObject *o, int *p, const char *allowed) { int overflow; - long long_value = PyLong_AsLongAndOverflow(o, &overflow); - if (PyFloat_Check(o) || - (long_value == -1 && !overflow && PyErr_Occurred())) { - PyErr_Clear(); + long long_value; + + PyObject *index = PyNumber_Index(o); + if (index == NULL) { PyErr_Format(PyExc_TypeError, - "argument should be %s, not %.200s", - allowed, Py_TYPE(o)->tp_name); + "argument should be %s, not %.200s", + allowed, Py_TYPE(o)->tp_name); return 0; } + + long_value = PyLong_AsLongAndOverflow(index, &overflow); + Py_DECREF(index); if (overflow > 0 || long_value > INT_MAX) { PyErr_SetString(PyExc_OverflowError, - "signed integer is greater than maximum"); + "fd is greater than maximum"); return 0; } if (overflow < 0 || long_value < INT_MIN) { PyErr_SetString(PyExc_OverflowError, - "signed integer is less than minimum"); + "fd is less than minimum"); return 0; } + *p = (int)long_value; return 1; } |