Skip to content

uc_mem_map crashes at unicorn!ram_block_add+0xf3 ? #2179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
YuHuanTin opened this issue May 5, 2025 · 13 comments
Closed

uc_mem_map crashes at unicorn!ram_block_add+0xf3 ? #2179

YuHuanTin opened this issue May 5, 2025 · 13 comments

Comments

@YuHuanTin
Copy link

I made an x64dbg plugin and ran it in x64dbg, but unfortunately it crashes!
I put any program like vcpkg.exe into the debugger and then right click on any function of that plugin and it crashes!
I have minidump and the following stack information
Here is the address of my plugin, I am using the latest x64dbg, snapshot_2025-03-15_15-57
https://github.com/YuHuanTin/CmakeVMHelp

To be honest, I can't really figure out why, so I came to ask :(

00007fff`02cceba4 c3              ret
rax=dddddddddddddddd rbx=000002450d8d2c80 rcx=0000000000004000
rdx=00007ffe9f937910 rsi=000000960f503548 rdi=000000960f5034f8
rip=00007ffe9e08bb73 rsp=000000960f5032f0 rbp=0000000000000109
 r8=000000960f503118  r9=0000000000000109 r10=0000000000000000
r11=0000000000000246 r12=000002450c551cb0 r13=000002450d9db330
r14=000000960f505290 r15=000000000000002a
iopl=0         nv up ei ng nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
unicorn!ram_block_add+0xf3:
00007ffe`9e08bb73 48394820        cmp     qword ptr [rax+20h],rcx ds:dddddddd`ddddddfd=????????????????
  *** Stack trace for last set context - .thread/.cxr resets it
 # RetAddr               : Args to Child                                                           : Call Site
00 00007ffe`9e089d09     : 00000245`0d9c2000 00000245`0f875fb0 00000096`0f503360 00000000`00004000 : unicorn!ram_block_add+0xf3 [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\qemu\exec.c @ 1080] 
01 00007ffe`9e089d6b     : 00000245`0d9c2000 00000000`00004000 00000000`00000000 00000245`0f7c0f50 : unicorn!qemu_ram_alloc_from_ptr_x86_64+0x159 [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\qemu\exec.c @ 1137] 
02 00007ffe`9e0968b5     : 00000245`0d9c2000 00000000`00004000 00000245`0f7c0f50 00007ffe`f0261540 : unicorn!qemu_ram_alloc_x86_64+0x2b [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\qemu\exec.c @ 1148] 
03 00007ffe`9e0974c6     : 00000245`0d9c2000 00000245`0f7c0f50 00000000`00004000 00000045`00000003 : unicorn!memory_region_init_ram_x86_64+0x85 [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\qemu\softmmu\memory.c @ 1570] 
04 00007ffe`9e0685e1     : 00000245`0d9c2000 00000045`b42fc000 00000000`00004000 00007fff`00000003 : unicorn!memory_map_x86_64+0x46 [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\qemu\softmmu\memory.c @ 50] 
05 00007ffe`f0250985     : 00000245`0d9c2000 00000045`b42fc000 00000000`00004000 00007ffe`00000003 : unicorn!uc_mem_map+0xa1 [D:\vcpkg\buildtrees\unicorn\src\2.1.3-fb40beb98c.clean\uc.c @ 1278] 
06 00007ffe`f024d218     : 00007ffe`f0261540 00000045`b42fc000 00000000`00004000 00000000`00000003 : CmakeVMHelp!VMExecute::sim_uc_mem_map+0x35 [C:\Users\YuHuanTin\Desktop\plugin_VMHelp\VMExecute.h @ 145] 
07 00007ffe`f0244788     : 00007ffe`f0261540 00000045`b42fec00 00000245`00001201 00000245`0000124c : CmakeVMHelp!Track::mem_map_range+0x158 [C:\Users\YuHuanTin\Desktop\plugin_VMHelp\Track.cpp @ 477] 
08 00007ffe`f02443c0     : 00007ffe`00000001 00000000`00000000 0000012b`00000381 00000383`00000496 : CmakeVMHelp!track_execute_exit+0x198 [C:\Users\YuHuanTin\Desktop\plugin_VMHelp\plugin.cpp @ 149] 
09 00007ffe`f0243bf5     : 00007ffe`f0261590 00000245`0d9c05f0 00000245`0d7abb30 00007ffe`d207e238 : CmakeVMHelp!track_execute+0x10 [C:\Users\YuHuanTin\Desktop\plugin_VMHelp\plugin.cpp @ 208] 
0a 00007ffe`d2705fdb     : 00000096`00000012 00000096`0f504500 ffffffff`fffffffe 00000109`00000001 : CmakeVMHelp!plugin_GuiEvent+0xa5 [C:\Users\YuHuanTin\Desktop\plugin_VMHelp\plugin.cpp @ 229] 

