/* * Revera — a real-time kernel with hardware-enforced isolation. * Copyright (c) 2026 VIACERTO LLC. All rights reserved. * * This file is dual-licensed under the PolyForm Noncommercial License 1.0.0 OR * a commercial license from VIACERTO LLC. You may choose to use this file under * the terms of either license. * * For the full license terms, see the LICENSE and LICENSE-COMMERCIAL.md files in * the root directory of this source tree. * * SPDX-FileCopyrightText: © 2026 VIACERTO LLC * SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 OR LicenseRef-VIACERTO-Commercial */ /* * isolation_reject/main.c — PROVE full-lockdown is FAIL-CLOSED at thread creation (review L4/D2). * * Companion to isolation: that one proves isolation HOLDS at runtime (a peer-stack write faults); * this one proves the guard REFUSES to create a thread whose stack it could not isolate in the first * place — loudly (NULL + TK_ERR_PARAM), instead of letting it run SILENTLY unisolated. * * Under TK_FEAT_ISOLATION, the tk_thread_create / tk_thread_spawn family run arch_stack_isolatable(stack, * cells) and reject a * non-conforming stack (create_core, kernel/tk_kernel.c). "Conforming" means the per-switch region grant * can actually fence it: on the denylist arches (arm_v7m / risc_v M-mode) power-of-two + naturally * aligned AND inside the .thread_stacks zone; on arm_v8m any 32-byte-aligned extent (base/limit region). * Declare stacks with TK_THREAD_STACK and they conform; a bare array or a hand-offset pointer does not. * * Two creates at setup: * 1. a deliberately NON-conforming stack (mis-aligned + outside the zone) -> tk_thread_create_in returns * NULL with TK_ERR_PARAM. The "fail-OPEN" line can only print if the guard let it through. * 2. a proper TK_THREAD_STACK stack -> accepted; the thread then runs, showing the system is healthy * (the guard rejects bad input, it does not wedge good input). * * make BOARD=nrf52832_pca10040 flash # ARMv7-M: watch the debug UART, 115200 8N1 * make BOARD=qemu_riscv32_virt run # RV32 under qemu * * Needs full lockdown (the Makefile sets it) on an MPU/PMP arch with the isolation linker zones. */ #include #include "tk_kernel.h" /* TK_THREAD_STACK / tk_thread_create* / arch_stack_isolatable via the guard */ #include "tk_time.h" #include "tk_board.h" #include "tk_console.h" #if !TK_FEAT_ISOLATION #error "isolation_reject needs full lockdown — build it via its Makefile (UNPRIV + MPU/PMP isolation)." #endif /* A conforming stack: TK_THREAD_STACK -> power-of-two, self-aligned, in the .thread_stacks zone. */ TK_THREAD_STACK(good_stack, 2048); /* Backing store for the NON-conforming stack. A bare array (NOT TK_THREAD_STACK) lands in ordinary .bss, * OUTSIDE the .thread_stacks zone — already non-conforming on the zone-based arches. We ALSO hand out a * mis-aligned pointer into it so it is rejected on EVERY MPU arch (incl. arm_v8m, which checks only * 32-byte alignment). The +8 slack keeps the offset stack in-bounds. */ static tk_stack_cell_t bad_pool[2048 + 8]; static void* good(void* arg) { (void)arg; for (uint32_t i = 0u; ; i++) { tk_print("[good] running on an isolatable stack, iter "); tk_print_u32(i); tk_print("\n"); tk_sleep_ms(500u); } return 0; } /* Must never be created — the guard rejects its stack before it can be scheduled. */ static void* never_runs(void* arg) { (void)arg; tk_print("[bad] !!! I SHOULD NOT EXIST — a non-isolatable stack was accepted !!!\n"); for (;;) { tk_sleep_ms(1000u); } return 0; } int main(void) { tk_board_init(); tk_console_init(TK_UART_CH_DEBUG); tk_print("\nisolation_reject: full lockdown REFUSES a non-isolatable stack (fail-closed, review L4).\n"); tk_kernel_init(); tk_time_init(); /* 1. Non-conforming stack: mis-aligned pointer into a non-zone array. Expect REJECTION. */ void* bad_stack = (void*)(((uintptr_t)&bad_pool[0]) | 0x4u); /* bit 2 set => not 32-aligned */ tk_status_t st = TK_OK; tk_thread_t* bad = tk_thread_create_in(tk_kernel_self(), never_runs, 0, bad_stack, 2048u, (unsigned char)(TK_PRIO_MIN + 1u), &st); if (bad == NULL && st == TK_ERR_PARAM) { tk_print("[guard] non-isolatable stack REJECTED at create (NULL + TK_ERR_PARAM) => fail-CLOSED. OK\n"); } else { tk_print("[guard] !!! non-isolatable stack ACCEPTED (handle="); tk_print_u32((uint32_t)(uintptr_t)bad); tk_print(", status="); tk_print_u32((uint32_t)st); tk_print(") => FAIL-OPEN !!!\n"); } /* 2. Conforming TK_THREAD_STACK: expect ACCEPTANCE, and it runs. */ tk_thread_t* g = tk_thread_create(good, 0, good_stack, sizeof good_stack, (unsigned char)(TK_PRIO_MIN + 1u)); tk_print(g != NULL ? "[guard] TK_THREAD_STACK thread accepted => the guard passes GOOD input. OK\n" : "[guard] !!! a conforming stack was rejected — over-strict !!!\n"); tk_kernel_start(); return 0; }