msgpack, struct, zlib, buffer_helper: values from outside used unchecked - #11377
Open
peterbay wants to merge 1 commit into
Open
msgpack, struct, zlib, buffer_helper: values from outside used unchecked#11377peterbay wants to merge 1 commit into
peterbay wants to merge 1 commit into
Conversation
msgpack recurses once per container and the nesting on the unpack side comes
from the input, so a stream of nested arrays ran the C stack out; 2000 of them
fault the board. It calls mp_cstack_check() now. A container's 32-bit element
count was also multiplied by the element size with no overflow check, so on a
32-bit build a count of 0x40000001 allocated four bytes for a billion entries.
Type 0xca is a 32-bit float, but the union paired uint32_t with mp_float_t,
which is a double on some ports; there it wrote half the value.
struct's repeat count and zlib's wbits both went through
MP_OBJ_SMALL_INT_VALUE, which on anything that is not a small int just
reinterprets the object word: struct.calcsize("9999999999b") returned
268486792. Both read through the converting accessors now.
normalize_buffer_bounds added the length to a negative start and left it
negative when it was below -length, so the caller worked from before the
buffer with a length reaching past its end.
Author
|
Testing and diagnostic script. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Code written by Claude Code, guided and corrected by @peterbay.
The problem
Six places where a value that comes from outside the program — a msgpack stream, a format string, an argument — is used without being checked. Two of them take the board down with a hard fault.
The changes
msgpackrecursed once per container with no C stack check. The nesting on the unpack side comes straight from the input, so a stream of nested arrays runs the C stack out; 2000 of them fault the board.mp_cstack_check()inpackandunpackturns that into aRuntimeError.A container's element count was multiplied by the element size with no overflow check. An array or map header carries a 32-bit count taken from the stream, and
mp_obj_new_list/mp_obj_new_dictmultiply it bysizeof(mp_obj_t)orsizeof(mp_map_elem_t). On a 32-bit build0x40000001 * 4wraps to 4, so a four-element allocation was filled with a billion entries.msgpackwrote and read half a float. Type0xcais a 32-bit float, but the union paireduint32_twithmp_float_t, which is a double on ports that have one. There it wrote the wrong half of the value and read back nonsense. The union holds afloatnow, converted at the boundary.struct's repeat count went throughMP_OBJ_SMALL_INT_VALUE.mp_parse_num_integerreturns a big-int object when the literal does not fit a small int, andMP_OBJ_SMALL_INT_VALUEon one of those just reinterprets the object word:struct.calcsize("9999999999b")returned 268486792 here. Read throughmp_obj_get_int, which raises for a value that does not fit.zlib.decompressreadwbitsthe same way, so a long int, a float, a string orNonecame through as a pointer shifted rather than being converted or rejected.normalize_buffer_boundsleft a negative start negative. A start below-lengthhad the length added and was still below zero, and the caller then worked frombuf + startwith a length reaching past the end.bitbangio.SPI().write(buf, start=-100)on a ten byte buffer reads ninety bytes from in front of it.Testing
Seeed XIAO nRF52840 Sense, on two builds differing only by these changes.
struct.calcsize("9999999999b")OverflowErrorstruct.calcsize("70000b")zlib.decompress(b"", "nonsense")ValueError: -3, from a pointerTypeError: can't convert str to intmsgpack.unpackof an array32 with count0x40000001EOFErrorValueError: array too longEOFErrorValueError: map too longmsgpack.unpackof 2000 nested arraysRuntimeError: maximum recursion depth exceededbitbangio.SPI().write(bytearray(10), start=-100)The two container-count rows stop at
EOFErrorrather than at corruption because the stream ends before the loop has filled much; the allocation is still four bytes for a billion entries.The float is not covered:
mp_float_tis a single on this port, so the old union was the right size there and nothing changes. It is a defect only wheremp_float_tis a double.No new translatable strings: the two messages reuse
"%q too long".