dump-05052025_2302210172.dmp

@wtdcode
Copy link
Member

wtdcode commented May 6, 2025

The crash seems weird, your uc ptr is null or invalid?

@YuHuanTin
Copy link
Author

Image
It at least has value, but I don't know how to tell if it's valid

@wtdcode
Copy link
Member

wtdcode commented May 6, 2025

What's the values of uc members at the crash site?

@YuHuanTin
Copy link
Author

YuHuanTin commented May 6, 2025

Image
That was just a dump file, so it didn't show the extra memory for the crash, and now that I debugged it today, it now looks like this
@wtdcode


Image

Image

then I debugged and found that the second time he did the RAMBLOCK_FOREACH macro the block was 0xdddddddddddddddd

@wtdcode
Copy link
Member

wtdcode commented May 6, 2025

Your uc pointer is corrupted and not our fault. Please debug your program =)

@YuHuanTin
Copy link
Author

YuHuanTin commented May 6, 2025

I think I got it, I forgot to return true when UC_MEM_FETCH_UNMAPPED happened, and then when I did uc_mem_map() I kept getting UC_ERR_NOMEM errors.

Image

@wtdcode
Copy link
Member

wtdcode commented May 6, 2025

If you do not return true in unmapped hooks, you should get errors from uc_emu_start, no?

@YuHuanTin
Copy link
Author

