dicore thread-ownership map¶
Who owns which resource, and how threads talk to each other. The libuv-style rule this codifies: the sweep thread owns all verdict state; every other thread is detached, stateless with respect to findings, and reports through exactly one narrow channel. No thread other than the sweep ever writes a finding, a counter, or the token.
The threads¶
| Thread / process | Created by | Owns | Talks to the rest via |
|---|---|---|---|
Caller's thread (JNI entry: NativeBridge.e / .c) |
the app | the entire sweep: verdict cores, dicore_verdict aggregation, token signing, the attested-session cache, the licence/g_mon monitor state |
is the rest |
| Custody monitor | pthread_create(monitor_main) — custody_wd.cpp, detached |
the beat-receive + chunk-MAC verification loop; the custody page accumulation | verifies MAC chunks off the socketpair; publishes only via the sticky custody_wd_note_* bits |
| Custody heartbeat | pthread_create(heartbeat_main) — custody_wd.cpp, detached |
beat scheduling toward the forked child | writes only to the socketpair |
| Custody child (separate process, not a thread) | fork() — custody_wd.cpp |
the SipHash key + its 16-byte custody half (exists nowhere else in the process) | releases 2 MAC'd bytes per clean verdict over the socketpair |
| Syscall smokescreen | pthread_create(dicore_smoke_thread) — antidebug_verdict.cpp, detached |
nothing — fire-and-forget decoy syscalls | touches no shared verdict state by contract (every decoy is read-only / own-process / expected-denied) |
JNI up-calls¶
framework_shim.cpp caches the JavaVM at JNI_OnLoad (framework_shim_set_vm).
Any thread that needs a framework value attaches its own JNIEnv around the call
(AttachCurrentThread — framework_shim.cpp) and reads a logic-free static off
FrameworkShim. Up-calls never throw: any failure returns an empty/false/-1
"unknown" sentinel. The sweep thread is the only caller on the hot path; the
custody and smokescreen threads make no up-calls at all.
What was removed (do not reintroduce informally)¶
The enforcement-era continuous kill-on-late-CRITICAL re-sweep daemon is gone
(lifecycle.cpp); dicore_verdict runs only when the app drives it. A stale
comment in framework_shim.h still mentioned that thread — the map above is the
authoritative list. Adding a new background thread requires: (1) an entry here,
(2) a stated single reporting channel, (3) no findings/counter writes, (4) a
fail-open story for its own death.
Reporting channels (complete list)¶
- socketpair bytes, MAC-verified — custody child → monitor.
- Sticky status bits
custody_wd_note_clean_sweep/_note_critical— sweep thread → watchdog (one-way, never read back into the verdict). - The return value of
dicore_verdict— sweep thread → app → backend.
Nothing else crosses a thread boundary. In particular there is no lock shared
between the smokescreen thread and any detector, and no detector reads custody
state except through the string_gate_wait availability gate documented in
custody_wd.h.