uctypes: Element assignment through PTR silently fails
Port, board and/or hardware
unix port, longlong build
MicroPython version
MicroPython v1.26.0-preview.391.gba3f9cacf0.dirty
Reproduction
import uctypes
# Find the size corresponding to a pointer
ptr_size = uctypes.sizeof((uctypes.PTR, uctypes.INT8))
assert ptr_size in (4, 8)
UINTPTR = uctypes.UINT64 if ptr_size == 8 else uctypes.UINT32
# This descriptor lets us store an address via val then dereference it via ptr
descr = {
'ptr': (uctypes.PTR | 0, uctypes.INT8),
'val': UINTPTR | 0
}
# Build the data. `x.ptr` points at b2.
b1 = bytearray(uctypes.sizeof(descr))
b2 = bytearray(8)
x = uctypes.struct(uctypes.addressof(b1), descr)
x.val = uctypes.addressof(b2)
# Optimization: avoid rebuilding the `x.ptr` struct object
# (this has no influence over the bug)
ptr = x.ptr
# Access the data...
print("Setting via uctypes ptr, reading via bytearray")
for i in range(8):
ptr[i] = i
for i in range(8):
print(b2[i], end=" ")
#assert ptr[i] == i ## Assertion fails!
print()
print("Setting via bytearray, reading via ptr")
for i in range(8):
b2[i] = 10 + i
for i in range(8):
print(ptr[i], end=" ")
assert ptr[i] == 10 + i
print()
Expected behaviour
The first line printed should say 0 1 2 3 4 5 6 7 (the values assigned by ptr[i] = i)
Observed behaviour
Instead, it says 0 0 0 0 0 0 0 , because the element assignment is silently ignored.
Additional Information
over in m68k-micropython I'm using this change:
} else if (agg_type == PTR) {
byte *p = *(void **)self->addr;
if (mp_obj_is_small_int(t->items[1])) {
uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS);
- return get_aligned(val_type, p, index);
+ if (value == MP_OBJ_SENTINEL) {
+ return get_aligned(val_type, p, index);
+ } else {
+ set_aligned(val_type, p, index, value);
+ return value; // just !MP_OBJ_NULL
+ }
I can submit this as a PR, but I wanted to make sure the functionality wasn't intentionally missing for some reason I wasn't aware of.
Code of Conduct
Yes, I agree
bytearray(uctypes.bytes_at(0, 8)) segfaults in memcpy
Port, board and/or hardware
Unix port
MicroPython version
MicroPython v1.27.0-preview.107.gd1607598f on 2025-09-09; linux [GCC 14.2.0] version
Reproduction
import uctypes
arr = uctypes.bytes_at(0, 8)
arr2 = bytearray(arr)
arr2[0] = 1
Expected behaviour
A Python-level exception when the source buffer refers to an unreadable address (e.g., ValueError or OSError), or the unix port rejects bytes_at(0, 8) up front.
Observed behaviour
Program received signal SIGSEGV, Segmentation fault.
[#0] 0x7ffff7d95780 → __memcpy_avx_unaligned_erms()
[#1] 0x5555555fa3fd → memcpy(__dest=<optimised out>, __src=<optimised out>, __len=0x8)
[#2] 0x5555555fa3fd → array_construct(typecode=0x1, initializer=<optimised out>)
[#3] 0x5555555fa73b → bytearray_make_new(type_in=<optimised out>, n_args=0x1, n_kw=<optimised out>, args=0x7ffff7e27de0)
[#4] 0x555555618eff → type_call(self_in=<optimised out>, n_args=<optimised out>, n_kw=<optimised out>, args=<optimised out>)
[#5] 0x5555555ed738 → mp_call_function_n_kw(fun_in=0x55555571d070 <mp_type_bytearray>, n_args=0x1, n_kw=0x0, args=0x7ffff7e27de0)
[#6] 0x555555629936 → mp_execute_bytecode(code_state=0x7ffff7e27da0, inject_exc=<optimised out>)
[#7] 0x555555602482 → fun_bc_call(self_in=0x7ffff7e27ce0, n_args=<optimised out>, n_kw=<optimised out>, args=0x0)
[#8] 0x5555555ed738 → mp_call_function_n_kw(fun_in=0x7ffff7e27ce0, n_args=0x0, n_kw=0x0, args=0x0)
[#9] 0x5555555f0211 → mp_call_function_0(fun=<optimised out>)
Additional Information
No, I've provided everything above.
Code of Conduct
Yes, I agree