Skip to content

Commit 733d110

Browse files
committed
tests: add libc default compartment regression
1 parent 504cd31 commit 733d110

File tree

8 files changed

+372
-13
lines changed

8 files changed

+372
-13
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ cmake_minimum_required(VERSION 4.0)
22
project(IA2Phase2)
33

44
# Feature flags
5+
option(IA2_DEBUG "Enable debug telemetry and runtime checks" OFF)
56
option(IA2_LIBC_COMPARTMENT "Enable libc/ld.so compartmentalization and exit callgate support" OFF)
67

8+
if(IA2_DEBUG)
9+
message(STATUS "IA2: debug mode enabled")
10+
endif()
11+
712
if(IA2_LIBC_COMPARTMENT)
813
message(STATUS "IA2: libc compartmentalization enabled")
914
add_compile_definitions(IA2_LIBC_COMPARTMENT=1)

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ if (NOT LIBIA2_AARCH64)
7575
add_subdirectory(trusted_indirect)
7676

7777
if(IA2_LIBC_COMPARTMENT)
78+
add_subdirectory(libc_default_compartment)
7879
add_subdirectory(dl_debug_test)
7980
add_subdirectory(dl_debug_getpwnam)
8081
add_subdirectory(dl_debug_mbstowcs_test)

tests/dl_debug_test/run_all_tests.sh

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# 2. Core Functionality Tests (12 tests) - Validates runtime loader isolation
1010
# 3. Debug Telemetry Tests (11 tests) - Validates wrapper coverage (IA2_DEBUG builds)
1111
#
12-
# TOTAL: 27 tests (4 bootstrap + 12 core + 11 debug)
12+
# TOTAL: 28 tests (4 bootstrap + 12 core + 11 debug + 1 shared-compartment sanity)
1313
#
1414
# ============================================================================
1515
# WHAT IS BEING TESTED
@@ -62,7 +62,7 @@
6262
# - Detects LLVM/Clang installation (tries llvm-18 down to llvm-11)
6363
# - Configures with -DIA2_DEBUG=ON for full telemetry testing
6464
# - Builds dl_debug_test and libia2_bootstrap_shim (always included)
65-
# - Runs all 27 tests (4 bootstrap + 12 core + 11 debug)
65+
# - Runs all 28 tests (4 bootstrap + 12 core + 11 debug + 1 shared-compartment sanity)
6666
#
6767
# PKRU gates are unconditionally enabled for hardware-enforced loader isolation.
6868
# No manual configuration required! Just run the script.
@@ -94,7 +94,7 @@
9494
# - Enables -DIA2_DEBUG=ON for debug telemetry (11 tests)
9595
# - PKRU gates always enabled (hardware loader isolation + bootstrap shim)
9696
# - Builds all required targets (dl_debug_test, libia2_bootstrap_shim)
97-
# - Result: All 27 tests available
97+
# - Result: All 28 tests available
9898
#
9999
# Just run: ./run_all_tests.sh
100100
#
@@ -115,15 +115,15 @@
115115
# CMAKE_ARGS="-DIA2_DEBUG=OFF" ./run_all_tests.sh
116116
#
117117
# Test availability by configuration:
118-
# Standard (no debug): 16 tests (12 core + 4 bootstrap shim)
119-
# + IA2_DEBUG=ON (default): 27 tests (all)
118+
# Standard (no debug): 17 tests (12 core + 4 bootstrap shim + shared-compartment sanity)
119+
# + IA2_DEBUG=ON (default): 28 tests (all)
120120
#
121121
# ============================================================================
122122
# EXPECTED OUTPUT
123123
# ============================================================================
124124
#
125125
# Success:
126-
# ✓ All 27 tests pass
126+
# ✓ All 28 tests pass
127127
# ✓ Exit code 0
128128
#
129129
# Partial success (non-debug build):
@@ -204,6 +204,10 @@ TEST_BINARY="$BUILD_TEST_DIR/dl_debug_test"
204204

205205
# Bootstrap shim library
206206
BOOTSTRAP_SHIM="$PROJECT_ROOT/build/runtime/libia2/liblibia2_bootstrap_shim.so"
207+
# Shared-compartment regression binary
208+
LIBC_DEFAULT_TEST_DIR="$PROJECT_ROOT/build/tests/libc_default_compartment"
209+
LIBC_DEFAULT_TEST_BINARY="$LIBC_DEFAULT_TEST_DIR/libc_default_compartment"
210+
LIBC_DEFAULT_TEST_NAME="library_stays_in_pkey0"
207211

208212

209213
# ============================================================================
@@ -224,6 +228,20 @@ detect_llvm_paths() {
224228
echo ""
225229
}
226230

