Skip to content

Commit f78b119

Browse files
andrewnesterserhiy-storchaka
authored andcommitted
bpo-29649: Improve struct.pack_into() boundary error messages (#424)
1 parent 5de85a1 commit f78b119

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

Lib/test/test_struct.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,26 @@ def test__sizeof__(self):
578578
self.check_sizeof('0s', 1)
579579
self.check_sizeof('0c', 0)
580580

581+
def test_boundary_error_message(self):
582+
regex = (
583+
r'pack_into requires a buffer of at least 6 '
584+
r'bytes for packing 1 bytes at offset 5 '
585+
r'\(actual buffer size is 1\)'
586+
)
587+
with self.assertRaisesRegex(struct.error, regex):
588+
struct.pack_into('b', bytearray(1), 5, 1)
589+
590+
def test_boundary_error_message_with_negative_offset(self):
591+
byte_list = bytearray(10)
592+
with self.assertRaisesRegex(
593+
struct.error,
594+
r'no space to pack 4 bytes at offset -2'):
595+
struct.pack_into('<I', byte_list, -2, 123)
596+
597+
with self.assertRaisesRegex(
598+
struct.error,
599+
'offset -11 out of range for 10-byte buffer'):
600+
struct.pack_into('<B', byte_list, -11, 123)
581601

582602
class UnpackIteratorTest(unittest.TestCase):
583603
"""

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ Extension Modules
303303
Library
304304
-------
305305

306+
- bpo-29649: Improve struct.pack_into() exception messages for problems
307+
with the buffer size and offset. Patch by Andrew Nester.
308+
306309
- bpo-29654: Support If-Modified-Since HTTP header (browser cache). Patch
307310
by Pierre Quentel.
308311

Modules/_struct.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,14 +1931,40 @@ s_pack_into(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames
19311931
}
19321932

19331933
/* Support negative offsets. */
1934-
if (offset < 0)
1934+
if (offset < 0) {
1935+
/* Check that negative offset is low enough to fit data */
1936+
if (offset + soself->s_size > 0) {
1937+
PyErr_Format(StructError,
1938+
"no space to pack %zd bytes at offset %zd",
1939+
soself->s_size,
1940+
offset);
1941+
PyBuffer_Release(&buffer);
1942+
return NULL;
1943+
}
1944+
1945+
/* Check that negative offset is not crossing buffer boundary */
1946+
if (offset + buffer.len < 0) {
1947+
PyErr_Format(StructError,
1948+
"offset %zd out of range for %zd-byte buffer",
1949+
offset,
1950+
buffer.len);
1951+
PyBuffer_Release(&buffer);
1952+
return NULL;
1953+
}
1954+
19351955
offset += buffer.len;
1956+
}
19361957

19371958
/* Check boundaries */
1938-
if (offset < 0 || (buffer.len - offset) < soself->s_size) {
1959+
if ((buffer.len - offset) < soself->s_size) {
19391960
PyErr_Format(StructError,
1940-
"pack_into requires a buffer of at least %zd bytes",
1941-
soself->s_size);
1961+
"pack_into requires a buffer of at least %zd bytes for "
1962+
"packing %zd bytes at offset %zd "
1963+
"(actual buffer size is %zd)",
1964+
soself->s_size + offset,
1965+
soself->s_size,
1966+
offset,
1967+
buffer.len);
19421968
PyBuffer_Release(&buffer);
19431969
return NULL;
19441970
}

0 commit comments

Comments
 (0)