diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2018-07-31 12:58:58 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-31 12:58:58 +0300 |
commit | ada5d99306dc8af21c32cefb3d86891e8553dbc6 (patch) | |
tree | 2b7e7aaa39273fc590d52184f259e73840866198 /Lib | |
parent | bpo-27671: Update FAQ about why len is function (GH-8432) (diff) | |
download | cpython-ada5d99306dc8af21c32cefb3d86891e8553dbc6.tar.gz cpython-ada5d99306dc8af21c32cefb3d86891e8553dbc6.tar.bz2 cpython-ada5d99306dc8af21c32cefb3d86891e8553dbc6.zip |
[3.6] bpo-33871: Fix os.sendfile(), os.writev(), os.readv(), etc. (GH-7931) (GH-8584)
* Fix integer overflow in os.readv(), os.writev() and in os.sendfile()
with headers or trailers arguments (on BSD-based OSes and MacOS).
* Fix sending the part of the file in os.sendfile() on MacOS.
Using the trailers argument could cause sending more bytes from
the input file than was specified.
Thanks Ned Deily for testing on 32-bit MacOS.
(cherry picked from commit 9d5727326af53ddd91016d98e16ae7cf829caa95)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_os.py | 50 | ||||
-rw-r--r-- | Lib/test/test_posix.py | 27 |
2 files changed, 62 insertions, 15 deletions
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 240b7c432ba..8339f849e84 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2540,12 +2540,14 @@ if threading is not None: def __init__(self, conn): asynchat.async_chat.__init__(self, conn) self.in_buffer = [] + self.accumulate = True self.closed = False self.push(b"220 ready\r\n") def handle_read(self): data = self.recv(4096) - self.in_buffer.append(data) + if self.accumulate: + self.in_buffer.append(data) def get_data(self): return b''.join(self.in_buffer) @@ -2627,6 +2629,8 @@ class TestSendfile(unittest.TestCase): not sys.platform.startswith("sunos") requires_headers_trailers = unittest.skipUnless(SUPPORT_HEADERS_TRAILERS, 'requires headers and trailers support') + requires_32b = unittest.skipUnless(sys.maxsize < 2**32, + 'test is only meaningful on 32-bit builds') @classmethod def setUpClass(cls): @@ -2657,17 +2661,13 @@ class TestSendfile(unittest.TestCase): self.server.stop() self.server = None - def sendfile_wrapper(self, sock, file, offset, nbytes, headers=[], trailers=[]): + def sendfile_wrapper(self, *args, **kwargs): """A higher level wrapper representing how an application is supposed to use sendfile(). """ - while 1: + while True: try: - if self.SUPPORT_HEADERS_TRAILERS: - return os.sendfile(sock, file, offset, nbytes, headers, - trailers) - else: - return os.sendfile(sock, file, offset, nbytes) + return os.sendfile(*args, **kwargs) except OSError as err: if err.errno == errno.ECONNRESET: # disconnected @@ -2758,20 +2758,22 @@ class TestSendfile(unittest.TestCase): @requires_headers_trailers def test_headers(self): total_sent = 0 + expected_data = b"x" * 512 + b"y" * 256 + self.DATA[:-1] sent = os.sendfile(self.sockno, self.fileno, 0, 4096, - headers=[b"x" * 512]) + headers=[b"x" * 512, b"y" * 256]) + self.assertLessEqual(sent, 512 + 256 + 4096) total_sent += sent offset = 4096 - nbytes = 4096 - while 1: + while total_sent < len(expected_data): + nbytes = min(len(expected_data) - total_sent, 4096) sent = self.sendfile_wrapper(self.sockno, self.fileno, offset, nbytes) if sent == 0: break + self.assertLessEqual(sent, nbytes) total_sent += sent offset += sent - expected_data = b"x" * 512 + self.DATA self.assertEqual(total_sent, len(expected_data)) self.client.close() self.server.wait() @@ -2787,12 +2789,30 @@ class TestSendfile(unittest.TestCase): create_file(TESTFN2, file_data) with open(TESTFN2, 'rb') as f: - os.sendfile(self.sockno, f.fileno(), 0, len(file_data), - trailers=[b"1234"]) + os.sendfile(self.sockno, f.fileno(), 0, 5, + trailers=[b"123456", b"789"]) self.client.close() self.server.wait() data = self.server.handler_instance.get_data() - self.assertEqual(data, b"abcdef1234") + self.assertEqual(data, b"abcde123456789") + + @requires_headers_trailers + @requires_32b + def test_headers_overflow_32bits(self): + self.server.handler_instance.accumulate = False + with self.assertRaises(OSError) as cm: + os.sendfile(self.sockno, self.fileno, 0, 0, + headers=[b"x" * 2**16] * 2**15) + self.assertEqual(cm.exception.errno, errno.EINVAL) + + @requires_headers_trailers + @requires_32b + def test_trailers_overflow_32bits(self): + self.server.handler_instance.accumulate = False + with self.assertRaises(OSError) as cm: + os.sendfile(self.sockno, self.fileno, 0, 0, + trailers=[b"x" * 2**16] * 2**15) + self.assertEqual(cm.exception.errno, errno.EINVAL) @requires_headers_trailers @unittest.skipUnless(hasattr(os, 'SF_NODISKIO'), diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index eb3ef849478..a204377d9af 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -19,6 +19,9 @@ import warnings _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), support.TESTFN + '-dummy-symlink') +requires_32b = unittest.skipUnless(sys.maxsize < 2**32, + 'test is only meaningful on 32-bit builds') + class PosixTester(unittest.TestCase): def setUp(self): @@ -322,6 +325,17 @@ class PosixTester(unittest.TestCase): finally: os.close(fd) + @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") + @requires_32b + def test_writev_overflow_32bits(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + with self.assertRaises(OSError) as cm: + os.writev(fd, [b"x" * 2**16] * 2**15) + self.assertEqual(cm.exception.errno, errno.EINVAL) + finally: + os.close(fd) + @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") def test_readv(self): fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) @@ -344,6 +358,19 @@ class PosixTester(unittest.TestCase): finally: os.close(fd) + @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") + @requires_32b + def test_readv_overflow_32bits(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + buf = [bytearray(2**16)] * 2**15 + with self.assertRaises(OSError) as cm: + os.readv(fd, buf) + self.assertEqual(cm.exception.errno, errno.EINVAL) + self.assertEqual(bytes(buf[0]), b'\0'* 2**16) + finally: + os.close(fd) + @unittest.skipUnless(hasattr(posix, 'dup'), 'test needs posix.dup()') def test_dup(self): |