As far as I know it doesn't seem to be the case, it crashes before I call uc_emu_start, here is my minimal example code, but it sometimes crashes and sometimes doesn't (I don't know why and it crashes every time inside x64dbg)

bool hook_mem_unmapped(
    uc_engine * uc,
    uc_mem_type type,
    uint64_t    address,
    int         size,
    int64_t     value,
    void *      user_data
) {
    return false;
}

struct SegmentSelector {
    union {
        struct {
            uint16_t rpl  : 2;
            uint16_t table: 1;
            uint16_t index: 13;
        };

        uint64_t desc;
    };
};

struct SegmentDescriptor {
    union {
        struct {
            uint16_t limit0;
            uint16_t base0;
            uint8_t  base1;
            uint8_t  type       : 4;
            uint8_t  system     : 1;
            uint8_t  dpl        : 2;
            uint8_t  present    : 1;
            uint8_t  limit1     : 4;
            uint8_t  avail      : 1;
            uint8_t  is_64_code : 1;
            uint8_t  db         : 1;
            uint8_t  granularity: 1;
            uint8_t  base2;
        };

        uint64_t desc;
    };
};

int main() {
    uc_engine *uc_ = nullptr;
    uc_open(UC_ARCH_X86, UC_MODE_64, &uc_);
    uc_hook passUnMapped;

    auto       err = uc_hook_add(uc_, &passUnMapped, UC_HOOK_MEM_UNMAPPED, hook_mem_unmapped, nullptr, 1, 0);
    uc_x86_mmr gdtr;

    const uint64_t     m_gdt_address = 0xc000000000000000;
    SegmentDescriptor *gdt           = (struct SegmentDescriptor *) malloc(31 * sizeof(struct SegmentDescriptor));

    SegmentSelector r_gs = {};
    r_gs.desc            = 0x2B;

    gdtr.base  = m_gdt_address;
    gdtr.limit = 31 * sizeof(struct SegmentDescriptor) - 1;

    err = uc_reg_write(uc_, UC_X86_REG_GDTR, &gdtr);

    // call the UC_HOOK_MEM_UNMAPPED hook, and return false
    err = uc_reg_write(uc_, UC_X86_REG_GS, &r_gs);
    free(gdt);

    // both UC_ERR_NOMEM, but sometimes crash the program
    err = uc_mem_map(uc_, 0x00007ffe0e250000, 0x0000000000001000, 1);
    err = uc_mem_map(uc_, 0x00007ffe0e251000, 0x000000000011b000, 5);
    err = uc_mem_map(uc_, 0x000000927fcfc000, 0x0000000000004000, 3);
    err = uc_mem_map(uc_, 0x00007ffe0e250000, 0x0000000000001000, 1);
}

It may crash in the following two places, the uc_mem_map address I passed in is the segment where the RIP and RSP of the program being debugged by x64dbg reside
Image

@wtdcode
Copy link
Member

wtdcode commented May 8, 2025

I can't reproduce your bug. Note I modified your case to C:

#include <unicorn/unicorn.h>
#include <string.h>

bool hook_mem_unmapped(
    uc_engine * uc,
    uc_mem_type type,
    uint64_t    address,
    int         size,
    int64_t     value,
    void *      user_data
) {
    return false;
}

struct SegmentSelector {
    union {
        struct {
            uint16_t rpl  : 2;
            uint16_t table: 1;
            uint16_t index: 13;
        };

        uint64_t desc;
    };
};

struct SegmentDescriptor {
    union {
        struct {
            uint16_t limit0;
            uint16_t base0;
            uint8_t  base1;
            uint8_t  type       : 4;
            uint8_t  system     : 1;
            uint8_t  dpl        : 2;
            uint8_t  present    : 1;
            uint8_t  limit1     : 4;
            uint8_t  avail      : 1;
            uint8_t  is_64_code : 1;
            uint8_t  db         : 1;
            uint8_t  granularity: 1;
            uint8_t  base2;
        };

        uint64_t desc;
    };
};

int main() {
    uc_engine *uc_ = NULL;
    uc_open(UC_ARCH_X86, UC_MODE_64, &uc_);
    uc_hook passUnMapped;

    uc_err       err = uc_hook_add(uc_, &passUnMapped, UC_HOOK_MEM_UNMAPPED, hook_mem_unmapped, NULL, 1, 0);
    uc_x86_mmr gdtr;

    const uint64_t     m_gdt_address = 0xc000000000000000;
    struct SegmentDescriptor *gdt           = (struct SegmentDescriptor *) malloc(31 * sizeof(struct SegmentDescriptor));

    struct SegmentSelector r_gs;
    memset(&r_gs, 0, sizeof(struct SegmentSelector));
    r_gs.desc            = 0x2B;

    gdtr.base  = m_gdt_address;
    gdtr.limit = 31 * sizeof(struct SegmentDescriptor) - 1;

    err = uc_reg_write(uc_, UC_X86_REG_GDTR, &gdtr);

    // call the UC_HOOK_MEM_UNMAPPED hook, and return false
    err = uc_reg_write(uc_, UC_X86_REG_GS, &r_gs);
    free(gdt);

    // both UC_ERR_NOMEM, but sometimes crash the program
    err = uc_mem_map(uc_, 0x00007ffe0e250000, 0x0000000000001000, 1);
    err = uc_mem_map(uc_, 0x00007ffe0e251000, 0x000000000011b000, 5);
    err = uc_mem_map(uc_, 0x000000927fcfc000, 0x0000000000004000, 3);
    err = uc_mem_map(uc_, 0x00007ffe0e250000, 0x0000000000001000, 1);
}

are you on dev branch?

@YuHuanTin
Copy link
Author

Image
Yes, I can still reproduce it on the dev branch, I probably re-run it 7 or 8 times before he crashes once (it doesn't seem to be able to reproduce it stably)

2025-05-08.16-05-55.mp4

@wtdcode
Copy link
Member

wtdcode commented May 8, 2025

This reminds of turning to ASAN:

=================================================================
==3080441==ERROR: AddressSanitizer: heap-use-after-free on address 0x6070000002d0 at pc 0x7fb9cfcad7d0 bp 0x7ffc9e49a440 sp 0x7ffc9e49a430
READ of size 8 at 0x6070000002d0 thread T0
    #0 0x7fb9cfcad7cf in find_ram_offset_last /home/mio/opensource/unicorn/qemu/exec.c:963
    #1 0x7fb9cfcad8ff in find_ram_offset /home/mio/opensource/unicorn/qemu/exec.c:987
    #2 0x7fb9cfcadcee in ram_block_add /home/mio/opensource/unicorn/qemu/exec.c:1057
    #3 0x7fb9cfcae700 in qemu_ram_alloc_from_ptr_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1135
    #4 0x7fb9cfcae790 in qemu_ram_alloc_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1147
    #5 0x7fb9cfcc5ae7 in memory_region_init_ram_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:1570
    #6 0x7fb9cfcb986e in memory_map_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:49
    #7 0x7fb9cfc74ec4 in uc_mem_map /home/mio/opensource/unicorn/uc.c:1271
    #8 0x55a251b92675 in main /home/mio/opensource/unicorn/samples/sample_x86.c:75
    #9 0x7fb9cf229d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #10 0x7fb9cf229e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #11 0x55a251b92224 in _start (/home/mio/opensource/unicorn/build_asan/sample_x86+0x1224)

0x6070000002d0 is located 16 bytes inside of 72-byte region [0x6070000002c0,0x607000000308)
freed by thread T0 here:
    #0 0x7fb9d2ab4537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
    #1 0x7fb9cfc8c529 in g_free /home/mio/opensource/unicorn/glib_compat/gmem.c:256
    #2 0x7fb9cfcae74f in qemu_ram_alloc_from_ptr_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1138
    #3 0x7fb9cfcae790 in qemu_ram_alloc_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1147
    #4 0x7fb9cfcc5ae7 in memory_region_init_ram_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:1570
    #5 0x7fb9cfcb986e in memory_map_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:49
    #6 0x7fb9cfc74ec4 in uc_mem_map /home/mio/opensource/unicorn/uc.c:1271
    #7 0x55a251b92628 in main /home/mio/opensource/unicorn/samples/sample_x86.c:74
    #8 0x7fb9cf229d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

previously allocated by thread T0 here:
    #0 0x7fb9d2ab4a57 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154
    #1 0x7fb9cfc8c3ed in g_malloc0 /home/mio/opensource/unicorn/glib_compat/gmem.c:139
    #2 0x7fb9cfcae4d4 in qemu_ram_alloc_from_ptr_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1121
    #3 0x7fb9cfcae790 in qemu_ram_alloc_x86_64 /home/mio/opensource/unicorn/qemu/exec.c:1147
    #4 0x7fb9cfcc5ae7 in memory_region_init_ram_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:1570
    #5 0x7fb9cfcb986e in memory_map_x86_64 /home/mio/opensource/unicorn/qemu/softmmu/memory.c:49
    #6 0x7fb9cfc74ec4 in uc_mem_map /home/mio/opensource/unicorn/uc.c:1271
    #7 0x55a251b92628 in main /home/mio/opensource/unicorn/samples/sample_x86.c:74
    #8 0x7fb9cf229d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-use-after-free /home/mio/opensource/unicorn/qemu/exec.c:963 in find_ram_offset_last
Shadow bytes around the buggy address:
  0x0c0e7fff8000: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
  0x0c0e7fff8010: fa fa 00 00 00 00 00 00 00 00 05 fa fa fa fa fa
  0x0c0e7fff8020: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa 00 00
  0x0c0e7fff8030: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
  0x0c0e7fff8040: 00 00 00 00 00 fa fa fa fa fa 00 00 00 00 00 00
=>0x0c0e7fff8050: 00 00 00 fa fa fa fa fa fd fd[fd]fd fd fd fd fd
  0x0c0e7fff8060: fd fa fa fa fa fa 00 00 00 00 00 00 00 00 00 fa
  0x0c0e7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff80a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==3080441==ABORTING

Cool, looks like indeed a bug happening.

@PhilippTakacs
Copy link
Contributor

I belive I know what happen: the second memory mappings fails (probably because of not not enough memory) this sets uc->invalid_error to UC_ERR_NOMEM, which is never cleared. The next uc_mem_map can allocate the block and update the internal data structures (ram_list) but the check for invalid_error still triggers and frees the new block. The last uc_mem_map try to use this already freed block and crashes.

can you try this patch:

diff --git a/qemu/softmmu/memory.c b/qemu/softmmu/memory.c
index 08e747e8..7fc4c52e 100644
--- a/qemu/softmmu/memory.c
+++ b/qemu/softmmu/memory.c
@@ -49,6 +49,7 @@ MemoryRegion *memory_map(struct uc_struct *uc, hwaddr begin, size_t size, uint32
     memory_region_init_ram(uc, ram, size, perms);
     if (ram->addr == -1 || !ram->ram_block) {
         // out of memory
+        uc->invalid_error = UC_ERR_OK;
         g_free(ram);
         return NULL;
     }

I'm not sure if this is the correct place, but it should work.

@wtdcode
Copy link
Member

wtdcode commented Jun 8, 2025

I confirmed the patch fixes the issue. Thanks for @PhilippTakacs

@wtdcode wtdcode closed this as completed Jun 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants