From 92ac78dc37e0a7432bf90c8494b2a76cc54647dc Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Thu, 13 Aug 2026 21:18:50 +0800 Subject: [PATCH] LATX, fix: Separate KZT host pointers from guest VAs KZT previously treated every address as a valid guest address so native wrapper pointers and bridge entry points could be accessed. That also let Wine mapping probes escape the reserved guest virtual-address window and reach host mmap addresses. Keep guest mapping validation strict, while retaining a separate compatibility path for host-owned wrapper data. Register bridge slots and classify executable KZT entries by exact membership instead of their numeric address, including the X11 TB bridge table. Snapshot bridge metadata while holding the registry lock so translation and logging cannot race with bridge teardown. Add unit coverage for guest bounds, host pointers, zero lengths, crossing ranges, and overflow. Signed-off-by: Hanlu Li --- accel/tcg/cpu-exec.c | 9 +- accel/tcg/translate-all.c | 5 - accel/tcg/user-exec.c | 21 +++- include/exec/cpu_ldst.h | 36 +++++-- linux-user/qemu.h | 17 ++++ target/i386/latx/context/bridge.c | 98 ++++++++++++++++++- target/i386/latx/context/wrappertbbridge.c | 14 ++- target/i386/latx/include/bridge.h | 3 + target/i386/latx/include/kzt-address-policy.h | 45 +++++++++ target/i386/latx/include/wrappertbbridge.h | 1 + target/i386/latx/latx-config.c | 14 ++- target/i386/latx/translator/tr-misc.c | 36 ++++--- target/i386/latx/translator/translate.c | 7 +- tests/unit/kzt/test_kzt_address_policy.c | 57 +++++++++++ tests/unit/meson.build | 15 +++ 15 files changed, 333 insertions(+), 45 deletions(-) create mode 100644 target/i386/latx/include/kzt-address-policy.h create mode 100644 tests/unit/kzt/test_kzt_address_policy.c diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 1f0d6af6038..0e6a156bcdc 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -42,8 +42,7 @@ #include "tcg/tcg.h" #include "library.h" #include "fileutils.h" -#include "bridge_private.h" -void *getAlternate(void *addr); +#include "bridge.h" extern struct elfheader_s * elf_header; #endif #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) @@ -195,8 +194,10 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) if (qemu_loglevel_mask(CPU_LOG_EXEC)) { #ifdef CONFIG_LATX_KZT Dl_info dl_info; - if (latx_kzt_runtime_enabled() && itb->pc > reserved_va && - dladdr ((const void *)((onebridge_t *)itb->pc)->f, &dl_info)) { + uintptr_t function; + if (latx_kzt_runtime_enabled() && + kzt_registered_onebridge_snapshot(itb->pc, NULL, &function) && + dladdr((const void *)function, &dl_info)) { qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, "pid(%d) - tid(%" PRIuPTR ") Trace cpu%d: %p [ " TARGET_FMT_lx "/%#x] KZT:%s\n", diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 6cac103e56c..f4d515e257a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -3816,11 +3816,6 @@ void *page_alloc_target_data(target_ulong address, size_t size) bool page_check_range(target_ulong start, target_ulong len, int flags) { -#if defined(CONFIG_LATX_KZT) - if (latx_kzt_runtime_enabled() && start > reserved_va) { - return true; - } -#endif target_ulong last; int locked; /* tri-state: =0: unlocked, +1: global, -1: local */ bool ret; diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index b5f38f390b1..748f8177cac 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -37,6 +37,10 @@ #include "latx-options.h" #include "latx-backtrace.h" #include "latx-smc.h" +#if defined(CONFIG_LATX_KZT) +#include "bridge.h" +#include "wrappertbbridge.h" +#endif #endif #ifdef CONFIG_LATX_DEBUG #include "latx-debug.h" @@ -724,8 +728,21 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, g_assert_not_reached(); } - if (!guest_addr_valid_untagged(addr) || - !page_check_range(addr, 1, flags)) { + bool valid = guest_addr_valid_untagged(addr) && + page_check_range(addr, 1, flags); + +#if defined(CONFIG_LATX_KZT) + if (!valid && latx_kzt_runtime_enabled()) { + if (access_type == MMU_INST_FETCH) { + valid = kzt_is_registered_onebridge(addr) || + kzt_tbbridge_contains(addr); + } else if (!guest_addr_valid_untagged(addr)) { + valid = kzt_data_addr_valid_untagged(addr); + } + } +#endif + + if (!valid) { if (nonfault) { return TLB_INVALID_MASK; } else { diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 9e0fd328e33..71502fdd069 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -59,6 +59,7 @@ #if defined(CONFIG_LATX_KZT) #include "kzt-runtime.h" +#include "kzt-address-policy.h" #endif #if defined(CONFIG_USER_ONLY) /* sparc32plus has 64bit long but 32bit space address @@ -93,23 +94,42 @@ static inline void *g2h(CPUState *cs, abi_ptr x) static inline bool guest_addr_valid_untagged(abi_ulong x) { #if defined(CONFIG_LATX_KZT) - if (latx_kzt_runtime_enabled()) { - return true; - } else -#endif + return kzt_guest_addr_is_valid(x, GUEST_ADDR_MAX); +#else return x <= GUEST_ADDR_MAX; +#endif } static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) { #if defined(CONFIG_LATX_KZT) - if (latx_kzt_runtime_enabled()) { - return true; - } else -#endif + return kzt_guest_range_is_valid(start, len, GUEST_ADDR_MAX); +#else return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1; +#endif +} + +#if defined(CONFIG_LATX_KZT) +/* + * KZT wrappers may expose host-owned objects to translated code. Keep that + * compatibility permission separate from the guest virtual-address policy: + * callers which create, remove, or protect guest mappings must continue to + * use guest_addr_valid_untagged() and guest_range_valid_untagged(). + */ +static inline bool kzt_data_addr_valid_untagged(abi_ulong addr) +{ + return kzt_data_addr_is_valid(latx_kzt_runtime_enabled(), addr, + GUEST_ADDR_MAX); } +static inline bool kzt_data_range_valid_untagged(abi_ulong start, + abi_ulong len) +{ + return kzt_data_range_is_valid(latx_kzt_runtime_enabled(), start, len, + GUEST_ADDR_MAX); +} +#endif + #define h2g_valid(x) \ (HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS || \ (uintptr_t)(x) - guest_base <= GUEST_ADDR_MAX) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index cba8f748b37..70d1c8c2aa7 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -638,11 +638,28 @@ extern unsigned long vir_rlimit_as_acc; static inline bool access_ok_untagged(int type, abi_ulong addr, abi_ulong size) { +#if defined(CONFIG_LATX_KZT) + bool guest_range = size == 0 + ? guest_addr_valid_untagged(addr) + : guest_range_valid_untagged(addr, size); + + if (size == 0 + ? !kzt_data_addr_valid_untagged(addr) + : !kzt_data_range_valid_untagged(addr, size)) { + return false; + } + + /* Native wrapper objects do not have entries in the guest page map. */ + if (!guest_range) { + return true; + } +#else if (size == 0 ? !guest_addr_valid_untagged(addr) : !guest_range_valid_untagged(addr, size)) { return false; } +#endif return page_check_range((target_ulong)addr, size, type); } diff --git a/target/i386/latx/context/bridge.c b/target/i386/latx/context/bridge.c index 6083f9d3575..ce2a066d157 100755 --- a/target/i386/latx/context/bridge.c +++ b/target/i386/latx/context/bridge.c @@ -29,14 +29,46 @@ typedef struct brick_s { onebridge_t *b; int sz; brick_t *next; + brick_t *registry_next; } brick_t; typedef struct bridge_s { brick_t *head; brick_t *last; // to speed up kh_bridgemap_t *bridgemap; + GMutex lock; } bridge_t; +static GMutex bridge_registry_lock; +static brick_t *bridge_registry; + +static void register_brick(brick_t *brick) +{ + if (brick->b == MAP_FAILED) { + return; + } + + g_mutex_lock(&bridge_registry_lock); + brick->registry_next = bridge_registry; + bridge_registry = brick; + g_mutex_unlock(&bridge_registry_lock); +} + +static void unregister_brick(brick_t *brick) +{ + brick_t **link; + + g_mutex_lock(&bridge_registry_lock); + for (link = &bridge_registry; *link; link = &(*link)->registry_next) { + if (*link == brick) { + *link = brick->registry_next; + break; + } + } + brick->registry_next = NULL; + g_mutex_unlock(&bridge_registry_lock); +} + //from wrapped/wrappedlibc.c //void* my_mmap(x64emu_t* emu, void* addr, unsigned long length, int prot, int flags, int fd, int64_t offset); //int my_munmap(x64emu_t* emu, void* addr, unsigned long length); @@ -50,12 +82,14 @@ brick_t* NewBrick(void) printf("Warning, cannot allocate 0x%lx aligned bytes for bridge, will probably crash later\n", NBRICK*sizeof(onebridge_t)); } ret->b = ptr; + register_brick(ret); return ret; } bridge_t *NewBridge(void) { bridge_t *b = (bridge_t*)box_calloc(1, sizeof(bridge_t)); + g_mutex_init(&b->lock); b->head = NewBrick(); b->last = b->head; b->bridgemap = kh_init(bridgemap); @@ -66,14 +100,18 @@ void FreeBridge(bridge_t** bridge) { if(!bridge || !*bridge) return; + g_mutex_lock(&(*bridge)->lock); brick_t *b = (*bridge)->head; while(b) { brick_t *n = b->next; + unregister_brick(b); munmap(b->b, NBRICK*sizeof(onebridge_t)); box_free(b); b = n; } kh_destroy(bridgemap, (*bridge)->bridgemap); + g_mutex_unlock(&(*bridge)->lock); + g_mutex_clear(&(*bridge)->lock); box_free(*bridge); *bridge = NULL; } @@ -82,6 +120,7 @@ uintptr_t AddBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N, const char* { brick_t *b = NULL; int sz = -1; + g_mutex_lock(&bridge->lock); b = bridge->last; if(b->sz == NBRICK) { b->next = NewBrick(); @@ -89,28 +128,81 @@ uintptr_t AddBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N, const char* bridge->last = b; } sz = b->sz; - b->sz++; + g_mutex_lock(&bridge_registry_lock); b->b[sz].CC = 0xCC; b->b[sz].S = 'S'; b->b[sz].C='C'; b->b[sz].w = w; b->b[sz].f = (uintptr_t)fnc; b->b[sz].C3 = N?0xC2:0xC3; b->b[sz].N = N; + b->sz++; + g_mutex_unlock(&bridge_registry_lock); // add bridge to map, for fast recovery int ret; khint_t k = kh_put(bridgemap, bridge->bridgemap, (uintptr_t)fnc, &ret); kh_value(bridge->bridgemap, k) = (uintptr_t)&b->b[sz].CC; + g_mutex_unlock(&bridge->lock); return (uintptr_t)&b->b[sz].CC; } +static onebridge_t *find_registered_onebridge(uintptr_t addr) +{ + for (brick_t *brick = bridge_registry; brick; + brick = brick->registry_next) { + uintptr_t start = (uintptr_t)brick->b; + uintptr_t used_end = start + brick->sz * sizeof(onebridge_t); + + if (addr >= start && addr < used_end && + (addr - start) % sizeof(onebridge_t) == 0) { + onebridge_t *slot = (onebridge_t *)addr; + + return slot->CC == 0xCC && slot->S == 'S' && slot->C == 'C' && + (slot->C3 == 0xC3 || slot->C3 == 0xC2) ? slot : NULL; + } + } + + return NULL; +} + +bool kzt_is_registered_onebridge(uintptr_t addr) +{ + g_mutex_lock(&bridge_registry_lock); + bool found = find_registered_onebridge(addr) != NULL; + g_mutex_unlock(&bridge_registry_lock); + return found; +} + +bool kzt_registered_onebridge_snapshot(uintptr_t addr, wrapper_t *wrapper, + uintptr_t *function) +{ + g_mutex_lock(&bridge_registry_lock); + onebridge_t *slot = find_registered_onebridge(addr); + + if (slot) { + if (wrapper) { + *wrapper = slot->w; + } + if (function) { + *function = slot->f; + } + } + g_mutex_unlock(&bridge_registry_lock); + return slot != NULL; +} + uintptr_t CheckBridged(bridge_t* bridge, void* fnc) { // check if function alread have a bridge (the function wrapper will not be tested) + g_mutex_lock(&bridge->lock); khint_t k = kh_get(bridgemap, bridge->bridgemap, (uintptr_t)fnc); - if(k==kh_end(bridge->bridgemap)) + if (k == kh_end(bridge->bridgemap)) { + g_mutex_unlock(&bridge->lock); return 0; - return kh_value(bridge->bridgemap, k); + } + uintptr_t ret = kh_value(bridge->bridgemap, k); + g_mutex_unlock(&bridge->lock); + return ret; } uintptr_t AddCheckBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N, const char* name) diff --git a/target/i386/latx/context/wrappertbbridge.c b/target/i386/latx/context/wrappertbbridge.c index 2305058a419..fb3f4ffd394 100644 --- a/target/i386/latx/context/wrappertbbridge.c +++ b/target/i386/latx/context/wrappertbbridge.c @@ -37,15 +37,23 @@ void* kzt_tbbridge_init(void) } struct kzt_tbbridge* kzt_tbbridge_lookup(target_ulong pc) { - lsassert(tree&&pc); + if (!pc) { + return NULL; + } struct kzt_tbbridge key = {.pc = pc}; g_mutex_lock(&tree_lock); - struct kzt_tbbridge *bridge = - (struct kzt_tbbridge *)g_tree_lookup(tree, &key); + struct kzt_tbbridge *bridge = tree + ? (struct kzt_tbbridge *)g_tree_lookup(tree, &key) + : NULL; g_mutex_unlock(&tree_lock); return bridge; } +bool kzt_tbbridge_contains(target_ulong pc) +{ + return kzt_tbbridge_lookup(pc) != NULL; +} + int kzt_tbbridge_insert(target_ulong pc, ADDR func, void * wrapper) { lsassert(tree&&pc&&wrapper); diff --git a/target/i386/latx/include/bridge.h b/target/i386/latx/include/bridge.h index 67166a9f3ba..fec6511d650 100755 --- a/target/i386/latx/include/bridge.h +++ b/target/i386/latx/include/bridge.h @@ -15,6 +15,9 @@ uintptr_t AddBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N, const char* uintptr_t CheckBridged(bridge_t* bridge, void* fnc); uintptr_t AddCheckBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N, const char* name); uintptr_t AddAutomaticBridge(bridge_t* bridge, wrapper_t w, void* fnc, int N); +bool kzt_is_registered_onebridge(uintptr_t addr); +bool kzt_registered_onebridge_snapshot(uintptr_t addr, wrapper_t *wrapper, + uintptr_t *function); void* GetNativeFnc(uintptr_t fnc); void* GetNativeFncOrFnc(uintptr_t fnc); diff --git a/target/i386/latx/include/kzt-address-policy.h b/target/i386/latx/include/kzt-address-policy.h new file mode 100644 index 00000000000..85a2371fb23 --- /dev/null +++ b/target/i386/latx/include/kzt-address-policy.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef KZT_ADDRESS_POLICY_H +#define KZT_ADDRESS_POLICY_H + +#include +#include + +static inline bool kzt_guest_addr_is_valid(uint64_t addr, + uint64_t guest_addr_max) +{ + return addr <= guest_addr_max; +} + +static inline bool kzt_guest_range_is_valid(uint64_t start, uint64_t len, + uint64_t guest_addr_max) +{ + return len - 1 <= guest_addr_max && + start <= guest_addr_max - len + 1; +} + +static inline bool kzt_data_addr_is_valid(bool kzt_enabled, uint64_t addr, + uint64_t guest_addr_max) +{ + return kzt_guest_addr_is_valid(addr, guest_addr_max) || kzt_enabled; +} + +static inline bool kzt_data_range_is_valid(bool kzt_enabled, uint64_t start, + uint64_t len, + uint64_t guest_addr_max) +{ + if (kzt_guest_range_is_valid(start, len, guest_addr_max)) { + return true; + } + + /* A host-pointer exception must never make an overflowing range valid. */ + return kzt_enabled && start > guest_addr_max && len != 0 && + start <= UINT64_MAX - len + 1; +} + +#endif diff --git a/target/i386/latx/include/wrappertbbridge.h b/target/i386/latx/include/wrappertbbridge.h index c1af2127d23..b2cf8d3a919 100644 --- a/target/i386/latx/include/wrappertbbridge.h +++ b/target/i386/latx/include/wrappertbbridge.h @@ -13,5 +13,6 @@ struct kzt_tbbridge { void* kzt_tbbridge_init(void); struct kzt_tbbridge* kzt_tbbridge_lookup(target_ulong pc); +bool kzt_tbbridge_contains(target_ulong pc); int kzt_tbbridge_insert(target_ulong pc, ADDR func, void * wrapper); #endif diff --git a/target/i386/latx/latx-config.c b/target/i386/latx/latx-config.c index 536d2d4b61c..3d067e169b6 100644 --- a/target/i386/latx/latx-config.c +++ b/target/i386/latx/latx-config.c @@ -18,6 +18,9 @@ #include "translate.h" #include "latx-config.h" #include "syscall-tunnel.h" +#if defined(CONFIG_LATX_KZT) +#include "wrappertbbridge.h" +#endif #ifdef CONFIG_LATX_TU #include "tu.h" @@ -97,9 +100,16 @@ int target_latx_host(CPUArchState *env, struct TranslationBlock *tb, if (option_anonym && (tb->flags & HF_TF_MASK)) { max_insns = 1; } - tr_disasm(tb, max_insns); + bool is_tbbridge = false; +#if defined(CONFIG_LATX_KZT) + is_tbbridge = latx_kzt_runtime_enabled() && + kzt_tbbridge_contains(tb->pc); +#endif + if (!is_tbbridge) { + tr_disasm(tb, max_insns); + } /* return code_size and skip translate */ - if (!tb->icount && tb->pc < reserved_va) { + if (!tb->icount && !is_tbbridge) { return 0; } diff --git a/target/i386/latx/translator/tr-misc.c b/target/i386/latx/translator/tr-misc.c index ee730750497..d1fd068950b 100644 --- a/target/i386/latx/translator/tr-misc.c +++ b/target/i386/latx/translator/tr-misc.c @@ -9,6 +9,9 @@ #include "latx-config.h" #include "lsenv.h" #include "flag-lbt.h" +#if defined(CONFIG_LATX_KZT) +#include "bridge.h" +#endif #include "translate.h" #include "syscall-tunnel.h" #include "profile.h" @@ -19,7 +22,6 @@ #if defined(CONFIG_LATX_KZT) #include "wrapper.h" #include "debug.h" -#include "bridge_private.h" #include "exec/tb-lookup.h" #include "elfloader.h" @@ -41,11 +43,6 @@ bool translate_endbr32(IR1_INST *pir1) { return true; } bool translate_endbr64(IR1_INST *pir1) { return true; } #if defined(CONFIG_LATX_KZT) -static uint8_t Peek8(uintptr_t addr, uintptr_t offset) -{ - return *(uint8_t*)(addr+offset); -} - static void gen_set_next_tb_code(IR2_OPND *esp_ir2_opnd) { IR2_OPND nextip_ir2_opnd = ra_alloc_dbt_arg2(); @@ -203,22 +200,28 @@ static void do_translate_free_brick_tb(void) tr_generate_exit_tb_for_bridge(); } -static void do_translate_brick_tb(onebridge_t *bridge, struct cpu_state_info *state_info, CPUState *cpu, target_ulong tb_pc, TranslationBlock *tb) +static void do_translate_brick_tb(wrapper_t wrapper, uintptr_t function, + struct cpu_state_info *state_info, + CPUState *cpu, target_ulong tb_pc, + TranslationBlock *tb) { tb = lsenv->tr_data->curr_tb; IR2_OPND esp_ir2_opnd = ra_alloc_gpr(esp_index); - if (bridge->f == (uintptr_t)my_free ||bridge->f == (uintptr_t)my___libc_free ||bridge->f == (uintptr_t)my___free ||bridge->f == (uintptr_t)my_cfree ) { + if (function == (uintptr_t)my_free || + function == (uintptr_t)my___libc_free || + function == (uintptr_t)my___free || + function == (uintptr_t)my_cfree) { do_translate_free_brick_tb(); return; - } else if (bridge->f == (uintptr_t)my_realloc) { + } else if (function == (uintptr_t)my_realloc) { do_translate_realloc_brick_tb(); return; } kzt_native_to_wrapper(); - wrapper_gpr_trans((ADDR)bridge->f); + wrapper_gpr_trans((ADDR)function); tr_set_running_of_cs(false); - li_d(ra_ir2_opnd, (ADDR)bridge->w); + li_d(ra_ir2_opnd, (ADDR)wrapper); la_jirl(ra_ir2_opnd, ra_ir2_opnd, 0); tr_set_running_of_cs(true); @@ -256,14 +259,15 @@ bool translate_int_3(IR1_INST *pir1) state_info.cflags = cpu->tcg_cflags; cpu_get_tb_cpu_state(cpu->env_ptr, &state_info.current_pc, &state_info.cs_base, &state_info.flags); + wrapper_t wrapper; + uintptr_t function; if (latx_kzt_runtime_enabled() && - Peek8(state_info.current_pc + 1, 0) == 'S' && - Peek8(state_info.current_pc + 1, 1) == 'C') - { + kzt_registered_onebridge_snapshot(state_info.current_pc, + &wrapper, &function)) { TranslationBlock *tb = NULL; mmap_lock(); - onebridge_t *bridge= (onebridge_t*)state_info.current_pc; - do_translate_brick_tb(bridge, &state_info, cpu, state_info.current_pc, tb); + do_translate_brick_tb(wrapper, function, &state_info, cpu, + state_info.current_pc, tb); mmap_unlock(); } else { la_break(0x5); diff --git a/target/i386/latx/translator/translate.c b/target/i386/latx/translator/translate.c index c7787b2f842..01d2f35b354 100644 --- a/target/i386/latx/translator/translate.c +++ b/target/i386/latx/translator/translate.c @@ -2305,7 +2305,9 @@ void do_translate_tbbridge(ADDR func_pc, ADDR wrapper, TranslationBlock *tb); static int kzt_tr_bridge(struct TranslationBlock *tb) { struct kzt_tbbridge* bridge = kzt_tbbridge_lookup(tb->pc); - lsassert(bridge); + if (!bridge) { + return 0; + } do_translate_tbbridge(bridge->func, (ADDR)bridge->wrapper, tb); return 1; } @@ -2350,7 +2352,8 @@ int tr_translate_tb(struct TranslationBlock *tb) /* generate ir2 from ir1 */ #if defined(CONFIG_LATX_KZT) int translation_done = 0; - if (unlikely(!tb->icount && tb->pc > reserved_va)) { + if (unlikely(latx_kzt_runtime_enabled() && + kzt_tbbridge_contains(tb->pc))) { translation_done = kzt_tr_bridge(tb); } else { translation_done = tr_ir2_generate(tb); diff --git a/tests/unit/kzt/test_kzt_address_policy.c b/tests/unit/kzt/test_kzt_address_policy.c new file mode 100644 index 00000000000..18d8c6cda64 --- /dev/null +++ b/tests/unit/kzt/test_kzt_address_policy.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include +#include + +#include "kzt-address-policy.h" + +static const uint64_t guest_addr_max = 0x3fffffffffULL; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + abort(); \ + } \ +} while (0) + +static void test_guest_vm_policy_stays_strict(void) +{ + CHECK(kzt_guest_addr_is_valid(guest_addr_max, guest_addr_max)); + CHECK(!kzt_guest_addr_is_valid(guest_addr_max + 1, guest_addr_max)); + + CHECK(kzt_guest_range_is_valid(guest_addr_max - 0xfff, 0x1000, + guest_addr_max)); + CHECK(!kzt_guest_range_is_valid(guest_addr_max, 2, guest_addr_max)); + CHECK(!kzt_guest_range_is_valid(UINT64_MAX - 1, 4, guest_addr_max)); + CHECK(!kzt_guest_range_is_valid(0, 0, guest_addr_max)); +} + +static void test_kzt_data_policy_keeps_host_pointer_compatibility(void) +{ + uint64_t host_pointer = guest_addr_max + 0x10000; + + CHECK(!kzt_data_addr_is_valid(false, host_pointer, guest_addr_max)); + CHECK(kzt_data_addr_is_valid(true, host_pointer, guest_addr_max)); + + CHECK(!kzt_data_range_is_valid(false, host_pointer, 32, + guest_addr_max)); + CHECK(kzt_data_range_is_valid(true, host_pointer, 32, + guest_addr_max)); + + CHECK(!kzt_data_range_is_valid(true, guest_addr_max, 2, + guest_addr_max)); + CHECK(!kzt_data_range_is_valid(true, UINT64_MAX - 1, 4, + guest_addr_max)); + CHECK(!kzt_data_range_is_valid(true, host_pointer, 0, + guest_addr_max)); +} + +int main(void) +{ + test_guest_vm_policy_stays_strict(); + test_kzt_data_policy_keeps_host_pointer_compatibility(); + return 0; +} diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 458cc540cdb..50280c8dc8f 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -154,3 +154,18 @@ test( test_kzt_relocation_transaction, suite: 'lat-pr-fast', ) + +test_kzt_address_policy = executable( + 'test-kzt-address-policy', + files('kzt/test_kzt_address_policy.c'), + include_directories: include_directories( + '../../target/i386/latx/include', + ), + build_by_default: false, +) + +test( + 'test-kzt-address-policy', + test_kzt_address_policy, + suite: 'lat-pr-fast', +)