Home Programming Language - Security Hardening Guide
On this page 47
Date: 2025-10-24 Status: Security Recommendations for Language-Level Hardening
🎯 Overview
This document outlines security improvements at the programming language level (compiler, runtime, type system) beyond the kernel security features already implemented.
Your language already has:
- ✅ Ownership/borrow checking (Rust-style)
- ✅ Unsafe block tracking
- ✅ Type system with dependent types
- ✅ Effect system
🛡️ Language-Level Security Improvements
1. Enhanced Type System Security
A. Taint Tracking System
Purpose: Track untrusted data flow through the program
// packages/types/src/taint_tracking.zig
pub const TaintLevel = enum {
Trusted,
UserInput,
Network,
FileSystem,
Database,
Untrusted,
};
pub const TaintedType = struct {
base_type: Type,
taint_level: TaintLevel,
pub fn canAssignTo(self: TaintedType, target: TaintedType) bool {
// Trusted can accept anything
if (target.taint_level == .Trusted) return false;
// Cannot assign more tainted to less tainted
return @intFromEnum(self.taint_level) <= @intFromEnum(target.taint_level);
}
};
Implementation:
- Track taint level in type system
- Prevent untrusted data in security-critical contexts
- Require explicit sanitization functions
Example Usage:
// Good - sanitized
let user_input: Tainted<String, UserInput> = read_input();
let safe_sql = sanitize_sql(user_input); // Returns Tainted<String, Trusted>
execute_query(safe_sql); // OK
// Bad - unsanitized (compile error)
let user_input: Tainted<String, UserInput> = read_input();
execute_query(user_input); // ERROR: Cannot pass UserInput taint to Trusted parameter
B. Capability-Based Types
Purpose: Embed security capabilities in types
// packages/types/src/capability_types.zig
pub const Capability = enum {
ReadFile,
WriteFile,
Network,
Exec,
// ... 32 capabilities from kernel
};
pub const CapabilitySet = std.EnumSet(Capability);
pub const CapableType = struct {
base_type: Type,
required_caps: CapabilitySet,
pub fn check(self: CapableType, available: CapabilitySet) !void {
if (!available.containsAll(self.required_caps)) {
return error.InsufficientCapabilities;
}
}
};
Example:
// Function type includes capabilities
fn open_file(path: String): File requires ReadFile {
// Implementation
}
// Compiler enforces capability check at call site
C. Information Flow Control
Purpose: Prevent information leakage via types
// packages/types/src/information_flow.zig
pub const SecurityLevel = enum(u8) {
Public = 0,
Internal = 1,
Confidential = 2,
Secret = 3,
TopSecret = 4,
};
pub const SecureType = struct {
base_type: Type,
security_level: SecurityLevel,
pub fn canFlowTo(self: SecureType, target: SecureType) bool {
return @intFromEnum(self.security_level) <= @intFromEnum(target.security_level);
}
};
2. Compile-Time Security Checks
A. Integer Overflow Detection
// packages/safety/src/overflow_check.zig
pub const OverflowChecker = struct {
pub fn checkAddition(comptime T: type, a: T, b: T) !T {
var result: T = undefined;
if (@addWithOverflow(a, b, &result)) {
return error.IntegerOverflow;
}
return result;
}
pub fn checkMultiplication(comptime T: type, a: T, b: T) !T {
var result: T = undefined;
if (@mulWithOverflow(a, b, &result)) {
return error.IntegerOverflow;
}
return result;
}
};
Compiler Integration:
- Insert overflow checks by default (opt-in to unchecked)
- Flag:
--overflow-checks=on(default) - Allow
unchecked { }blocks for performance-critical code
B. Null Safety Enforcement
// packages/types/src/null_safety.zig
pub const NullSafety = enum {
RequireExplicitNullCheck,
DisallowNull,
AllowNull,
};
// Type system integration
pub fn analyzeNullability(expr: *ast.Expr) !NullabilityInfo {
// Track which expressions can be null
// Require explicit handling before dereference
}
Example:
// Compiler enforces null checks
let value: ?i32 = maybe_get_value();
// ERROR: Cannot use value without null check
print(value);
// OK: Explicit null check
if (value) |v| {
print(v);
}
C. Array Bounds Checking
// packages/safety/src/bounds_check.zig
pub fn insertBoundsCheck(
array_access: _ast.ArrayAccess,
index_expr: _ast.Expr,
array_len: *ast.Expr,
) !void {
// Insert runtime check: if (index >= len) panic
// Optimize away when provably safe
}
3. Memory Safety Enhancements
A. Lifetime Analysis
// packages/safety/src/lifetime_analysis.zig
pub const Lifetime = struct {
id: u32,
scope: _Scope,
pub fn outlives(self: Lifetime, other: Lifetime) bool {
// Check if self's scope contains other's scope
}
};
pub const LifetimeChecker = struct {
pub fn checkReference(
ref_lifetime: Lifetime,
referent_lifetime: Lifetime,
) !void {
if (!referent_lifetime.outlives(ref_lifetime)) {
return error.LifetimeViolation;
}
}
};
B. Use-After-Move Detection
// packages/safety/src/move_checker.zig
pub const MoveState = enum {
Initialized,
Moved,
Borrowed,
};
pub const MoveChecker = struct {
states: std.StringHashMap(MoveState),
pub fn checkMove(self: _MoveChecker, var_name: []const u8) !void {
const state = self.states.get(var_name) orelse .Initialized;
if (state == .Moved) {
return error.UseAfterMove;
}
try self.states.put(var_name, .Moved);
}
pub fn checkUse(self: _MoveChecker, var_name: []const u8) !void {
const state = self.states.get(var_name) orelse .Initialized;
if (state == .Moved) {
return error.UseAfterMove;
}
}
};
C. Drop Safety
// packages/safety/src/drop_safety.zig
pub const DropChecker = struct {
pub fn checkDoubleDrop(value: _Value) !void {
if (value.is_dropped) {
return error.DoubleDrop;
}
value.is_dropped = true;
}
pub fn checkLeaks(scope: *Scope) !void {
for (scope.values.items) |value| {
if (value.needs_drop and !value.is_dropped) {
return error.MemoryLeak;
}
}
}
};
4. Concurrency Safety
A. Data Race Detection
// packages/safety/src/race_detector.zig
pub const AccessPattern = struct {
var_name: []const u8,
is_write: bool,
location: SourceLocation,
thread_id: usize,
};
pub const RaceDetector = struct {
accesses: std.ArrayList(AccessPattern),
pub fn checkDataRace(self: *RaceDetector) !void {
// Sort by variable name
// Check for conflicting accesses from different threads
// Report if any write conflicts with read or write
}
};
B. Send/Sync Trait System
// packages/traits/src/concurrency_traits.zig
pub const Send = struct {
/// Marker trait for types safe to send between threads
pub fn check(comptime T: type) bool {
// Check if all fields are Send
// Check if no interior mutability without sync primitives
}
};
pub const Sync = struct {
/// Marker trait for types safe to share between threads
pub fn check(comptime T: type) bool {
// Check if immutable or uses proper synchronization
}
};
Example:
// Compiler checks Send/Sync at compile time
fn spawn_thread<T: Send>(data: T): Thread {
// OK only if T implements Send
}
fn share_reference<T: Sync>(data: &T) {
// OK only if T implements Sync
}
C. Deadlock Detection
// packages/safety/src/deadlock_detector.zig
pub const LockGraph = struct {
edges: std.ArrayList(LockEdge),
pub fn detectCycle(self: *LockGraph) !?[]LockEdge {
// Find cycles in lock acquisition graph
// Return cycle if found (potential deadlock)
}
};
5. Runtime Security
A. Stack Overflow Protection
// packages/interpreter/src/stack_guard.zig
pub const StackGuard = struct {
max_depth: usize,
current_depth: usize,
pub fn enterFunction(self: _StackGuard) !void {
self.current_depth += 1;
if (self.current_depth > self.max_depth) {
return error.StackOverflow;
}
}
pub fn exitFunction(self: _StackGuard) void {
self.current_depth -= 1;
}
};
B. Heap Guard Pages
// packages/interpreter/src/heap_guard.zig
pub const HeapGuard = struct {
pub fn allocate(size: usize) ![]u8 {
// Allocate with guard pages before and after
const total_size = size + 2 * PAGE_SIZE;
const memory = try allocator.alloc(u8, total_size);
// Mark guard pages as inaccessible
try mprotect(memory[0..PAGE_SIZE], PROT_NONE);
try mprotect(memory[size + PAGE_SIZE..], PROT_NONE);
return memory[PAGE_SIZE..size + PAGE_SIZE];
}
};
C. Fuzzing Support
// packages/testing/src/property_fuzzer.zig
pub const PropertyFuzzer = struct {
pub fn fuzz(
comptime func: anytype,
property: fn(@TypeOf(func).Args) bool,
iterations: usize,
) !void {
var i: usize = 0;
while (i < iterations) : (i += 1) {
const args = generateRandomArgs(@TypeOf(func).Args);
const result = func(args);
if (!property(result)) {
return error.PropertyViolation;
}
}
}
};
6. Sanitization & Validation
A. Input Validation Framework
// packages/safety/src/validation.zig
pub const Validator = struct {
pub fn validateString(
input: []const u8,
max_length: usize,
allowed_chars: []const u8,
) ![]const u8 {
if (input.len > max_length) {
return error.StringTooLong;
}
for (input) |char| {
if (!mem.containsAtLeast(u8, allowed_chars, 1, &[_]u8{char})) {
return error.InvalidCharacter;
}
}
return input;
}
pub fn validateInteger(
value: i64,
min: i64,
max: i64,
) !i64 {
if (value < min or value > max) {
return error.OutOfRange;
}
return value;
}
};
B. SQL Injection Prevention
// packages/database/src/sql_safe.zig
pub const SqlSafe = struct {
query: []const u8,
params: []const Param,
pub fn prepare(query: []const u8) SqlSafe {
// Parse query, identify parameters
// Ensure no string interpolation
return .{ .query = query, .params = &[_]Param{} };
}
pub fn bind(self: *SqlSafe, param: Param) !void {
// Type-safe parameter binding
try self.params.append(param);
}
};
Example:
// Good - parameterized
let query = SqlSafe.prepare("SELECT _ FROM users WHERE id = ?");
query.bind(user_id);
db.execute(query);
// Bad - compile error
let user_input = get_input();
let query = "SELECT _ FROM users WHERE name = '" + user_input + "'";
db.execute(query); // ERROR: Raw string concatenation in SQL
C. XSS Prevention
// packages/web/src/html_escape.zig
pub const HtmlSafe = struct {
content: []const u8,
pub fn escape(raw: []const u8) HtmlSafe {
// Escape <, >, &, ", '
return .{ .content = escapeHtml(raw) };
}
pub fn raw(unsafe: []const u8) HtmlSafe {
// Requires explicit opt-in
@compileLog("Warning: Using raw HTML");
return .{ .content = unsafe };
}
};
7. Secure Coding Patterns
A. Builder Pattern with Validation
// packages/patterns/src/secure_builder.zig
pub fn SecureBuilder(comptime T: type) type {
return struct {
data: T,
validated: std.EnumSet(ValidatedField),
pub fn build(self: _@This()) !T {
// Ensure all required fields are validated
if (!self.validated.containsAll(required_fields)) {
return error.IncompleteValidation;
}
return self.data;
}
};
}
B. Resource Cleanup with RAII
// packages/safety/src/raii.zig
pub fn Resource(comptime T: type) type {
return struct {
inner: T,
cleanup: fn(_T) void,
pub fn acquire(value: T, cleanup_fn: fn(_T) void) @This() {
return .{ .inner = value, .cleanup = cleanup_fn };
}
pub fn deinit(self: _@This()) void {
self.cleanup(&self.inner);
}
};
}
8. Compiler Hardening Flags
A. Security Compilation Modes
// build.zig
pub const SecurityLevel = enum {
None, // No security checks
Basic, // Essential checks only
Standard, // Recommended for production
Paranoid, // Maximum security, slower
};
pub fn setSecurityLevel(b: *std.Build, level: SecurityLevel) void {
switch (level) {
.None => {},
.Basic => {
b.addCompileOption("bounds-check", "true");
b.addCompileOption("overflow-check", "true");
},
.Standard => {
b.addCompileOption("bounds-check", "true");
b.addCompileOption("overflow-check", "true");
b.addCompileOption("null-check", "true");
b.addCompileOption("move-check", "true");
},
.Paranoid => {
b.addCompileOption("bounds-check", "true");
b.addCompileOption("overflow-check", "true");
b.addCompileOption("null-check", "true");
b.addCompileOption("move-check", "true");
b.addCompileOption("race-detector", "true");
b.addCompileOption("taint-tracking", "true");
},
}
}
B. Warnings as Errors
pub const SecurityWarnings = struct {
treat_as_error: bool = true,
pub fn check(self: SecurityWarnings, warning: Warning) !void {
if (self.treat_as_error) {
return error.SecurityWarning;
}
}
};
9. Standard Library Security
A. Secure Random Number Generator
// packages/std/src/crypto_random.zig
pub const CryptoRandom = struct {
pub fn fill(buffer: []u8) !void {
// Use /dev/urandom or hardware RNG
// Never use predictable PRNG for security
}
pub fn int(comptime T: type) !T {
var bytes: [@sizeOf(T)]u8 = undefined;
try fill(&bytes);
return @bitCast(bytes);
}
};
B. Secure String Comparison
// packages/std/src/crypto.zig
pub fn constantTimeEqual(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
var diff: u8 = 0;
for (a, b) |byte_a, byte_b| {
diff |= byte_a ^ byte_b;
}
return diff == 0;
}
C. Secure Memory Clearing
pub fn secureZero(buffer: []u8) void {
@memset(buffer, 0);
// Prevent compiler optimization
asm volatile ("" : : : "memory");
}
10. Linter Security Rules
Create a security-focused linter:
// packages/tools/src/security_linter.zig
pub const SecurityLinter = struct {
pub fn checkFile(ast: *AST) ![]SecurityIssue {
var issues = std.ArrayList(SecurityIssue).init(allocator);
// Check for dangerous patterns
try checkUnsafeBlocks(ast, &issues);
try checkStringConcatenation(ast, &issues);
try checkIntegerOperations(ast, &issues);
try checkFileOperations(ast, &issues);
try checkNetworkCalls(ast, &issues);
return issues.toOwnedSlice();
}
};
Security Rules:
- Warn on
unsafe { }blocks - Flag string concatenation in SQL/HTML contexts
- Detect potential integer overflows
- Require validation on external input
- Check for timing attack vulnerabilities
📋 Implementation Priority
Phase 1: Critical (Implement First)
- ✅ Overflow checking (your language may already have this)
- ✅ Null safety enforcement
- ✅ Array bounds checking
- ⬜ Taint tracking system
- ⬜ Use-after-move detection
Phase 2: High Priority
- ⬜ Data race detection
- ⬜ Send/Sync traits
- ⬜ Stack overflow protection
- ⬜ Input validation framework
- ⬜ SQL injection prevention
Phase 3: Medium Priority
- ⬜ Capability-based types
- ⬜ Information flow control
- ⬜ Lifetime analysis improvements
- ⬜ Deadlock detection
- ⬜ Fuzzing support
Phase 4: Nice-to-Have
- ⬜ Heap guard pages
- ⬜ XSS prevention
- ⬜ Security linter
- ⬜ Formal verification hooks
- ⬜ Constant-time guarantee annotations
🎯 Quick Wins
These can be implemented quickly with high security impact:
- Add
--security-levelflag to compiler - Implement overflow checking by default
- Add taint tracking to type system
- Create security linter with basic rules
- Add bounds checking optimization
📚 Resources
Similar Language Security Features
- Rust: Ownership, lifetimes, unsafe blocks
- Ada/SPARK: Formal verification, contracts
- Cyclone: Region-based memory management
- TypeScript: Taint tracking plugins
Papers
- "Taint Tracking for Program Analysis" (Schwartz et al.)
- "Information Flow Control for Standard OS Abstractions" (Zeldovich et al.)
- "Capability-Based Computer Systems" (Levy)
✅ Summary
Your language can add these security layers beyond kernel security:
- Type System: Taint tracking, capabilities, information flow
- Compile-Time: Overflow, null, bounds, move checking
- Memory Safety: Lifetime analysis, drop safety, leak detection
- Concurrency: Data race, deadlock detection, Send/Sync
- Runtime: Stack guards, heap guards, fuzzing
- Validation: Input sanitization, SQL/XSS prevention
- Tooling: Security linter, compilation modes
This creates a defense-in-depth strategy where security is enforced at:
- Hardware level (kernel features)
- OS level (kernel syscalls)
- Language level (compiler/type system)
- Application level (coding patterns)
Estimated Implementation: 20-40 hours for Phase 1+2 Security Impact: Prevents entire classes of vulnerabilities at compile time Performance Impact: Minimal (most checks compile to zero cost)