Skip to content

uc_context_alloc leaves header fields uninitialized; misuse aborts the host #2319

Description

@MarkLee131

This is a robustness bug in the spirit of #1766 ("The code should return errors instead of aborting/exitting"):
uc_context_alloc (uc.c:2241) calls g_malloc(size) and only explicitly zeros fv (uc.c:2246). snapshot_level, ramblock_freed, and
last_block stay uninitialized. If a caller does uc_context_alloc + uc_context_restore without an intervening uc_context_save, restore happily writes those garbage values into the engine (uc.c:2567/2578/2579) and returns OK. The next emulation hits g_assert(ret < cpu->num_ases && ret >= 0) in cpu_asidx_from_attrs (qemu/include/hw/core/cpu.h:421) and aborts the host process.

A Python user sees the interpreter abort with no Python-side exception.

Repro

/* repro_ctx_restore_no_save.c */
#include <stdio.h>
#include <unicorn/unicorn.h>

int main(void) {
    uc_engine *uc;
    uc_open(UC_ARCH_X86, UC_MODE_64, &uc);

    uc_context *ctx;
    uc_context_alloc(uc, &ctx);
    uc_context_restore(uc, ctx);   /* returns UC_ERR_OK silently */

    uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL);
    uint8_t nop = 0x90;
    uc_mem_write(uc, 0x1000, &nop, 1);
    uc_emu_start(uc, 0x1000, 0x1001, 0, 1);
}

Build the same way as a stock ASan debug build:

CC=clang CFLAGS="-fsanitize=address,undefined -g -O1" \
  cmake .. -DUNICORN_ARCH=x86 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Debug
cmake --build . -j
clang -fsanitize=address,undefined -g -O1 -I../include \
  repro_ctx_restore_no_save.c libunicorn.a -lpthread -o repro
./repro

Output:

calling uc_context_restore WITHOUT prior uc_context_save...
ret = OK (UC_ERR_OK)
Assertion failed: (ret < cpu->num_ases && ret >= 0),
  function cpu_asidx_from_attrs, file cpu.h, line 421.

Reproduces both with apple clang and homebrew clang ASan-only builds.

Suggested fix

Change g_malloc(size) to g_malloc0(size) at uc.c:2241. One line. The explicit fv = NULL at 2246 then becomes redundant but harmless.

If you'd prefer to keep g_malloc for the variable-length body for any reason, zero just the header bytes (sizeof uc_context minus data[])
before returning.

A more conservative variant is to set a "saved" flag in the header in uc_context_save and refuse uc_context_restore (return UC_ERR_ARG) if the flag is unset. That also closes #1766 for this entry point.

Happy to send the patch + a tests/unit/test_ctl.c regression test against dev.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions