micropython / micropython/micropython

Crash/segfault in async code

Open
#18,559 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C
Stars
22.1k
Forks
9k
Avg merge
6d 4h
Merged PRs (30d)
16

Description

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 equivalent
  • git clone -b bughunt --depth 15 https://github.com/M-o-a-T/moat.git
  • git submodule update ---init --recursive
  • pip install -r requirements.txt # or equivalent
  • (cd ext/micropython/ports/unix; make DEBUG=1 COPT=-O0 CSUPEROPT=-O0 ) # CSUPEROPT is required to debug vm.c
  • pytest -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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read py/objexcept.c at mp_obj_exception_add_traceback and py/vm.c at the reported call site. First build the Unix port with DEBUG=1, COPT=-O0, and CSUPEROPT=-O0, then run pytest -sx tests/moat_micro/test_micro.py::test_iter_m and inspect the failure in GDB. Done means the test completes without the MicroPython SIGSEGV.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
compilers, embedded-iot
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.