Skip to content

Two crash cases involving collections.namedtuple #12605

Description

@gwangmu

Description

We found two crash cases involving collections.namedtuple. The first (heap-buffer-overvflow at mp_seq_multiply) and second cases (null-dereference at mp_obj_equal_not_equal) both attempted to operate on a namedtuple object indirectly.

All PoCs were not straightforward to analyze as-is, so we compared the behaviors to the reference implementation (CPython). In the first case, CPython threw an exception while creating a namedtuple object (v4 in the PoC). In the second case, the exception happened while deriving a superclass of builtins (v6 in the PoC) using an already-created namedtuple object.

We've attached two PoCs for each cases.

poc.zip

Proof of Concept

$ # build unix port with ASAN, at the root source code directory.
$ export CC=clang
$ export CXX=clang++
$ export CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
$ export CXXFLAGS=$CFLAGS
$ export LDFLAGS=$CFLAGS
$ export DEBUG=1
$ make -C mpy-cross -j
$ make -C ports/unix -j all lib
$
$ # run a poc.
$ export ASAN_OPTIONS="detect_leaks=0"
$ ./ports/unix/build-standard/micropython <poc_file>

Environment

Ubuntu 20.04
Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz
Memory: 64 GB

Affected Version

v1.20.0 (commit a3862e7, latest as of 2023-09-26)
v1.20.0 (commit 813d559, 2023-06-19)
Discovered in the UNIX port version.

Activity

  1. stinos commented on Nov 8, 2023

    @stinos
    Contributor

    The crash in the first case is probably the same as #12830, comes down to the super not validating arguments: here for super(some_namedtuple_type, -5.0) there's no check that the second argument is effectively the namedtuple so later on this will crash treating -5.0 as a namedtuple in memory.
    But, even if super were validating arguments, the following also crashes (should print 0):

    import collections
    tup = collections.namedtuple("a", ["x"])
    print(super(tup, tup(2)).index(2))
    

    It's a bit hard to reproduce, but the culprit seems to be that the super object looks up 'index' which usually goes wrong because in micropython a namedtuple instance is actually a tuple, but that is not implemented as 'full' inheritance or because it's native inheritance or so - in any case, super calls into mp_obj_class_lookup and that succeeds but returns a bogus function object which tries to call tuple_index on something which is not actually a tuple.

    Note that

    import collections
    tup = collections.namedtuple("a", "field")
    tup(2).index(2)
    

    is fine in CPython but raises AttributeError: 'a' object has no attribute 'index' in micropython, which also explains why trying to do it anyway, as illustrated in previous paragraph, crashes.

    Second poc is much simpler and has noting to do with namedtuple, repro:

    (1,) * 9223372036854775807
    

    This should raise a MemoryError (unless you really have a lot of RAM) but in micropython calls mp_obj_malloc_var which comes down to an unchecked integer overflow because it does the multiplication sizeof( mp_obj_tuple_t ) + sizeof( mp_obj_t ) * ( 9223372036854775807 ) which is 24, so that only allocates space for 3 tuples but then tries to copy 9223372036854775807 of them into that memory.

  2. dpgeorge commented on Jul 25, 2024

    @dpgeorge
    Member

    The first case should be fixed by 093d0c0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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