C Code Audit Skill
Perform comprehensive security, safety, and quality audits on Hull C code.
Target: $ARGUMENTS (default: all src/cap/, src/runtime/, and include/hull/ files)
Usage
/c-audit # Audit all source files
/c-audit src/cap/hull_cap_db.c # Audit a specific file
/c-audit --fix # Audit and apply fixes
Audit Categories
1. Memory Safety (Critical)
| Issue | Pattern to Find | Severity |
|---|
| Buffer overflow | strcpy, strcat, sprintf, gets, unbounded loops | Critical |
| Unbounded string ops | strlen, strcmp on untrusted input | Critical |
| Unsafe integer parsing | atoi, atol, atof (no error detection, no bounds) | High |
| Integer overflow | malloc(a * b) without overflow check | Critical |
| Use-after-free | Pointer used after free() | Critical |
| Double-free | free() called twice on same pointer | Critical |
| Null dereference | Pointer used without NULL check | High |
| Uninitialized memory | Variables used before assignment | High |
| Missing null terminator | String buffer not explicitly terminated | High |
| Memory leak | malloc without corresponding free | Medium |
| Stack buffer overflow | Large stack arrays, VLAs | Medium |
Safe Replacements:
c
1// Copying
2strcpy(dst, src) -> strncpy(dst, src, sizeof(dst)-1); dst[sizeof(dst)-1] = '\0';
3strcat(dst, src) -> strncat(dst, src, sizeof(dst)-strlen(dst)-1);
4
5// Formatting
6sprintf(buf, fmt, ...) -> snprintf(buf, sizeof(buf), fmt, ...);
7gets(buf) -> fgets(buf, sizeof(buf), stdin);
8
9// Memory allocation (overflow-safe)
10malloc(count * size) -> calloc(count, size);
11
12// Integer parsing (atoi/atol have no error detection!)
13atoi(str) -> strtol(str, &end, 10) with validation
14atof(str) -> strtof(str, &end) with validation
Allocator Discipline:
c
1// Hull uses malloc/free directly (no custom allocator like Keel)
2// Verify: every malloc has a matching free
3// Verify: every malloc return is checked for NULL
4// Verify: calloc used when count*size multiplication is needed
5
6// BAD:
7void *p = malloc(count * elem_size); // overflow risk
8
9// GOOD:
10void *p = calloc(count, elem_size); // overflow-safe
11if (!p) return -1;
| Issue | What to Check |
|---|
| Array bounds | All array indices validated before access |
| Pointer validity | NULL checks before dereference |
| Size parameters | Non-negative, within reasonable bounds |
| String length | Length checked before copy/concat |
| Numeric ranges | Values within expected domain |
| SQL parameters | param_count checked before binding in hl_cap_db_* |
| File paths | Path validation via hl_cap_fs_validate() before any I/O |
| Env allowlist | Variable name checked against allowlist in hl_cap_env_get() |
3. Resource Management
| Issue | What to Check |
|---|
| File descriptors | fopen paired with fclose |
| Memory | malloc/calloc paired with free |
| SQLite | sqlite3_open paired with sqlite3_close |
| SQLite statements | sqlite3_prepare_v2 paired with sqlite3_finalize |
| QuickJS runtime | JS_NewRuntime paired with JS_FreeRuntime |
| QuickJS context | JS_NewContext paired with JS_FreeContext |
| Lua state | lua_newstate paired with lua_close |
| Error paths | Resources freed on all exit paths |
| I/O return values | fwrite/fread return values checked |
Hull Cleanup Pattern:
c
1// Every init must have matching free
2hl_js_init() -> hl_js_free()
3hl_lua_init() -> hl_lua_free()
4sqlite3_open() -> sqlite3_close()
5sqlite3_prepare_v2() -> sqlite3_finalize()
6JS_NewRuntime() -> JS_FreeRuntime()
7JS_NewContext() -> JS_FreeContext()
8lua_newstate() -> lua_close()
9fopen() -> fclose()
4. Integer Overflow
Overflow in size computations can cause undersized allocations and buffer overflows.
c
1// BAD: overflow on 32-bit
2int total = count * sizeof(HlColumn);
3void *buf = malloc(total);
4
5// GOOD: use size_t + calloc
6HlColumn *cols = calloc(count, sizeof(HlColumn));
7
8// GOOD: check before multiply
9if (count > 0 && (size_t)count > SIZE_MAX / sizeof(HlColumn)) {
10 return -1; // overflow
11}
Key areas in Hull:
HlColumn array allocation in hl_cap_db_query() row callbacks
- PBKDF2 output buffer sizing
- SQL parameter arrays (
HlValue binding arrays)
- Filesystem path buffer construction (
base_dir + "/" + relative_path)
- QuickJS/Lua value conversion arrays
5. Capability Boundary Enforcement
Hull's security model depends on the shared hl_cap_* layer. Verify:
| Issue | Severity | What to Check |
|---|
| Direct SQLite calls | Critical | JS/Lua bindings never call sqlite3_* directly |
| Direct file I/O | Critical | JS/Lua bindings never call fopen/fread/fwrite directly |
| Path traversal | Critical | hl_cap_fs_validate() called before every file operation |
| SQL injection | Critical | All SQL uses parameterized binding via hl_cap_db_* |
| Env leakage | High | All env access through hl_cap_env_get() with allowlist |
| Sandbox escape | Critical | eval() removed, io/os libs not loaded, loadfile/dofile removed |
6. Defensive Macros
Check for and suggest these patterns:
c
1#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
2#define SAFE_FREE(p) do { free(p); (p) = NULL; } while(0)
3#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x)))
7. Test Coverage
Check test files (tests/test_*.c) for:
8. Dead Code Detection
| Pattern | Issue | Fix |
|---|
if (0) { ... } | Dead branch | Remove |
return; code_after; | Unreachable code | Remove |
#if 0 ... #endif | Disabled code | Remove or document |
Unused #define | Dead macro | Remove |
| Unused static function | Dead function | Remove |
Compile with -Wunused flags to detect automatically.
9. Build Hardening
Development build (make debug):
makefile
1-fsanitize=address,undefined -g -O0 -fno-omit-frame-pointer
Production build (make):
makefile
1-Wall -Wextra -Wpedantic -Wshadow -Wformat=2
2-fstack-protector-strong
3-O2
Full validation (make check):
makefile
1clean + DEBUG=1 build + test + e2e
Audit Checks:
Audit Procedure
When /c-audit is invoked:
-
Locate Files
src/cap/*.c # Shared capability layer
src/runtime/js/*.c # QuickJS runtime integration
src/runtime/lua/*.c # Lua 5.4 runtime integration
src/main.c # Entry point
include/hull/*.h # Public headers
tests/test_*.c # Test files
Makefile # Build configuration
-
Scan for Critical Issues
- Search for unsafe functions:
strcpy, sprintf, gets, strcat
- Search for unsafe integer parsing:
atoi, atol, atof
- Search for unchecked allocations:
malloc/calloc without NULL check
- Search for integer overflow in size calculations
- Search for missing bounds checks on array access
- Search for unchecked
fwrite()/fread() return values
- Search for direct SQLite/file I/O calls in runtime bindings (bypass of
hl_cap_*)
-
Review Public API
- Check all public functions in headers (
hl_* prefix)
- Verify NULL checks on pointer parameters
- Verify bounds checks on size parameters
-
Check Resource Management
- Every
_init()/_create() has matching _free()/_close()
- Error paths free allocated resources
- No memory leaks on early returns
- SQLite statements finalized on error paths
- QuickJS/Lua contexts cleaned up on error paths
-
Check Capability Boundaries
- JS/Lua bindings only access resources through
hl_cap_* functions
- Path validation enforced before every filesystem operation
- SQL always parameterized
- Sandbox restrictions in place (no eval, no io/os libs)
-
Detect Dead Code
- Compile with
-Wunused flags
- Find unused static functions
- Find unused variables and parameters
- Flag commented-out or
#if 0 code blocks
-
Check Build Hardening
- Sanitizers available in debug build
- Stack protection in production build
- Warning flags comprehensive
-
Generate Report
Format as markdown table with findings, severity, file:line, and suggested fix.
markdown
1## C Audit Report: Hull
2
3**Date:** YYYY-MM-DD
4**Files Scanned:** N
5**Issues Found:** N (Critical: N, High: N, Medium: N, Low: N)
6
7### Critical Issues
8
910|---|-----------|-------|--------------|---------------|
11| C1 | src/cap/hull_cap_db.c:42 | Buffer overflow | `strcpy(buf, src)` | `snprintf(buf, sizeof(buf), "%s", src)` |
12
13### High Issues
14...
15
16### Medium Issues
17...
18
19### Low Issues
20...
21
22### Recommendations
231. ...
Fix Mode (--fix)
When --fix is specified:
- Generate the audit report first
- For each fixable issue, apply the transformation
- Rebuild (
make)
- Re-run tests (
make test)
- Report any test failures or new warnings
Auto-fixable Issues:
strcpy -> snprintf with buffer size
sprintf -> snprintf with buffer size
atoi -> strtol with validation
- Missing NULL checks (add early return)
- Missing
malloc return check (add NULL check)
- Integer overflow in size calc ->
calloc or overflow check
- Missing
size_t casts in size calculations
- Unused local variables (remove)
- Unused static functions (remove)
NOT Auto-fixable (require manual review):
- Logic errors
- Resource leaks in complex control flow
- Capability boundary violations
- Sandbox escape paths
- Architectural changes