CONTRACT-INIT-ROOTS-401: Init-Section Synthetic Roots
Status: Published Version: 1.0.0 Published: 2025-12-13 Owners: Scanner Guild, Policy Guild, Signals Guild Unblocks: SCANNER-INITROOT-401-036, EDGE-BUNDLE-401-054, and downstream tasks
Part of the Stella Ops Contracts index. Audience: Scanner, Policy, and Signals implementers who produce or consume reachability roots, and reviewers verifying that init-phase code is accounted for in vulnerability assessment.
Overview
This contract defines how ELF/PE/Mach-O initialization sections (.init_array, .ctors, DT_INIT, etc.) are modeled as synthetic roots in Stella Ops reachability graphs. These roots represent code that executes during program load, before main() — code that a naive main-only traversal would miss but that must be included in reachability analysis for complete vulnerability assessment.
1. Init-Section Categories
1.1 ELF Init Sections
| Section/Tag | Phase | Order | Description |
|---|---|---|---|
.preinit_array / DT_PREINIT_ARRAY | preinit | 0-N | Executed before dynamic linker init |
.init / DT_INIT | init | 0 | Single init function |
.init_array / DT_INIT_ARRAY | init | 1-N | Array of init function pointers |
.ctors | init | after init_array | Legacy C++ constructors |
.fini / DT_FINI | fini | 0 | Single cleanup function |
.fini_array / DT_FINI_ARRAY | fini | 1-N | Array of cleanup function pointers |
.dtors | fini | after fini_array | Legacy C++ destructors |
1.2 PE Init Sections
| Mechanism | Phase | Order | Description |
|---|---|---|---|
DllMain (DLL_PROCESS_ATTACH) | init | 0 | DLL initialization |
| TLS callbacks | init | 1-N | Thread-local storage callbacks |
| C++ global constructors | init | after TLS | Via CRT init table |
DllMain (DLL_PROCESS_DETACH) | fini | 0 | DLL cleanup |
1.3 Mach-O Init Sections
| Section | Phase | Order | Description |
|---|---|---|---|
__mod_init_func | init | 0-N | Module init functions |
__mod_term_func | fini | 0-N | Module termination functions |
2. Synthetic Root Schema
2.1 Root Object in richgraph-v1
{
"roots": [
{
"id": "root:init:0:sym:binary:abc123...",
"phase": "init",
"source": "init_array",
"order": 0,
"target_id": "sym:binary:abc123...",
"binary_path": "/usr/lib/libfoo.so.1",
"build_id": "gnu-build-id:5f0c7c3c..."
}
]
}
2.2 Root ID Format
root:{phase}:{order}:{target_symbol_id}
Examples:
root:preinit:0:sym:binary:abc...- First preinit functionroot:init:0:sym:binary:def...- DT_INIT functionroot:init:1:sym:binary:ghi...- First init_array entryroot:main:0:sym:binary:jkl...- main() functionroot:fini:0:sym:binary:mno...- DT_FINI function
2.3 Phase Enumeration
| Phase | Numeric Order | Execution Time |
|---|---|---|
load | 0 | Dynamic linker resolution |
preinit | 1 | Before dynamic init |
init | 2 | During initialization |
main | 3 | Program entry (main) |
fini | 4 | During termination |
3. Root Discovery Algorithm
3.1 ELF Root Discovery
1. Parse .dynamic section for DT_PREINIT_ARRAY, DT_INIT, DT_INIT_ARRAY
2. For each array:
a. Read function pointer addresses
b. Resolve to symbol (if available) or emit unknown
c. Create root with phase + order
3. Find _start, main, _init, _fini symbols and add as roots
4. Sort roots by (phase, order, target_id) for determinism
3.2 Handling Unresolved Targets
When init array contains address without symbol:
{
"roots": [
{
"id": "root:init:2:unknown:0x12345678",
"phase": "init",
"source": "init_array",
"order": 2,
"target_id": "unknown:0x12345678",
"resolved": false,
"reason": "No symbol at address 0x12345678"
}
],
"unknowns": [
{
"id": "unknown:0x12345678",
"type": "unresolved_init_target",
"address": "0x12345678",
"source": "init_array[2]"
}
]
}
4. DT_NEEDED Dependency Modeling
4.1 Purpose
DT_NEEDED entries specify shared library dependencies. These execute their init code before the depending binary’s init code.
4.2 Schema
{
"dependencies": [
{
"id": "dep:libssl.so.3",
"name": "libssl.so.3",
"source": "DT_NEEDED",
"order": 0,
"resolved_path": "/usr/lib/x86_64-linux-gnu/libssl.so.3",
"resolved_build_id": "gnu-build-id:abc..."
}
]
}
4.3 Init Order with Dependencies
1. libssl.so.3 preinit → init
2. libcrypto.so.3 preinit → init
3. libc.so.6 preinit → init
4. main_binary preinit → init → main
5. Patch Oracle Integration
5.1 Oracle Expected Roots
{
"expected_roots": [
{
"id": "root:init:*:sym:binary:*",
"phase": "init",
"source": "init_array",
"required": true,
"reason": "Init function must be detected for CVE-2023-XXXX"
}
]
}
5.2 Oracle Forbidden Roots
{
"forbidden_roots": [
{
"id": "root:preinit:*:*",
"phase": "preinit",
"reason": "Preinit code should not exist after patch"
}
]
}
6. Policy Integration
6.1 Reachability State with Init Roots
When evaluating reachability:
- If vulnerable function is reachable from
main→REACHABLE - If vulnerable function is reachable from
initroots →REACHABLE_INIT - If vulnerable function is reachable only from
fini→REACHABLE_FINI
6.2 Policy DSL Extensions
# Require init-phase reachability for not_affected
rules:
- name: init-reachability-required
condition: |
vuln.phase_reachable.includes("init") and
reachability.confidence >= 0.8
action: require_evidence
- name: init-only-lower-severity
condition: |
reachability.reachable_phases == ["init"] and
not reachability.reachable_phases.includes("main")
action: reduce_severity
severity_adjustment: -1
7. Evidence Requirements
7.1 Init Root Evidence Bundle
{
"root_evidence": {
"root_id": "root:init:0:sym:binary:...",
"extraction_method": "dynamic_section",
"source_offset": "0x1234",
"target_address": "0x5678",
"target_symbol": "frame_dummy",
"evidence_hash": "sha256:...",
"evidence_uri": "cas://binary/roots/sha256:..."
}
}
7.2 CAS Storage Layout
cas://reachability/roots/{graph_hash}/
init.json # All init-phase roots
fini.json # All fini-phase roots
dependencies.json # DT_NEEDED graph
evidence/
root:{id}.json # Per-root evidence
8. Determinism Rules
8.1 Root Ordering
Roots are sorted by:
- Phase (numeric: load=0, preinit=1, init=2, main=3, fini=4)
- Order within phase (numeric)
- Target ID (string, ordinal)
8.2 Root ID Canonicalization
root_id = "root:" + phase + ":" + order + ":" + target_id
All components lowercase, no whitespace.
9. Implementation Status
| Component | Location | Status |
|---|---|---|
| ELF init parser | NativeCallgraphBuilder.cs | Implemented |
| Root model | NativeSyntheticRoot | Implemented |
| richgraph-v1 roots | RichGraph.cs | Implemented |
| Patch oracle roots | PatchOracleComparer.cs | Implemented |
| Policy integration | - | Pending |
| DT_NEEDED graph | - | Pending |
10. Test Fixtures
Location: tests/Binary/fixtures/init-roots/
| Fixture | Description |
|---|---|
elf-simple-init/ | Binary with single init function |
elf-init-array/ | Binary with multiple init_array entries |
elf-preinit/ | Binary with preinit_array |
elf-ctors/ | Binary with .ctors section |
elf-stripped-init/ | Stripped binary with init |
pe-dllmain/ | PE DLL with DllMain |
pe-tls-callbacks/ | PE with TLS callbacks |
11. Related Contracts
- richgraph-v1 - Root schema in graphs
- Build-ID Propagation - Binary identification
- Patch Oracles - Oracle validation
Changelog
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0.0 | 2025-12-13 | Scanner Guild | Initial contract for init-section roots |
