/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. This file is part of the StarForth project. Licensed under the StarForth License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: https://github.com/star.4th@proton.me/StarForth/LICENSE.txt This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. See the License for the specific language governing permissions and limitations under the License. StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. This file is part of the StarForth project. Licensed under the StarForth License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: https://github.com/star.4th@proton.me/StarForth/LICENSE.txt This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. See the License for the specific language governing permissions and limitations under the License. */ /** * parity.c - Parity packet collection and canonical hash * * Implements M7 parity validation for hosted vs kernel comparison. * * M7 Normative Rules enforced here: * - Rule 1: word_id is monotonic creation index * - Rule 2: Colon bodies hashed as word_id sequence * - Rule 3: Dictionary traversal in creation order */ #include "starkernel/vm/parity.h" #include "vm.h" #ifdef __STARKERNEL__ #include "console.h" #include "starkernel/hal/hal.h" #else #include #include #endif #include #include #ifndef SK_PARITY_DEBUG #define SK_PARITY_DEBUG 0 #endif static void print_u32(uint32_t val); static void print_hex64(uint64_t val); static void print_str(const char *s); static void print_nl(void); #if SK_PARITY_DEBUG #define SK_PARITY_DEBUG_PREFIX "SKPD:" enum sk_parity_ptr_region { SK_PTR_REGION_NULL = 0, SK_PTR_REGION_VM_ARENA, SK_PTR_REGION_TEXT, SK_PTR_REGION_RODATA, SK_PTR_REGION_DATA, SK_PTR_REGION_BSS, SK_PTR_REGION_DIRECTMAP, SK_PTR_REGION_UNKNOWN }; static void sk_parity_debug_log_msg(const char *msg); static bool sk_parity_debug_is_canonical(uint64_t addr); static enum sk_parity_ptr_region sk_parity_classify_region(struct VM *vm, const void *ptr); static const char *sk_parity_region_name(enum sk_parity_ptr_region region); static void sk_parity_debug_print_word_name(const DictEntry *entry); static void sk_parity_debug_panic(struct VM *vm, const char *reason, const DictEntry *entry, uint32_t word_index, const void *header_ptr, const void *xt_ptr, const void *bad_ptr, enum sk_parity_ptr_region region, bool canonical); static void sk_parity_debug_log_ptr(const char *label, const void *ptr, enum sk_parity_ptr_region region, bool canonical); static void sk_parity_debug_check_ptr(struct VM *vm, const char *label, const DictEntry *entry, uint32_t word_index, const void *header_ptr, const void *xt_ptr, const void *ptr); #else #define sk_parity_debug_log_msg(msg) do { (void)(msg); } while (0) #define sk_parity_debug_check_ptr(vm,label,entry,idx,hptr,xt,ptr) \ do { (void)(vm); (void)(label); (void)(entry); (void)(idx); (void)(hptr); (void)(xt); (void)(ptr); } while (0) #endif /* Maximum dictionary entries for traversal array */ #define MAX_DICT_ENTRIES 2048 #if SK_PARITY_DEBUG #define SK_PARITY_CANONICAL_MASK 0xffff800000000000ULL #define SK_PARITY_DIRECTMAP_BASE 0xffff800000000000ULL /* Note: For __STARKERNEL__ builds, we use sk_hal_text_start/end() from hal.h * to avoid GOT indirection issues with -fPIC. The rodata/data/bss section * checks are skipped since XT pointers should only be in text section. */ /** * @brief Emit a @c SK_PARITY_DEBUG_PREFIX-prefixed diagnostic message line. * * Prints @c "SKPD:\n" to the kernel console or stdout. Used throughout * parity traversal to trace the hash walk when @c SK_PARITY_DEBUG=1. * Prints @c "" if @p msg is @c NULL. * * Only compiled when @c SK_PARITY_DEBUG is non-zero; all call sites are * macro-eliminated otherwise. * * @param msg Null-terminated diagnostic message string. */ static void sk_parity_debug_log_msg(const char *msg) { print_str(SK_PARITY_DEBUG_PREFIX); print_str(msg ? msg : ""); print_nl(); } /** * @brief Test whether a 64-bit address is a canonical x86-64 virtual address. * * On x86-64, bits [63:48] must all equal bit 47 (sign-extend). This function * checks that invariant: extracts bit 47, then verifies that the upper 16 bits * (@c SK_PARITY_CANONICAL_MASK) are either all-zero (user space) or all-one * (kernel space). * * Only compiled when @c SK_PARITY_DEBUG is non-zero. * * @param addr 64-bit virtual address to test. * @return @c true if @p addr is canonical, @c false otherwise. */ static bool sk_parity_debug_is_canonical(uint64_t addr) { uint64_t sign = (addr >> 47) & 1ULL; uint64_t mask = SK_PARITY_CANONICAL_MASK; return sign ? ((addr & mask) == mask) : ((addr & mask) == 0); } /** * @brief Classify a pointer into a known memory region for debug diagnostics. * * Checks @p ptr against known address ranges in the following priority order: * 1. @c NULL → @c SK_PTR_REGION_NULL * 2. VM arena [@c vm->memory, @c vm->memory + VM_MEMORY_SIZE) → * @c SK_PTR_REGION_VM_ARENA * 3. Kernel .text section [sk_hal_text_start(), sk_hal_text_end()) → * @c SK_PTR_REGION_TEXT (kernel build only; rodata/data/bss skipped) * 4. Direct-map region (≥ @c SK_PARITY_DIRECTMAP_BASE) → * @c SK_PTR_REGION_DIRECTMAP (kernel build only) * 5. Anything else → @c SK_PTR_REGION_UNKNOWN * * Only compiled when @c SK_PARITY_DEBUG is non-zero. * * @param vm Active VM (used for arena base/size); may be @c NULL. * @param ptr Pointer to classify. * @return One of the @c sk_parity_ptr_region enum values. */ static enum sk_parity_ptr_region sk_parity_classify_region(struct VM *vm, const void *ptr) { if (!ptr) { return SK_PTR_REGION_NULL; } uintptr_t addr = (uintptr_t)ptr; if (vm && vm->memory) { uintptr_t arena_start = (uintptr_t)vm->memory; uintptr_t arena_end = arena_start + VM_MEMORY_SIZE; if (addr >= arena_start && addr < arena_end) { return SK_PTR_REGION_VM_ARENA; } } #ifdef __STARKERNEL__ /* Use HAL getters to avoid GOT indirection issues with -fPIC */ uintptr_t text_start = (uintptr_t)sk_hal_text_start(); uintptr_t text_end = (uintptr_t)sk_hal_text_end(); if (addr >= text_start && addr < text_end) { return SK_PTR_REGION_TEXT; } /* Note: rodata/data/bss section checks skipped for kernel builds. * Function pointers (XTs) should only be in the text section anyway. */ if (addr >= SK_PARITY_DIRECTMAP_BASE) { return SK_PTR_REGION_DIRECTMAP; } #endif return SK_PTR_REGION_UNKNOWN; } /** * @brief Return a human-readable name for a @c sk_parity_ptr_region value. * * Used by debug print helpers to label pointer regions in diagnostic output. * Returns @c "unknown" for any value not listed in the enum. * * Only compiled when @c SK_PARITY_DEBUG is non-zero. * * @param region Enum value from @c sk_parity_classify_region(). * @return Pointer to a static string literal naming the region. */ static const char *sk_parity_region_name(enum sk_parity_ptr_region region) { switch (region) { case SK_PTR_REGION_NULL: return "null"; case SK_PTR_REGION_VM_ARENA: return "vm_arena"; case SK_PTR_REGION_TEXT: return "text"; case SK_PTR_REGION_RODATA: return "rodata"; case SK_PTR_REGION_DATA: return "data"; case SK_PTR_REGION_BSS: return "bss"; case SK_PTR_REGION_DIRECTMAP: return "directmap"; default: return "unknown"; } } /** * @brief Print the name of a dictionary entry to the console for debug output. * * Copies @p entry->name into a local null-terminated buffer (bounded by * @c WORD_NAME_MAX) and emits it via @c print_str(). Prints @c "" if * @p entry is @c NULL. Used by @c sk_parity_debug_panic() to identify the * word being hashed when a pointer violation is detected. * * Only compiled when @c SK_PARITY_DEBUG is non-zero. * * @param entry Dictionary entry whose name to print; may be @c NULL. */ static void sk_parity_debug_print_word_name(const DictEntry *entry) { if (!entry) { print_str(""); return; } uint8_t len = entry->name_len; if (len == 0 || len > WORD_NAME_MAX) { len = (len > WORD_NAME_MAX) ? WORD_NAME_MAX : len; } char buf[WORD_NAME_MAX + 1]; if (len > 0) { memcpy(buf, entry->name, len); } buf[len] = '\0'; print_str(buf); } /** * @brief Log a pointer's address, canonicality, and region to the console. * * Emits a single debug line in the format: * @c "SKPD: