Skip to content

pin vs stream confusion leads to undefined behavior and Asan diagnostic #17852

@jepler

Description

@jepler

Port, board and/or hardware

unix port, coverage variant w/address sanitizer, x86_64 linux

MicroPython version

MicroPython v1.26.0-preview.524.g255d74b5a8 on 2025-08-06; linux [GCC 12.2.0] version

Reproduction

Build with . tools/ci.sh; ci_unix_sanitize_address_build. Then, run the following code (I did it at the repl):

>>> import machine
>>> import ssl
>>> ctx = ssl.SSLContext(1)
>>> p = machine.PinBase()
>>> ctx.wrap_socket(p)

Expected behaviour

ctx.wrap_socket should raise an exception, because a PinBase does not implement the stream protocol. For instance, ctx.wrap_socket(1) throws OSError: stream operation not supported.

Observed behaviour

>>> ctx.wrap_socket(p)
=================================================================
==2165677==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5585c4169a28 at pc 0x5585c3f6a4fb bp 0x7ffe114fe930 sp 0x7ffe114fe928
READ of size 8 at 0x5585c4169a28 thread T0
    #0 0x5585c3f6a4fa in mp_get_stream_raise ../../py/stream.c:102
    #1 0x5585c3fb01cc in ssl_socket_make_new ../../extmod/modtls_mbedtls.c:623
    #2 0x5585c3fb0a30 in ssl_context_wrap_socket ../../extmod/modtls_mbedtls.c:519
    #3 0x5585c3f3c338 in fun_builtin_var_call ../../py/objfun.c:118
    #4 0x5585c3f1c0d8 in mp_call_function_n_kw ../../py/runtime.c:727
    #5 0x5585c3f1ca56 in mp_call_method_n_kw ../../py/runtime.c:743
    #6 0x5585c3f7e435 in mp_execute_bytecode ../../py/vm.c:1069
    #7 0x5585c3f3c700 in fun_bc_call ../../py/objfun.c:295
    #8 0x5585c3f1c0d8 in mp_call_function_n_kw ../../py/runtime.c:727
    #9 0x5585c3f1ca56 in mp_call_method_n_kw ../../py/runtime.c:743
    #10 0x5585c3f7e435 in mp_execute_bytecode ../../py/vm.c:1069
    #11 0x5585c3f3c700 in fun_bc_call ../../py/objfun.c:295
    #12 0x5585c3f1c0d8 in mp_call_function_n_kw ../../py/runtime.c:727
    #13 0x5585c3f2006a in mp_call_function_0 ../../py/runtime.c:701
    #14 0x5585c4091d84 in execute_from_lexer /home/jepler/src/micropython/ports/unix/main.c:162
    #15 0x5585c40923fb in do_repl /home/jepler/src/micropython/ports/unix/main.c:273
    #16 0x5585c4093b3d in main_ /home/jepler/src/micropython/ports/unix/main.c:753
    #17 0x5585c4093d6d in main /home/jepler/src/micropython/ports/unix/main.c:494
    #18 0x7f12f5e46249 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #19 0x7f12f5e46304 in __libc_start_main_impl ../csu/libc-start.c:360
    #20 0x5585c3ec0bf0 in _start (/home/jepler/src/micropython/ports/unix/build-coverage/micropython+0x1a7bf0)

0x5585c4169a28 is located 56 bytes to the left of global variable 'pinbase_singleton' defined in '../../extmod/machine_pinbase.c:43:27' (0x5585c4169a60) of size 8
0x5585c4169a28 is located 0 bytes to the right of global variable 'pinbase_pin_p' defined in '../../extmod/machine_pinbase.c:75:25' (0x5585c4169a20) of size 8
SUMMARY: AddressSanitizer: global-buffer-overflow ../../py/stream.c:102 in mp_get_stream_raise

Pin objects and stream objects both fill the protocol spot. However, their protocol definitions are different and incompatible.

Without ASan, this resulted in an exception (yay!) but this is by luck, because mp_get_stream_raise can't tell that the protocol slot of the object is an incompatible "Pin" protocol (with only ioctl) rather than the standard "Stream" protocol (with read/write/ioctl).

Additional Information

Somewhat related: #17714 (can pass an object without protocol to machine.time_pulse_us; missing check whether the protocol slot is filled at all).

There's a third leg to the problem, in which this calls read a bunch of times on sys.stdout (due to incorrectly calling through the read function of mp_stream_p_t):

>>> machine.time_pulse_us(sys.stdout, .001)
-2

For once this wasn't from fuzzing. 😜

Code of Conduct

Yes, I agree

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions