Files
Linux-Hello/CODING_STANDARDS.md

5.1 KiB

Safety-Critical Coding Standards

This document outlines the safety-critical coding standards applied to Linux Hello, based on MISRA C and similar standards, adapted for both C and Rust code.

Principles Applied

1. Simple Control Flow

  • C: No goto, setjmp, longjmp, or recursion
  • Rust: Avoid recursion where possible; use iterative solutions. Rust's ownership system prevents many control flow issues.

2. Fixed Loop Bounds

  • C: All loops have fixed upper bounds (e.g., MAX_ARGS = 32)
  • Rust: Use bounded iterators, take(), or explicit bounds. Example:
    for item in items.iter().take(MAX_ITEMS) { ... }
    

3. No Dynamic Memory After Init

  • C: No malloc/free after startup (stack allocation only)
  • ⚠️ Rust: Rust's allocator is safer, but we still prefer:
    • Pre-allocated buffers where possible
    • Vec::with_capacity() to avoid reallocations
    • Stack allocation for small data

4. Function Length Limit (~60 lines)

  • C: All functions under 60 lines
  • Rust: Same principle - break complex functions into smaller, testable units

5. Runtime Assertions (2+ per function)

  • C: Using assert() and assert_condition() helper
  • Rust: Use debug_assert!(), assert!(), and unwrap_or_else() with validation

6. Smallest Scope for Data

  • C: Declare variables at smallest possible scope
  • Rust: Rust enforces this - variables are scoped to blocks

7. Check All Return Values

  • C: All function returns checked, void casts for intentional ignores
  • Rust: Use Result<T, E> and Option<T> - compiler enforces checking

8. Minimal Preprocessor

  • C: Only #include and simple #define constants
  • Rust: Use cfg!() and #[cfg(...)] attributes instead of macros

9. Restricted Pointer Use

  • C: Single level dereferencing only, no function pointers unless essential
  • Rust: Rust's references are safer, but we still:
    • Avoid deep nesting (&&&T)
    • Prefer owned types over references where possible
    • Use function pointers only when necessary (e.g., trait objects)

10. Strictest Warnings

  • C: -Wall -Wextra -Werror -Wpedantic + static analysis
  • Rust: #![deny(warnings)] in critical modules, clippy with pedantic lints

Implementation Status

C PAM Module (pam-module/pam_linux_hello.c)

Fully compliant:

  • Split authenticate_face() into smaller functions
  • Fixed loop bounds (MAX_ARGS = 32)
  • All return values checked
  • Runtime assertions added
  • Strictest compiler warnings enabled
  • Single-level pointer dereferencing
  • No dynamic allocation after init

Rust Codebase

🔄 In Progress - Applying equivalent principles:

Completed:

  • Return values checked (Rust enforces this)
  • Small scope (Rust enforces this)
  • No goto/setjmp (not applicable in Rust)
  • Minimal preprocessor (Rust uses attributes)

To Improve:

  • Function length limits (some functions > 60 lines)
  • Fixed loop bounds (some iterators unbounded)
  • More runtime assertions
  • Enable strictest Rust lints

Rust-Specific Safety Measures

Memory Safety

  • Rust's ownership system prevents use-after-free, double-free
  • No manual memory management needed
  • unsafe blocks are minimal and documented

Type Safety

  • Strong typing prevents many classes of bugs
  • Pattern matching ensures exhaustive handling
  • Result and Option enforce error handling

Bounds Checking

  • Array/vector bounds checked at runtime (can be disabled with unsafe)
  • Iterator bounds enforced by type system

Static Analysis

C Code

# Compile with strictest warnings
make CFLAGS="-Wall -Wextra -Werror -Wpedantic ..."

# Run static analysis
cppcheck --enable=all pam-module/pam_linux_hello.c
splint pam-module/pam_linux_hello.c

Rust Code

# Clippy with pedantic lints
cargo clippy -- -W clippy::pedantic

# Deny warnings in release
RUSTFLAGS="-D warnings" cargo build --release

Code Review Checklist

  • No functions > 60 lines
  • All loops have fixed bounds
  • All return values checked
  • Minimum 2 assertions per function
  • Variables at smallest scope
  • No recursion (unless essential)
  • Single-level pointer/reference dereferencing
  • Compiles with strictest warnings
  • Passes static analysis

Examples

Good: Fixed Loop Bound

const MAX_ITEMS: usize = 100;
for item in items.iter().take(MAX_ITEMS) {
    // Process item
}

Good: Assertions

fn process_data(data: &[u8]) -> Result<()> {
    debug_assert!(!data.is_empty(), "Data must not be empty");
    debug_assert!(data.len() <= MAX_SIZE, "Data exceeds maximum size");
    // ... processing
}

Good: Small Functions

// Instead of one 100-line function, split:
fn validate_input(input: &str) -> Result<()> { ... }  // 20 lines
fn parse_input(input: &str) -> Result<Data> { ... }   // 25 lines
fn process_data(data: Data) -> Result<Output> { ... } // 30 lines

References

  • MISRA C:2012 Guidelines
  • CERT C Coding Standard
  • Rust API Guidelines
  • Safety-Critical Software Development