Skip to content

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

@MarkLee131

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions