Crash/segfault in async code
Port, board and/or hardware
Unix
MicroPython version
MicroPython v1.27.0-18.g3bca4bdbde
The commit is the TaskGroup branch https://github.com/micropython/micropython/pull/8791 (possibly outdated, I need to do some cleanup, but nothing material).
Reproduction
Unfortunately I have not yet found a small(ish)-enough reproducer.
Steps to re-create the problem:
apt install git build-essential python3-dev libffi-dev pkg-config# or equivalentgit clone -b bughunt --depth 15 https://github.com/M-o-a-T/moat.gitgit submodule update ---init --recursivepip install -r requirements.txt# or equivalent(cd ext/micropython/ports/unix; make DEBUG=1 COPT=-O0 CSUPEROPT=-O0 )# CSUPEROPT is required to debug vm.cpytest -sx tests/moat_micro/test_micro.py::test_iter_m
The last step starts micropython and emits a command line for GDB. MicroPython segfaults shortly after you continue.
Expected behaviour
The test works, or at least it won't crash MicroPython.
Observed behaviour
Program received signal SIGSEGV, Segmentation fault.
0x0000558b239997e0 in mp_obj_exception_add_traceback (self_in=0x7f669081dc80, file=1994,
line=357, block=138) at ../../py/objexcept.c:636
636 self->traceback_len += TRACEBACK_ENTRY_LEN;
(gdb) whe
#0 0x000055bb494e8960 in mp_obj_exception_add_traceback (self_in=0x7fdd5d05a840, file=1994,
line=357, block=138) at ../../py/objexcept.c:636
#1 0x000055bb4957cb5d in mp_execute_bytecode (code_state=0x7fdd5d05a770,
inject_exc=<optimized out>) at ../../py/vm.c:1461
#2 0x000055bb494ee461 in mp_obj_gen_resume (self_in=0x7fdd5d05a760, send_value=0x6,
throw_value=0x0, ret_val=0x7ffd379fbe38) at ../../py/objgenerator.c:210
#3 0x000055bb494adce1 in mp_resume (self_in=0x7fdd5d05a760, send_value=0x6, throw_value=0x0,
ret_val=0x7ffd379fbe38) at ../../py/runtime.c:1424
#4 0x000055bb4957e447 in mp_execute_bytecode (code_state=0x7fdd5d04af50,
inject_exc=<optimized out>) at ../../py/vm.c:1230
#5 0x000055bb494ee461 in mp_obj_gen_resume (self_in=0x7fdd5d04af40, send_value=0x6,
throw_value=0x0, ret_val=0x7ffd379fbfe8) at ../../py/objgenerator.c:210
#6 0x000055bb494adce1 in mp_resume (self_in=0x7fdd5d04af40, send_value=0x6, throw_value=0x0,
ret_val=0x7ffd379fbfe8) at ../../py/runtime.c:1424
...
(gdb) inf locals
self = 0x55bb496bd140 <native_base_init_wrapper_obj>
tb_data = 0x55bb4954d6d9 <native_base_init_wrapper>
(gdb) p *self
$5 = {base = {type = 0x55bb496bb9b0 <mp_type_fun_builtin_var>}, traceback_alloc = 262143,
traceback_len = 0, traceback_data = 0x55bb4954d6d9 <native_base_init_wrapper>, args = 0x0}
Needless to say this is not supposed to happen.
(gdb) fr 1
#1 0x0000558b23a34e49 in mp_execute_bytecode (code_state=0x7f669081dbb0, inject_exc=0x0)
at ../../py/vm.c:1461
1461 mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name);
(gdb) inf locals
bytecode_start = 0x7f66907c52cf "\260\023y\022 \357CH\022\201E#\0064\001e\260\023i\022\034\334DC\022#e\260\024z6"
n_pos_args = 1
bc = 26
block_name = 138
n_kwonly_args = 0
line_info_top = 0x7f66907c52cf "\260\023y\022 \357CH\022\201E#\0064\001e\260\023i\022\034\334DC\022#e\260\024z6"
ip = 0x7f66907c52c8 "\220a(((#)\260\023y\022 \357CH\022\201E#\0064\001e\260\023i\022\034\334DC\022#e\260\024z6"
n_state = 8
n_exc_stack = 0
scope_flags = 7
n_def_pos_args = 0
n_info = 10
n_cell = 0
source_file = 1994
source_line = 357
nlr = {prev = 0x7fff5e2b9da0, ret_val = 0x7f669081dc80, regs = {
0x558b23a2d77b <mp_execute_bytecode+186>, 0x7fff5e2b9cb0, 0x7fff5e2b97c8, 0x7f669080ea00,
0x0, 0x7fff5e2bf5e0, 0x7f6690ce1000 <_rtld_global>, 0x558b23b685d8}}
entry_table = {0x558b23a335a5 <mp_execute_bytecode+24292> <repeats 16 times>,
... ... ...
0x558b23a335a5 <mp_execute_bytecode+24292>, 0x558b23a335a5 <mp_execute_bytecode+24292>}
fastn = 0x7f669081dc10
exc_stack = 0x7f669081dc18
exc_sp = 0x7f669081dc00
__PRETTY_FUNCTION__ = "mp_execute_bytecode"
(
Additional Information
I tried 1.24 through 1.26 and got the same problem.
This is 100% reproducible on my system (Debian Trixie, amd64). Running valgrind on MicroPython doesn't report anything suspicious.
The code that triggers the problem is in moat/micro/_embed/lib/moat/lib/cmd/msg.py line 357
async def send(self, *a, **kw) -> None: # noqa: D102
if not self._dir & SD_OUT:
raise RuntimeError("This stream is read only")
if self._stream_out != S_ON:
*** raise NoStream
await self._skipped()
await self.ml_send(a, kw, B_STREAM)
called from moat/micro/_embed/lib/app/_test.py line 50
async def stream_it(self, msg: Msg):
"Streams numbers."
lim = msg.get("lim", -1)
i = 0
d = int(msg.get("delay", 0.1) * 1000)
async with msg.stream_out() as s:
while i != lim:
await sleep_ms(d)
try:
*** await s.send(i)
except NoStream:
break
i += 1
At least one other test (tests/moat_micro/test_cfg.py::test_cfg) calls to line 357 without crashing.
Code of Conduct
Yes, I agree
Native & viper failures with setjmp exception handling
Port, board and/or hardware
unix
MicroPython version
MicroPython v1.26.0-preview.169.g429478dcfc.dirty on 2025-06-02; linux [GCC 12.2.0] version
(modified from v1.26.0-preview-166-gc0111e63b3)
Reproduction
- grab my branch at https://github.com/jepler/circuitpython/tree/coverage-setjmp
- in ports/unix
make VARIANT=coverage_setjmp test
Expected behaviour
Expected all tests to pass
Observed behaviour
The native_gen.py test fails with a segfault. This is the minimized version of the crashing code:
# catching an exception from .throw()
@micropython.native
def gen3():
try:
yield 1
except Exception as er:
print("caught", repr(er))
g = gen3()
print(g.throw(ValueError(42)))
Additional Information
The branch above has 3 changes:
- introduce a unix variant that uses setjmp for exception handling
- fix a build error in
mp_native_yield_from - turns on N_NLR_SETJMP in emitnative whenever MICROPY_NLR_SETJMP is enabled and N_NLR_SETJMP is not otherwise used (note: this fixes other errors in
make testthat occur with MICROPY_NLR_SETJMP enabled)
It is vaguely possible that my change in mp_native_yield_from is wrong and I considered whether it might contribute to the crash, but in my reduced testcase a gdb breakpoint on that function isn't hit so I don't think that's it.
Code of Conduct
Yes, I agree