bpo-29649: Improve struct.pack_into() boundary error messages (#424) · python/cpython@f78b119 (original) (raw)

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -578,6 +578,26 @@ def test__sizeof__(self):
578 578 self.check_sizeof('0s', 1)
579 579 self.check_sizeof('0c', 0)
580 580
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)
581 601
582 602 class UnpackIteratorTest(unittest.TestCase):
583 603 """
Original file line number Diff line number Diff line change
@@ -303,6 +303,9 @@ Extension Modules
303 303 Library
304 304 -------
305 305
306 +- bpo-29649: Improve struct.pack_into() exception messages for problems
307 + with the buffer size and offset. Patch by Andrew Nester.
308 +
306 309 - bpo-29654: Support If-Modified-Since HTTP header (browser cache). Patch
307 310 by Pierre Quentel.
308 311
Original file line number Diff line number Diff line change
@@ -1931,14 +1931,40 @@ s_pack_into(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames
1931 1931 }
1932 1932
1933 1933 /* 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 +
1935 1955 offset += buffer.len;
1956 + }
1936 1957
1937 1958 /* Check boundaries */
1938 -if (offset < 0 |
1959 +if ((buffer.len - offset) < soself->s_size) {
1939 1960 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);
1942 1968 PyBuffer_Release(&buffer);
1943 1969 return NULL;
1944 1970 }