ipaddress, random, rainbowio: conversions that give the wrong value - #11378
Merged
dhalbert merged 1 commit intoSep 14, 2026
Merged
Conversation
ipaddress never range-checked its octets, so anything above 255 was shifted into place and carried into its neighbour: "300.1.1.1" parsed as 44.1.1.1 and "1.2.3.256" as 1.2.3.0. An octet too long for a small int came back as a big-int object and MP_OBJ_SMALL_INT_VALUE reinterpreted the object word; "99999999999.1.1.1" parsed as 56.201.1.17. IPv4Address used its buffer pointer whether or not the last branch had set it, so anything that is not an int, a string or a buffer built the address from four bytes read at address zero. The dot count is required to be exactly three rather than merely no more than three. rainbowio.colorwheel reduced its argument through a cast to uint32_t, which does nothing useful for a negative position: colorwheel(-1) returned -768. random used the generator's initial pad value as the mark for "never seeded", so random.seed(0xEDA4BABA) went to the hardware source instead and the sequence was not reproducible. randint(a, b) calls randrange(a, b + 1, 1), and for the largest representable b that addition overflows.
Author
|
Testing and diagnostic script. |
dhalbert
approved these changes
Sep 14, 2026
dhalbert
left a comment
Collaborator
There was a problem hiding this comment.
Thanks - these all make sense.
tannewt
pushed a commit
that referenced
this pull request
Sep 22, 2026
#11378 made `randint(a, b)` raise `ValueError` when `b` is the largest `mp_int_t`, to avoid the `b + 1` overflow in `randrange(a, b + 1, 1)`. On 32-bit ports that is `0x7FFFFFFF`, which the WIZnet DHCP library passes, so `randint(1, 0x7FFFFFFF)` stopped working (#11441). Add `shared_modules_random_randint()`, which computes the span `b - a + 1` in unsigned arithmetic so it cannot overflow for any `a <= b`, and use it from the binding instead of forming `b + 1`. Fixes #11441 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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 conversions that produce a wrong value rather than an error: an address parsed from the wrong bits, an address built from uninitialised memory, a colour that comes out negative, and a seeded generator that is not reproducible.
The changes
ipaddressnever range-checked the octets. Each was shifted into place and OR-ed in, so anything above 255 carried into its neighbour:ip_address("300.1.1.1")gave44.1.1.1andip_address("1.2.3.256")gave1.2.3.0.An octet too long for a small int was read as one anyway.
mp_parse_num_integerreturns a big-int object for a long enough literal, andMP_OBJ_SMALL_INT_VALUEon one of those reinterprets the object word:ip_address("99999999999.1.1.1")parsed as56.201.1.17on one run here and144.201.1.17on another, the top octet being whatever the object landed at.IPv4Addressused its buffer pointer whether or not it had been set. Anything that is not an int, a string or a buffer fell through the lastelsewithbufstill NULL andvaluenever written, and the address was then built from four bytes read at address zero —IPv4Address(object())gave0.4.0.32. It raises now.The dot count was only bounded from above. Three parts were rejected only because the third slot of
period_indexstays zero and the parse of a nonsensical length throws; requiring exactly three says what is meant.rainbowio.colorwheeldid nothing useful for a negative position. The range reduction cast the quotient touint32_t, socolorwheel(-1)returned-768andcolorwheel(-100)returned-11264, neither of which is a colour. The reduction is signed now, with one addition to bring the result back into[0, 256).random.seed(0xEDA4BABA)did not seed. The generator used its own initial pad value as the mark for "never seeded", so seeding with that exact number sent it to the hardware source instead and the sequence was not reproducible. A flag records it.random.randint(a, b)overflowed for the largestb. It callsrandrange(a, b + 1, 1), and forbat the top ofmp_int_tthat addition overflows, so the span was computed from a negative bound. It raises for that one value now. If you would rather it computed the range correctly instead of refusing it, say so and I will do that — refusing seemed better than returning a number drawn from a broken span, which is what it does today.Testing
Seeed XIAO nRF52840 Sense, on two builds differing only by these changes.
ipaddressfollowsCIRCUITPY_WIFIand this port has none, so both builds setCIRCUITPY_IPADDRESS=1.ip_address("300.1.1.1")44.1.1.1ValueErrorip_address("1.2.3.256")1.2.3.0ValueErrorip_address("99999999999.1.1.1")56.201.1.17, and144.201.1.17on a later runValueErrorIPv4Address(object())0.4.0.32ValueError: Invalid addressIPv4Address(b"\x01\x02\x03")ValueError, unchangedValueErrorcolorwheel(-1)-76816711680colorwheel(-100)-1126410965colorwheel(300)8094720, unchanged8094720seed(0xEDA4BABA)twice, then six drawsseed(12345)twicerandint(0, 2**31-1)ValueErrorTwo of the changes do not show on this port. Requiring exactly three dots only makes explicit what the parse already rejected by accident. And
mp_obj_get_int_maybewriting anmp_int_tthrough auint32_t *is the same width here; it is a defect only on a 64-bit build.No new translatable strings.