231+
cache_flag_matches() {
232+
local cache_file=$1
233+
local flag=$2
234+
local expected=$3
235+
236+
if [ ! -f "$cache_file" ]; then
237+
return 1
238+
fi
239+
240+
local value
241+
value=$(grep "^${flag}:" "$cache_file" 2>/dev/null | head -n1 | cut -d'=' -f2)
242+
[ "$value" = "$expected" ]
243+
}
244+
227245
# Configure project with optimal settings for comprehensive testing
228246
ensure_project_configured() {
229247
local build_dir="$PROJECT_ROOT/build"
@@ -232,19 +250,38 @@ ensure_project_configured() {
232250
mkdir -p "$build_dir"
233251
fi
234252

235-
if [ ! -f "$build_dir/CMakeCache.txt" ] || [ ! -f "$build_dir/build.ninja" ]; then
253+
local cache_file="$build_dir/CMakeCache.txt"
254+
local needs_reconfigure=0
255+
256+
if [ ! -f "$cache_file" ] || [ ! -f "$build_dir/build.ninja" ]; then
257+
needs_reconfigure=1
258+
fi
259+
260+
if [ "$needs_reconfigure" -eq 0 ] && ! cache_flag_matches "$cache_file" "IA2_DEBUG" "ON"; then
261+
echo "[build] Existing build missing IA2_DEBUG=ON - reconfiguring"
262+
needs_reconfigure=1
263+
fi
264+
265+
if [ "$needs_reconfigure" -eq 0 ] && ! cache_flag_matches "$cache_file" "IA2_LIBC_COMPARTMENT" "ON"; then
266+
echo "[build] Existing build missing IA2_LIBC_COMPARTMENT=ON - reconfiguring"
267+
needs_reconfigure=1
268+
fi
269+
270+
if [ "$needs_reconfigure" -eq 0 ]; then
271+
return
272+
fi
273+
236274
# Auto-detect LLVM paths
237275
local llvm_args=$(detect_llvm_paths)
238276

239-
# Build with debug enabled for all 27 tests
277+
# Build with debug enabled for all 28 tests
240278
# PKRU gates are now unconditionally enabled in the codebase
241279
# User can override by setting CMAKE_ARGS environment variable
242280
local default_args="-DIA2_DEBUG=ON -DIA2_LIBC_COMPARTMENT=ON ${llvm_args}"
243281
local cmake_args="${CMAKE_ARGS:-$default_args}"
244282

245283
echo "[build] Configuring project (cmake -GNinja $cmake_args ..)"
246284
(cd "$build_dir" && cmake -GNinja $cmake_args ..)
247-
fi
248285
}
249286

250287
ninja_target_exists() {
@@ -307,7 +344,7 @@ for arg in "$@"; do
307344
echo " • Auto-detects LLVM/Clang installation"
308345
echo " • Configures with -DIA2_DEBUG=ON (PKRU gates always enabled)"
309346
echo " • Builds dl_debug_test and libia2_bootstrap_shim"
310-
echo " • Runs all 27 tests (4 bootstrap + 12 core + 11 debug)"
347+
echo " • Runs all 28 tests (4 bootstrap + 12 core + 11 debug + 1 shared-compartment sanity)"
311348
echo
312349
echo "Options:"
313350
echo " --verbose Show full test output"
@@ -468,6 +505,53 @@ run_test() {
468505
fi
469506
}
470507

508+
run_libc_default_compartment_test() {
509+
echo
510+
echo "======================================================================"
511+
echo -e "${BLUE}Shared Compartment Sanity Test${NC}"
512+
echo "======================================================================"
513+
514+
ensure_target_built libc_default_compartment
515+
516+
TOTAL_TESTS=$((TOTAL_TESTS + 1))
517+
printf "[shared] %-45s ... " "$LIBC_DEFAULT_TEST_NAME"
518+
519+
if [ ! -x "$LIBC_DEFAULT_TEST_BINARY" ]; then
520+
echo -e "${RED}✗ MISSING${NC}"
521+
FAILED_TESTS=$((FAILED_TESTS + 1))
522+
FAILED_TEST_NAMES+=("libc_default_compartment")
523+
return 1
524+
fi
525+
526+
local log_file="/tmp/libc_default_compartment_$$.log"
527+
528+
if [ $VERBOSE -eq 1 ]; then
529+
if (cd "$LIBC_DEFAULT_TEST_DIR" && IA2_TEST_NAME="$LIBC_DEFAULT_TEST_NAME" ./libc_default_compartment); then
530+
echo -e "${GREEN}✓ PASS${NC}"
531+
PASSED_TESTS=$((PASSED_TESTS + 1))
532+
return 0
533+
fi
534+
else
535+
if (cd "$LIBC_DEFAULT_TEST_DIR" && IA2_TEST_NAME="$LIBC_DEFAULT_TEST_NAME" ./libc_default_compartment > "$log_file" 2>&1); then
536+
echo -e "${GREEN}✓ PASS${NC}"
537+
PASSED_TESTS=$((PASSED_TESTS + 1))
538+
rm -f "$log_file"
539+
return 0
540+
fi
541+
fi
542+
543+
echo -e "${RED}✗ FAIL${NC}"
544+
FAILED_TESTS=$((FAILED_TESTS + 1))
545+
FAILED_TEST_NAMES+=("libc_default_compartment")
546+
if [ $VERBOSE -eq 0 ] && [ -f "$log_file" ]; then
547+
echo -e "${RED}--- Test Output ---${NC}"
548+
tail -20 "$log_file"
549+
echo -e "${RED}--- End Output ---${NC}"
550+
rm -f "$log_file"
551+
fi
552+
return 1
553+
}
554+
471555
# ============================================================================
472556
# BOOTSTRAP SHIM TESTS (Stage 3)
473557
# ============================================================================
@@ -590,10 +674,12 @@ DEBUG_TESTS=(
590674
"nested_loader_gates"
591675
)
592676

593-
# Check debug build
677+
# Check debug build (look for PKRU assertions in call gates library)
594678
DEBUG_AVAILABLE=0
595-
if nm ./dl_debug_test | grep -q "ia2_dlopen_count"; then
596-
DEBUG_AVAILABLE=1
679+
if [ -f "./libdl_debug_test_call_gates.so" ]; then
680+
if objdump -d ./libdl_debug_test_call_gates.so | grep -q "rdpkru"; then
681+
DEBUG_AVAILABLE=1
682+
fi
597683
fi
598684

599685
# Calculate total
@@ -639,6 +725,14 @@ else
639725
SKIPPED_TESTS=$((SKIPPED_TESTS + ${#DEBUG_TESTS[@]}))
640726
fi
641727

728+
# ============================================================================
729+
# SHARED COMPARTMENT SANITY TEST
730+
# ============================================================================
731+
732+
if ! run_libc_default_compartment_test; then
733+
[ $STOP_ON_FAIL -eq 1 ] && exit 1
734+
fi
735+
642736
# ============================================================================
643737
# FINAL SUMMARY
644738
# ============================================================================
@@ -687,6 +781,7 @@ if [ $FAILED_TESTS -eq 0 ]; then
687781
echo " ✓ PartitionAlloc integration with loader gate"
688782
echo " ✓ All 10 dlopen-family wrappers functional"
689783
echo " ✓ MPK hardware enforcement working correctly"
784+
echo " ✓ Shared-compartment libraries (pkey 0) can call libc safely"
690785
if [ $DEBUG_AVAILABLE -eq 1 ]; then
691786
echo " ✓ Per-wrapper telemetry counters functional"
692787
echo " ✓ Nested loader gate depth tracking"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Verifies that libraries left in the shared compartment (pkey 0)
2+
# can still call into libc.
3+
4+
# Build the shared library in the default (shared) compartment.
5+
define_shared_lib(
6+
SRCS library.c
7+
NEEDS_LD_WRAP
8+
EXTRA_REWRITER_ARGS --libc-compartment
9+
)
10+
11+
# Build the test that links against the shared library.
12+
define_test(
13+
SRCS main.c
14+
NEEDS_LD_WRAP
15+
CRITERION_TEST
16+
EXTRA_REWRITER_ARGS --libc-compartment
17+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "library.h"
2+
3+
#include <ia2.h>
4+
#include <ia2_test_runner.h>
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
#include <unistd.h>
10+
11+
int call_libc_from_pkey0(void) {
12+
// The library deliberately stays in the shared compartment, so the PKRU
13+
// should still be 0 when we enter from main.
14+
cr_assert_eq(ia2_get_compartment(), 0);
15+
16+
const char *message = "pkey0-libc";
17+
18+
// Use tmpfile() to get a FILE* (not a builtin)
19+
FILE *tmp = tmpfile();
20+
cr_assert(tmp);
21+
22+
// Use fileno() to get file descriptor (not a builtin)
23+
int fd = fileno(tmp);
24+
cr_assert(fd >= 0);
25+
26+
// Write using fputs (not a builtin)
27+
int result = fputs(message, tmp);
28+
cr_assert(result >= 0);
29+
30+
// Use fflush (not a builtin)
31+
result = fflush(tmp);
32+
cr_assert(result == 0);
33+
34+
// Use feof and ferror (not builtins)
35+
cr_assert(!feof(tmp));
36+
cr_assert(!ferror(tmp));
37+
38+
// Clear error indicator (not a builtin)
39+
clearerr(tmp);
40+
41+
// Use fclose (not a builtin)
42+
result = fclose(tmp);
43+
cr_assert(result == 0);
44+
45+
// Use getpid (not a builtin)
46+
pid_t pid = getpid();
47+
cr_assert(pid > 0);
48+
49+
return 0;
50+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
int call_libc_from_pkey0(void);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "library.h"
2+
3+
#include <ia2.h>
4+
#include <ia2_test_runner.h>
5+
6+
INIT_RUNTIME(1);
7+
#define IA2_COMPARTMENT 1
8+
#include <ia2_compartment_init.inc>
9+
10+
void ia2_main(void) {
11+
ia2_register_compartment("main", 1, NULL);
12+
}
13+
14+
Test(libc_default_compartment, library_stays_in_pkey0) {
15+
int result = call_libc_from_pkey0();
16+
cr_assert_eq(result, 0);
17+
}

0 commit comments

Comments
 (0)