块头原写在 mmap 映射区起点之前 8 字节;第一次 malloc 时 mmap 返回用户堆起点 0x20000000,写 0x1ffffff8 落在映射区外 ⇒ #PF(err=0x7 用户态写、cr2=0x1ffffff8),四个 app 启动瞬间全部崩在同一个 stdlib.malloc 上。改为把块头写在映射区起始处 ptr,返回 ptr+hdr_size 不变,free/calloc/realloc 的 ptr-hdr_size 反查仍然正确。
60 lines
1.9 KiB
Plaintext
60 lines
1.9 KiB
Plaintext
import vpsdk.syscall as _sc
|
||
import string
|
||
|
||
|
||
define HEAP_BLOCK_MAGIC = 0x48454150
|
||
|
||
class _heap_hdr:
|
||
magic: uint32
|
||
size: uint32
|
||
|
||
_heap_inited: int = 0
|
||
|
||
def _heap_init():
|
||
global _heap_inited
|
||
_heap_inited = 1
|
||
|
||
export def malloc(size: sizet) -> rawptr:
|
||
global _heap_inited
|
||
if size == 0: return None
|
||
if _heap_inited == 0: _heap_init()
|
||
hdr_size: sizet = _heap_hdr.__sizeof__()
|
||
total: sizet = hdr_size + size
|
||
ptr: uint64 = _sc._syscall1(uint64(_sc.SYSCALL_MMAP), uint64(total))
|
||
if ptr == 0: return None
|
||
# 块头必须落在**映射区内部**(映射起点 ptr 处),不能写 ptr - hdr_size:
|
||
# 那 8 字节在映射之外 —— 第一次 malloc 时 mmap 返回的正是用户堆起点 0x20000000,
|
||
# 写 0x1ffffff8 就是映射区外 ⇒ #PF(err=0x7 用户态写),四个 app 启动即崩的现场。
|
||
hdr: _heap_hdr* = _heap_hdr*(uint64(ptr))
|
||
hdr.magic = HEAP_BLOCK_MAGIC
|
||
hdr.size = uint32(size)
|
||
return rawptr(ptr + hdr_size)
|
||
|
||
export def free(ptr: rawptr):
|
||
if ptr is None: return
|
||
hdr_size: sizet = _heap_hdr.__sizeof__()
|
||
hdr: _heap_hdr* = _heap_hdr*(uint64(ptr) - hdr_size)
|
||
if hdr.magic != HEAP_BLOCK_MAGIC: return
|
||
|
||
export def calloc(nmemb: sizet, size: sizet) -> rawptr:
|
||
total: sizet = nmemb * size
|
||
p: rawptr = malloc(total)
|
||
if p: string.memset(p, 0, total)
|
||
return p
|
||
|
||
export def realloc(ptr: rawptr, size: sizet) -> rawptr:
|
||
if ptr is None: return malloc(size)
|
||
if size == 0:
|
||
free(ptr)
|
||
return None
|
||
hdr_size: sizet = _heap_hdr.__sizeof__()
|
||
hdr: _heap_hdr* = _heap_hdr*(uint64(ptr) - hdr_size)
|
||
if hdr.magic != HEAP_BLOCK_MAGIC: return None
|
||
old_size: sizet = hdr.size
|
||
new_ptr: rawptr = malloc(size)
|
||
if new_ptr is None: return None
|
||
copy_size: sizet = old_size if old_size < size else size
|
||
string.memcpy(new_ptr, ptr, copy_size)
|
||
free(ptr)
|
||
return new_ptr
|