Skip to content

Commit 8905f7a

Browse files
[3.13] gh-137044: Support large limit values in getrlimit() and setrlimit() (GH-137338) (GH-137507)
* Return large limit values as positive integers instead of negative integers in resource.getrlimit(). * Accept large values and reject negative values (except RLIM_INFINITY) for limits in resource.setrlimit(). (cherry picked from commit baefaa6) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 751a890 commit 8905f7a

File tree

4 files changed

+201
-121
lines changed

4 files changed

+201
-121
lines changed

Lib/test/test_resource.py

Lines changed: 139 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -14,89 +14,154 @@ class ResourceTest(unittest.TestCase):
1414

1515
def test_args(self):
1616
self.assertRaises(TypeError, resource.getrlimit)
17-
self.assertRaises(TypeError, resource.getrlimit, 42, 42)
17+
self.assertRaises(TypeError, resource.getrlimit, 0, 42)
18+
self.assertRaises(OverflowError, resource.getrlimit, 2**1000)
19+
self.assertRaises(OverflowError, resource.getrlimit, -2**1000)
20+
self.assertRaises(TypeError, resource.getrlimit, '0')
1821
self.assertRaises(TypeError, resource.setrlimit)
19-
self.assertRaises(TypeError, resource.setrlimit, 42, 42, 42)
22+
self.assertRaises(TypeError, resource.setrlimit, 0)
23+
self.assertRaises(TypeError, resource.setrlimit, 0, 42)
24+
self.assertRaises(TypeError, resource.setrlimit, 0, 42, 42)
25+
self.assertRaises(OverflowError, resource.setrlimit, 2**1000, (42, 42))
26+
self.assertRaises(OverflowError, resource.setrlimit, -2**1000, (42, 42))
27+
self.assertRaises(ValueError, resource.setrlimit, 0, (42,))
28+
self.assertRaises(ValueError, resource.setrlimit, 0, (42, 42, 42))
29+
self.assertRaises(TypeError, resource.setrlimit, '0', (42, 42))
30+
self.assertRaises(TypeError, resource.setrlimit, 0, ('42', 42))
31+
self.assertRaises(TypeError, resource.setrlimit, 0, (42, '42'))
2032

2133
@unittest.skipIf(sys.platform == "vxworks",
2234
"setting RLIMIT_FSIZE is not supported on VxWorks")
35+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
2336
def test_fsize_ismax(self):
24-
try:
25-
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
26-
except AttributeError:
27-
pass
28-
else:
29-
# RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really big
30-
# number on a platform with large file support. On these platforms,
31-
# we need to test that the get/setrlimit functions properly convert
32-
# the number to a C long long and that the conversion doesn't raise
33-
# an error.
34-
self.assertEqual(resource.RLIM_INFINITY, max)
35-
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
37+
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
38+
# RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really big
39+
# number on a platform with large file support. On these platforms,
40+
# we need to test that the get/setrlimit functions properly convert
41+
# the number to a C long long and that the conversion doesn't raise
42+
# an error.
43+
self.assertEqual(resource.RLIM_INFINITY, max)
44+
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
3645

46+
@unittest.skipIf(sys.platform == "vxworks",
47+
"setting RLIMIT_FSIZE is not supported on VxWorks")
48+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
3749
def test_fsize_enforced(self):
50+
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
51+
# Check to see what happens when the RLIMIT_FSIZE is small. Some
52+
# versions of Python were terminated by an uncaught SIGXFSZ, but
53+
# pythonrun.c has been fixed to ignore that exception. If so, the
54+
# write() should return EFBIG when the limit is exceeded.
55+
56+
# At least one platform has an unlimited RLIMIT_FSIZE and attempts
57+
# to change it raise ValueError instead.
3858
try:
39-
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
40-
except AttributeError:
41-
pass
42-
else:
43-
# Check to see what happens when the RLIMIT_FSIZE is small. Some
44-
# versions of Python were terminated by an uncaught SIGXFSZ, but
45-
# pythonrun.c has been fixed to ignore that exception. If so, the
46-
# write() should return EFBIG when the limit is exceeded.
47-
48-
# At least one platform has an unlimited RLIMIT_FSIZE and attempts
49-
# to change it raise ValueError instead.
5059
try:
60+
resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max))
61+
limit_set = True
62+
except ValueError:
63+
limit_set = False
64+
f = open(os_helper.TESTFN, "wb")
65+
try:
66+
f.write(b"X" * 1024)
5167
try:
52-
resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max))
53-
limit_set = True
54-
except ValueError:
55-
limit_set = False
56-
f = open(os_helper.TESTFN, "wb")
57-
try:
58-
f.write(b"X" * 1024)
59-
try:
60-
f.write(b"Y")
68+
f.write(b"Y")
69+
f.flush()
70+
# On some systems (e.g., Ubuntu on hppa) the flush()
71+
# doesn't always cause the exception, but the close()
72+
# does eventually. Try flushing several times in
73+
# an attempt to ensure the file is really synced and
74+
# the exception raised.
75+
for i in range(5):
76+
time.sleep(.1)
6177
f.flush()
62-
# On some systems (e.g., Ubuntu on hppa) the flush()
63-
# doesn't always cause the exception, but the close()
64-
# does eventually. Try flushing several times in
65-
# an attempt to ensure the file is really synced and
66-
# the exception raised.
67-
for i in range(5):
68-
time.sleep(.1)
69-
f.flush()
70-
except OSError:
71-
if not limit_set:
72-
raise
73-
if limit_set:
74-
# Close will attempt to flush the byte we wrote
75-
# Restore limit first to avoid getting a spurious error
76-
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
77-
finally:
78-
f.close()
79-
finally:
78+
except OSError:
79+
if not limit_set:
80+
raise
8081
if limit_set:
82+
# Close will attempt to flush the byte we wrote
83+
# Restore limit first to avoid getting a spurious error
8184
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
82-
os_helper.unlink(os_helper.TESTFN)
85+
finally:
86+
f.close()
87+
finally:
88+
if limit_set:
89+
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
90+
os_helper.unlink(os_helper.TESTFN)
8391

84-
def test_fsize_toobig(self):
92+
@unittest.skipIf(sys.platform == "vxworks",
93+
"setting RLIMIT_FSIZE is not supported on VxWorks")
94+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
95+
def test_fsize_too_big(self):
8596
# Be sure that setrlimit is checking for really large values
8697
too_big = 10**50
98+
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
99+
try:
100+
resource.setrlimit(resource.RLIMIT_FSIZE, (too_big, max))
101+
except (OverflowError, ValueError):
102+
pass
87103
try:
88-
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
89-
except AttributeError:
104+
resource.setrlimit(resource.RLIMIT_FSIZE, (max, too_big))
105+
except (OverflowError, ValueError):
90106
pass
107+
108+
@unittest.skipIf(sys.platform == "vxworks",
109+
"setting RLIMIT_FSIZE is not supported on VxWorks")
110+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
111+
def test_fsize_not_too_big(self):
112+
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
113+
self.addCleanup(resource.setrlimit, resource.RLIMIT_FSIZE, (cur, max))
114+
115+
def expected(cur):
116+
if resource.RLIM_INFINITY < 0:
117+
return [(cur, max), (resource.RLIM_INFINITY, max)]
118+
elif resource.RLIM_INFINITY < cur:
119+
return [(resource.RLIM_INFINITY, max)]
120+
else:
121+
return [(cur, max)]
122+
123+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max))
124+
self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31-5, max))
125+
126+
try:
127+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max))
128+
except OverflowError:
129+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max))
130+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31))
131+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max))
132+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5))
91133
else:
134+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32))
135+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max))
136+
self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31, max))
137+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max))
138+
self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**32-5, max))
139+
140+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**63-5, max))
141+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5))
92142
try:
93-
resource.setrlimit(resource.RLIMIT_FSIZE, (too_big, max))
94-
except (OverflowError, ValueError):
95-
pass
96-
try:
97-
resource.setrlimit(resource.RLIMIT_FSIZE, (max, too_big))
98-
except (OverflowError, ValueError):
143+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max))
144+
except ValueError:
145+
# There is a hard limit on macOS.
99146
pass
147+
else:
148+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63))
149+
resource.setrlimit(resource.RLIMIT_FSIZE, (2**64-5, max))
150+
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5))
151+
152+
@unittest.skipIf(sys.platform == "vxworks",
153+
"setting RLIMIT_FSIZE is not supported on VxWorks")
154+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
155+
def test_fsize_negative(self):
156+
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
157+
for value in -5, -2**31, -2**32-5, -2**63, -2**64-5, -2**1000:
158+
with self.subTest(value=value):
159+
# This test assumes that the values don't map to RLIM_INFINITY,
160+
# though Posix doesn't guarantee it.
161+
self.assertNotEqual(value, resource.RLIM_INFINITY)
162+
163+
self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (value, max))
164+
self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (cur, value))
100165

101166
@unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage")
102167
def test_getrusage(self):
@@ -117,21 +182,18 @@ def test_getrusage(self):
117182
# Issue 6083: Reference counting bug
118183
@unittest.skipIf(sys.platform == "vxworks",
119184
"setting RLIMIT_CPU is not supported on VxWorks")
185+
@unittest.skipUnless(hasattr(resource, 'RLIMIT_CPU'), 'requires resource.RLIMIT_CPU')
120186
def test_setrusage_refcount(self):
121-
try:
122-
limits = resource.getrlimit(resource.RLIMIT_CPU)
123-
except AttributeError:
124-
pass
125-
else:
126-
class BadSequence:
127-
def __len__(self):
128-
return 2
129-
def __getitem__(self, key):
130-
if key in (0, 1):
131-
return len(tuple(range(1000000)))
132-
raise IndexError
187+
limits = resource.getrlimit(resource.RLIMIT_CPU)
188+
class BadSequence:
189+
def __len__(self):
190+
return 2
191+
def __getitem__(self, key):
192+
if key in (0, 1):
193+
return len(tuple(range(1000000)))
194+
raise IndexError
133195

134-
resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
196+
resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
135197

136198
def test_pagesize(self):
137199
pagesize = resource.getpagesize()
@@ -168,7 +230,8 @@ class BadSeq:
168230
def __len__(self):
169231
return 2
170232
def __getitem__(self, key):
171-
return limits[key] - 1 # new reference
233+
lim = limits[key]
234+
return lim - 1 if lim > 0 else lim + sys.maxsize*2 # new reference
172235

173236
limits = resource.getrlimit(resource.RLIMIT_AS)
174237
self.assertEqual(resource.prlimit(0, resource.RLIMIT_AS, BadSeq()),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Return large limit values as positive integers instead of negative integers
2+
in :func:`resource.getrlimit`. Accept large values and reject negative
3+
values (except :data:`~resource.RLIM_INFINITY`) for limits in
4+
:func:`resource.setrlimit`.

Modules/clinic/resource.c.h

Lines changed: 7 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)