If I ever get around to writing a C compiler, I would like to tackle this concept. I didn't write it myself, I just brainstormed this with the soon defunct Cody...
Abstract #
This paper presents a novel approach to C compiler safety that combines static analysis with layered trust hierarchies and API-level safety contracts. Rather than treating all code as equally trustworthy or untrustworthy, our system establishes trust levels that allow safe libraries to provide guarantees to user code while still permitting them to use lower-level unsafe operations internally. The compiler emits metadata about operations and API calls, which a specialized linker then verifies against trust boundaries and safety contracts.
1. Introduction #
1.1 Problem Statement #
Traditional C compilers provide little to no safety guarantees, leading to common vulnerabilities like buffer overflows, use-after-free, and pointer arithmetic errors. While static analysis tools exist, they often produce false positives or require marking arbitrary code as "trusted," which defeats the purpose of safety verification.
1.2 Our Approach #
We propose a layered trust model where:
- User code operates under strict safety constraints
- Safe libraries can provide safety guarantees through API contracts
- System libraries can perform unsafe operations internally while maintaining safe external interfaces
- The linker verifies that trust boundaries are respected
2. Trust Level Hierarchy #
2.1 Trust Level Definition #
1enum trust_level {
2 TRUST_USER_CODE = 0, // Highest restrictions
3 TRUST_SAFE_LIBRARY = 1, // Can call lower levels safely
4 TRUST_SYSTEM_LIBRARY = 2, // Can call anything
5 TRUST_LIBC = 3 // Lowest level, no restrictions
6};
2.2 Trust Level Rules #
Caller Level | Can Call | Restrictions |
---|---|---|
TRUST_USER_CODE |
Safe libraries only | No pointer arithmetic, no direct libc calls |
TRUST_SAFE_LIBRARY |
System libraries + libc | Must provide safety contracts |
TRUST_SYSTEM_LIBRARY |
Anything | Minimal restrictions |
TRUST_LIBC |
Anything | No restrictions |
3. Safety Contracts #
3.1 Contract Types #
1enum safety_contract {
2 CONTRACT_NO_OVERFLOW = 1 << 0, // Prevents buffer overflows
3 CONTRACT_NULL_SAFE = 1 << 1, // Handles NULL pointers gracefully
4 CONTRACT_BOUNDS_CHECKED = 1 << 2, // All array accesses are bounds-checked
5 CONTRACT_MEMORY_SAFE = 1 << 3, // No memory leaks or use-after-free
6 CONTRACT_THREAD_SAFE = 1 << 4 // Safe for concurrent access
7};
3.2 API Contract Declaration #
1// Example: mulle-buffer library
2__attribute__((trust_level(TRUST_SAFE_LIBRARY)))
3__attribute__((safety_contract(no_overflow, null_safe, bounds_checked)))
4int mulle_buffer_add_bytes(struct mulle_buffer *buffer, const void *data, size_t len);
5
6__attribute__((trust_level(TRUST_SAFE_LIBRARY)))
7__attribute__((can_call_level(TRUST_LIBC))) // Explicit permission
8char* mulle_buffer_get_string(struct mulle_buffer *buffer);
4. Compiler Implementation #
4.1 Metadata Structure #
1typedef struct {
2 enum operation_type {
3 RAW_POINTER_ARITHMETIC, // User did: ptr + offset
4 SAFE_API_CALL, // User called contracted API
5 UNSAFE_CAST, // User did: (int*)ptr
6 CONTRACTED_ACCESS, // Access through safe API
7 TRUST_VIOLATION // Calling unauthorized API
8 } type;
9
10 union {
11 struct {
12 const char* api_name;
13 enum safety_contract contracts;
14 enum trust_level api_level;
15 } api_info;
16
17 struct {
18 int line;
19 const char* operation;
20 enum trust_level caller_level;
21 } raw_info;
22
23 struct {
24 const char* caller;
25 enum trust_level caller_level;
26 const char* callee;
27 enum trust_level callee_level;
28 int line;
29 } violation_info;
30 };
31} operation_metadata_t;
4.2 Compilation Process #
1// Compiler integration pseudocode
2void emit_operation_metadata(enum operation_type type, const char* caller,
3 const char* callee, int line) {
4 enum trust_level caller_level = get_function_trust_level(caller);
5 enum trust_level callee_level = get_function_trust_level(callee);
6
7 operation_metadata_t metadata = {
8 .type = type,
9 .caller = caller,
10 .caller_level = caller_level,
11 .callee = callee,
12 .callee_level = callee_level,
13 .line = line
14 };
15
16 // Emit to .compiler_safety_metadata section
17 emit_to_metadata_section(&metadata);
18}
5. Usage Examples #
5.1 Compliant User Code #
1// User code (TRUST_USER_CODE level)
2void safe_user_function(const char *input) {
3 struct mulle_buffer buf = MULLE_BUFFER_INIT;
4
5 // ✅ ALLOWED: User calling safe library
6 mulle_buffer_add_bytes(&buf, input, strlen(input));
7
8 // ✅ ALLOWED: Safe API provides guarantees
9 char *result = mulle_buffer_extract_string(&buf);
10
11 // Use result safely...
12 mulle_buffer_done(&buf);
13}
5.2 Safe Library Implementation #
1// Safe library (TRUST_SAFE_LIBRARY level)
2__attribute__((trust_level(TRUST_SAFE_LIBRARY)))
3int mulle_buffer_add_bytes(struct mulle_buffer *buffer, const void *data, size_t len) {
4 // ✅ ALLOWED: Safe library handles NULL safely
5 if (!buffer || !data) return -1; // null_safe contract
6
7 // ✅ ALLOWED: Safe library prevents overflow
8 if (len > SIZE_MAX - buffer->size) return -1; // no_overflow contract
9
10 // ✅ ALLOWED: Safe library can call libc
11 if (buffer->size + len > buffer->capacity) {
12 void *new_data = realloc(buffer->data, buffer->capacity * 2);
13 if (!new_data) return -1;
14 buffer->data = new_data;
15 buffer->capacity *= 2;
16 }
17
18 // ✅ ALLOWED: Safe library can do pointer arithmetic safely
19 memcpy((char*)buffer->data + buffer->size, data, len);
20 buffer->size += len;
21 return 0;
22}
5.3 Trust Violations #
1// Trust violations that would be caught
2void bad_user_code(void) {
3 // ❌ VIOLATION: User code calling libc directly
4 char *ptr = malloc(100); // Linker error!
5
6 // ❌ VIOLATION: User code doing pointer arithmetic
7 ptr[50] = 'x'; // Linker error!
8
9 // ❌ VIOLATION: User code calling unsafe API
10 strcpy(ptr, "hello"); // Linker error!
11}
6. Linker Verification #
6.1 Verification Algorithm #
1bool verify_operation_metadata(operation_metadata_t *metadata) {
2 switch (metadata->type) {
3 case RAW_POINTER_ARITHMETIC:
4 // Only allowed for TRUST_SAFE_LIBRARY and below
5 return metadata->caller_level >= TRUST_SAFE_LIBRARY;
6
7 case SAFE_API_CALL:
8 // Verify API contract exists and caller has permission
9 return verify_api_contract_exists(metadata->api_info.api_name) &&
10 verify_call_permission(metadata->caller_level,
11 metadata->api_info.api_level);
12
13 case TRUST_VIOLATION:
14 // Always a violation
15 return false;
16
17 default:
18 return true;
19 }
20}
6.2 Call Permission Verification #
1bool verify_call_permission(enum trust_level caller_level,
2 enum trust_level callee_level) {
3 // Rule 1: Can only call same level or higher trust
4 if (callee_level < caller_level) {
5 return false; // Lower trust level cannot call higher trust
6 }
7
8 // Rule 2: User code cannot call libc directly
9 if (caller_level == TRUST_USER_CODE && callee_level == TRUST_LIBC) {
10 return false;
11 }
12
13 return true;
14}
7. Linker Output and Reporting #
7.1 Violation Reports #
1$ ld -verify-trust-levels user.o -lmulle-buffer -o program
2
3TRUST VIOLATIONS FOUND:
4----------------------------------------
5user.o:bad_user_code:23 - TRUST_USER_CODE calling malloc (TRUST_LIBC)
6 └─ User code cannot call libc directly. Use safe library instead.
7
8user.o:bad_user_code:24 - TRUST_USER_CODE performing pointer arithmetic
9 └─ User code cannot perform pointer arithmetic. Use safe APIs.
10
11user.o:unsafe_function:45 - TRUST_USER_CODE calling strcpy (TRUST_LIBC)
12 └─ User code cannot call unsafe libc functions. Use mulle_buffer_add_bytes.
13
14TRUST SUMMARY:
15----------------------------------------
16- User code: 15 function calls, 3 violations
17- mulle-buffer: 8 libc calls (all permitted)
18- Safety score: 80% (3 violations out of 15 operations)
19
20RECOMMENDATIONS:
21----------------------------------------
22- Replace malloc/free with mulle_buffer APIs
23- Replace pointer arithmetic with safe buffer operations
24- Use mulle_buffer_add_bytes instead of strcpy
7.2 Success Report #
1$ ld -verify-trust-levels clean_user.o -lmulle-buffer -o program
2
3TRUST VERIFICATION PASSED:
4----------------------------------------
5- User code: 12 function calls, 0 violations
6- mulle-buffer: 8 libc calls (all permitted via safety contracts)
7- Safety score: 100%
8
9SAFETY CONTRACTS VERIFIED:
10----------------------------------------
11- mulle_buffer_add_bytes: no_overflow, null_safe, bounds_checked ✓
12- mulle_buffer_get_string: null_safe, memory_safe ✓
13- mulle_buffer_done: memory_safe ✓
8. Benefits and Advantages #
8.1 Safety Without Performance Cost #
- User code gets safety guarantees without runtime overhead
- Safe libraries can optimize internally using unsafe operations
- No runtime checks needed due to compile-time verification
8.2 Gradual Adoption #
- Existing code can be gradually migrated to safe APIs
- Libraries can provide safety contracts without changing internal implementation
- Backward compatibility with existing unsafe code (with warnings)
8.3 Clear Responsibility Model #
- Library authors are responsible for providing safety contracts
- User code is protected from accidental unsafe operations
- Trust boundaries are explicit and verifiable
9. Implementation Considerations #
9.1 Compiler Integration #
- Requires modifications to C compiler front-end for attribute parsing
- Need to emit metadata to special ELF sections
- Integration with existing static analysis frameworks
9.2 Linker Modifications #
- New linker phase for trust verification
- Metadata aggregation across object files
- Integration with existing linking process
9.3 Standard Library Integration #
- Need to annotate standard library functions with trust levels
- Provide safe wrapper libraries for common operations
- Migration path for existing code
10. Future Work #
10.1 Advanced Contracts #
- Temporal safety contracts (use-after-free prevention)
- Concurrency safety contracts
- Resource management contracts
10.2 Dynamic Verification #
- Runtime verification of safety contracts
- Hybrid compile-time/runtime checking
- Performance monitoring of safe operations
10.3 Language Extensions #
- Syntax sugar for safety contracts
- First-class support for trust levels
- Integration with modern C standards
11. Conclusion #
The proposed trust-level safety verification system provides a practical approach to C safety that balances security with usability. By establishing clear trust boundaries and allowing safe libraries to provide guarantees through API contracts, we can achieve memory safety for user code while maintaining the performance and flexibility that makes C attractive for systems programming.
The key insight is that safety is not binary but layered - different levels of code should have different privileges and responsibilities. This approach allows for gradual adoption and provides a clear migration path from unsafe to safe C programming practices.
This whitepaper presents a conceptual framework for trust-level safety verification in C compilers. Implementation details may vary based on specific compiler and linker architectures.