-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reference counting bug in PyArg_ParseTuple and PyArg_ParseTupleAndKeywords #50333
Comments
The code for resource_setrlimit in Modules/resource.c does not handle -- import resource
l = [0, 0]
class MyNum:
def __int__(self):
l[1] = 20
return 10
def __del__(self):
print 'byebye', self
l[0] = MyNum()
l[1] = MyNum()
resource.setrlimit(resource.RLIMIT_CPU, l) -- The problem is that setrlimit gets its arguments by calling: PyArg_ParseTuple(args, "i(OO):setrlimit",
&resource, &curobj, &maxobj) The references curobj and maxobj are borrowed. The second argument can I've attached a patch that INCREFs both variables immediately after In my opinion it seems dangerous to allow format strings with the 'O' |
That is a good point. IMHO we'll be fine with a warning in the docs, |
IMO, any refcounting bug has the potential as a security risk. So I It's probably debatable whether to backport the warning to 2.6 or |
Actually, this can't be fixed without modifying C API methods PyArg_ParseTuple and PyArg_ParseTupleAndKeywords, because it's possible to make an object deallocated before PyArg_ParseTuple returns, so Py_INCREF immediately after parsing would be already too late. Here are my test cases: |
Let me summarize the issue: the PyArg_ParseTuple format code 'O' returns a borrowed reference. However, when the 'O' code appears inside parenthesis, there may not be an object to hold the reference to borrow from. This is what happens in the test-functools.py crasher: partial.__setstate__() takes a 4-tuple argument that is unpacked using a "(OOOO)" format. The test case passes an instance instead of a tuple that supports the sequence methods, but does not hold the reference to the "items" that its []-operator returns. This is not a problem at the top level because args argument to PyArg_ParseTuple is always a real tuple. I think that rather than deprecating the use of 'O' format inside parentheses, "(..O..)" unpacking should reject to unpack arguments other than tuples or maybe lists. |
Attached patch passes the regrtest and makes test-functools.py raise an exception rather than crash. The proposed change will make functions like partial.__setstate__ require tuple argument even though currently it would accept any container. This is not an issue with __setstate__ because it should only be called with arguments produced by __reduce__ and in the case of partial, __reduce__ produces state as a tuple. Other functions may need to be modified if they need to continue to accept arbitrary sequences. |
Here is a patch which get rid of all three PyArg_ParseTuple usage with parsing nested sequences. Thanks Evgeny for reproducers. |
Serhiy's patch looks good to me. |
New changeset a4c85f9b8f58 by Serhiy Storchaka in branch '2.7': New changeset 4bac47eb444c by Serhiy Storchaka in branch '3.2': New changeset e0ee10f27e5f by Serhiy Storchaka in branch '3.3': New changeset 3e3a7d825736 by Serhiy Storchaka in branch 'default': |
I do not have possibility and desires blind-repair a test on alien platform, so just temporarily disable a new test in Lib/ctypes/test/test_returnfuncptrs.py on Windows. If someone has a desire to fix it fell free to do this. I do not close this issue because committed patch only fix existing crashes in Python. There should be plenty of such bugs in third-party code. We have to deprecate this unsafe feature or reject any sequences except tuple as Alexander proposed. |
The FreeBSD 6.4 bot is failing, too. Note that the other functions dll = CDLL(_ctypes_test.__file__)
get_strchr = dll.get_strchr
get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char)
strchr = get_strchr() |
There are 6 different ways to get a function (see comment around PyCFuncPtr_new() in Modules/_ctypes/_ctypes.c). The other tests just use other ways. I'm more carefully read ctype code and found my mistake. Need to import "my_strchr", and not "strchr". |
FreeBSD 6.4 and Windows test failures was fixed in changesets 8fb98fb758e8 and ec70abe8c886. |
Oh, I shouldn't close this until this dangerous feature will be deprecated. |
Accepting an arbitrary sequence when "(...)" is used in the format string was introduced in changeset 0ef1071cb7fe. |
This test has a problem: though it tests not the ability to set a CPU hard limit, it fails if the hard limit is limited. Perhaps, ignore any exception there? Could you please help me re-write it correctly, so that I can run it on gyle--ALT's builder host--successfully): # Issue 6083: Reference counting bug
def test_setrusage_refcount(self):
try:
limits = resource.getrlimit(resource.RLIMIT_CPU)
except AttributeError:
self.skipTest('RLIMIT_CPU not available')
class BadSequence:
def __len__(self):
return 2
def __getitem__(self, key):
if key in (0, 1):
return len(tuple(range(1000000)))
raise IndexError
resource.setrlimit(resource.RLIMIT_CPU, BadSequence()) The failure: [builder@team ~]$ python /usr/lib64/python2.7/test/test_resource.py ====================================================================== Traceback (most recent call last):
File "/usr/lib64/python2.7/test/test_resource.py", line 117, in test_setrusage_refcount
resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
ValueError: not allowed to raise maximum limit Ran 6 tests in 0.085s FAILED (errors=1)
Traceback (most recent call last):
File "/usr/lib64/python2.7/test/test_resource.py", line 123, in <module>
test_main()
File "/usr/lib64/python2.7/test/test_resource.py", line 120, in test_main
test_support.run_unittest(ResourceTest)
File "/usr/lib64/python2.7/test/support/__init__.py", line 1577, in run_unittest
_run_suite(suite)
File "/usr/lib64/python2.7/test/support/__init__.py", line 1542, in _run_suite
raise TestFailed(err)
test.support.TestFailed: Traceback (most recent call last):
File "/usr/lib64/python2.7/test/test_resource.py", line 117, in test_setrusage_refcount
resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
ValueError: not allowed to raise maximum limit [builder@team ~]$ |
What does resource.getrlimit(resource.RLIMIT_CPU) return? |
>>> import resource
>>> resource.getrlimit(resource.RLIMIT_CPU)
(7200, 7260) |
The simplest way is to try passing the limit as a tuple resource.setrlimit(resource.RLIMIT_CPU, (1000000, 1000000)) and skip the test if it failed. |
Thanks! I also thought about this simplest way. What about this: diff --git a/Python/Lib/test/test_resource.py b/Python/Lib/test/test_resource.py
index de29d3b..bec4440 100644
--- a/Python/Lib/test/test_resource.py
+++ b/Python/Lib/test/test_resource.py
@@ -102,16 +102,21 @@ class ResourceTest(unittest.TestCase):
# Issue 6083: Reference counting bug
def test_setrusage_refcount(self):
+ howmany = 1000000
try:
limits = resource.getrlimit(resource.RLIMIT_CPU)
except AttributeError:
self.skipTest('RLIMIT_CPU not available')
+ try:
+ resource.setrlimit(resource.RLIMIT_CPU, (howmany, howmany))
+ except _:
+ self.skipTest('Setting RLIMIT_CPU not possible')
class BadSequence:
def __len__(self):
return 2
def __getitem__(self, key):
if key in (0, 1):
- return len(tuple(range(1000000)))
+ return len(tuple(range(howmany)))
raise IndexError
resource.setrlimit(resource.RLIMIT_CPU, BadSequence()) What should I write instead of _? |
And will the next call be effective (do anything), if we have already set the limit with the testing call? |
LGTM.
(ValueError, OSError)
This doesn't matter. We test that it doesn't crash when parse arguments. |
Ivan, can you supply a PR or would you like someone else to do so? |
I want to do a PR,if this is still needeed. |
Please do, Ananthakrishnan! |
No longer relevant because |
While the issue was fixed for |
…seTuple() Non-tuple sequences are deprecated as argument for the "(items)" format unit in PyArg_ParseTuple() and other argument parsing functions if items contains format units which store borrowed buffer or reference (e.g. "s" and "O"). str and bytearray are no longer accepted as valid sequences.
#128374 implements a more lenient variant of the initial deprecation plan. Non-tuple sequences are only deprecated if the nested format units store borrowed buffer or reference (e.g. "s" and "O"). If "(items)" only contains format units like "i" or "d" (or "s*"), general sequences are still accepted. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
The text was updated successfully, but these errors were encountered: