Skip to content

Commit c10b288

Browse files
authored
bpo-30249: Improve struct.unpack_from() error messages (GH-6059)
1 parent 67ee077 commit c10b288

File tree

5 files changed

+67
-15
lines changed

5 files changed

+67
-15
lines changed

Doc/library/struct.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ The module defines the following exception and functions:
7474

7575
Unpack from *buffer* starting at position *offset*, according to the format
7676
string *format*. The result is a tuple even if it contains exactly one
77-
item. The buffer's size in bytes, minus *offset*, must be at least
78-
the size required by the format, as reflected by :func:`calcsize`.
77+
item. The buffer's size in bytes, starting at position *offset*, must be at
78+
least the size required by the format, as reflected by :func:`calcsize`.
7979

8080

8181
.. function:: iter_unpack(format, buffer)
@@ -428,7 +428,7 @@ The :mod:`struct` module also defines the following type:
428428
.. method:: unpack_from(buffer, offset=0)
429429

430430
Identical to the :func:`unpack_from` function, using the compiled format.
431-
The buffer's size in bytes, minus *offset*, must be at least
431+
The buffer's size in bytes, starting at position *offset*, must be at least
432432
:attr:`size`.
433433

434434

Lib/test/test_struct.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -579,14 +579,22 @@ def test__sizeof__(self):
579579
self.check_sizeof('0c', 0)
580580

581581
def test_boundary_error_message(self):
582-
regex = (
582+
regex1 = (
583583
r'pack_into requires a buffer of at least 6 '
584584
r'bytes for packing 1 bytes at offset 5 '
585585
r'\(actual buffer size is 1\)'
586586
)
587-
with self.assertRaisesRegex(struct.error, regex):
587+
with self.assertRaisesRegex(struct.error, regex1):
588588
struct.pack_into('b', bytearray(1), 5, 1)
589589

590+
regex2 = (
591+
r'unpack_from requires a buffer of at least 6 '
592+
r'bytes for unpacking 1 bytes at offset 5 '
593+
r'\(actual buffer size is 1\)'
594+
)
595+
with self.assertRaisesRegex(struct.error, regex2):
596+
struct.unpack_from('b', bytearray(1), 5)
597+
590598
def test_boundary_error_message_with_negative_offset(self):
591599
byte_list = bytearray(10)
592600
with self.assertRaisesRegex(
@@ -599,16 +607,34 @@ def test_boundary_error_message_with_negative_offset(self):
599607
'offset -11 out of range for 10-byte buffer'):
600608
struct.pack_into('<B', byte_list, -11, 123)
601609

610+
with self.assertRaisesRegex(
611+
struct.error,
612+
r'not enough data to unpack 4 bytes at offset -2'):
613+
struct.unpack_from('<I', byte_list, -2)
614+
615+
with self.assertRaisesRegex(
616+
struct.error,
617+
"offset -11 out of range for 10-byte buffer"):
618+
struct.unpack_from('<B', byte_list, -11)
619+
602620
def test_boundary_error_message_with_large_offset(self):
603621
# Test overflows cause by large offset and value size (issue 30245)
604-
regex = (
622+
regex1 = (
605623
r'pack_into requires a buffer of at least ' + str(sys.maxsize + 4) +
606624
r' bytes for packing 4 bytes at offset ' + str(sys.maxsize) +
607625
r' \(actual buffer size is 10\)'
608626
)
609-
with self.assertRaisesRegex(struct.error, regex):
627+
with self.assertRaisesRegex(struct.error, regex1):
610628
struct.pack_into('<I', bytearray(10), sys.maxsize, 1)
611629

630+
regex2 = (
631+
r'unpack_from requires a buffer of at least ' + str(sys.maxsize + 4) +
632+
r' bytes for unpacking 4 bytes at offset ' + str(sys.maxsize) +
633+
r' \(actual buffer size is 10\)'
634+
)
635+
with self.assertRaisesRegex(struct.error, regex2):
636+
struct.unpack_from('<I', bytearray(10), sys.maxsize)
637+
612638
def test_issue29802(self):
613639
# When the second argument of struct.unpack() was of wrong type
614640
# the Struct object was decrefed twice and the reference to
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve struct.unpack_from() exception messages for problems with the buffer
2+
size and offset.

Modules/_struct.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,24 +1553,47 @@ Return a tuple containing unpacked values.
15531553
15541554
Values are unpacked according to the format string Struct.format.
15551555
1556-
The buffer's size in bytes, minus offset, must be at least Struct.size.
1556+
The buffer's size in bytes, starting at position offset, must be
1557+
at least Struct.size.
15571558
15581559
See help(struct) for more on format strings.
15591560
[clinic start generated code]*/
15601561

15611562
static PyObject *
15621563
Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer,
15631564
Py_ssize_t offset)
1564-
/*[clinic end generated code: output=57fac875e0977316 input=97ade52422f8962f]*/
1565+
/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/
15651566
{
15661567
assert(self->s_codes != NULL);
15671568

1568-
if (offset < 0)
1569+
if (offset < 0) {
1570+
if (offset + self->s_size > 0) {
1571+
PyErr_Format(StructError,
1572+
"not enough data to unpack %zd bytes at offset %zd",
1573+
self->s_size,
1574+
offset);
1575+
return NULL;
1576+
}
1577+
1578+
if (offset + buffer->len < 0) {
1579+
PyErr_Format(StructError,
1580+
"offset %zd out of range for %zd-byte buffer",
1581+
offset,
1582+
buffer->len);
1583+
return NULL;
1584+
}
15691585
offset += buffer->len;
1570-
if (offset < 0 || buffer->len - offset < self->s_size) {
1586+
}
1587+
1588+
if ((buffer->len - offset) < self->s_size) {
15711589
PyErr_Format(StructError,
1572-
"unpack_from requires a buffer of at least %zd bytes",
1573-
self->s_size);
1590+
"unpack_from requires a buffer of at least %zu bytes for "
1591+
"unpacking %zd bytes at offset %zd "
1592+
"(actual buffer size is %zd)",
1593+
(size_t)self->s_size + (size_t)offset,
1594+
self->s_size,
1595+
offset,
1596+
buffer->len);
15741597
return NULL;
15751598
}
15761599
return s_unpack_internal(self, (char*)buffer->buf + offset);

Modules/clinic/_struct.c.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ PyDoc_STRVAR(Struct_unpack_from__doc__,
7979
"\n"
8080
"Values are unpacked according to the format string Struct.format.\n"
8181
"\n"
82-
"The buffer\'s size in bytes, minus offset, must be at least Struct.size.\n"
82+
"The buffer\'s size in bytes, starting at position offset, must be\n"
83+
"at least Struct.size.\n"
8384
"\n"
8485
"See help(struct) for more on format strings.");
8586

@@ -302,4 +303,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
302303

303304
return return_value;
304305
}
305-
/*[clinic end generated code: output=9119f213a951e4cc input=a9049054013a1b77]*/
306+
/*[clinic end generated code: output=d79b009652ae0b89 input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)