carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1| Previous finding | Disposition at `98c0139b` |
2|---|---|
3| R1-1 — Readiness without established database state | **Resolved.** Unknown pointers block readiness; off and expired pointers require empty mirrors. |
4| R1-2 — `off` waits behind publication | **Resolved.** `TryLock` returns 409 while publication holds the slot. |
5| R1-3 — Unbounded console pointer reads | **Resolved for reads.** Pointer reads are streamed and capped. |
6| R1-4 — Recovery generation deleted after failed synchronization | **Resolved for the reported paths.** Failed installations preserve loaded and retained generations. |
7| R1-5 — Rejected-upload test does not establish zero reads | **Resolved at the reservation boundary.** The panicking-reader test detects reads before reservation. |
8| R1-6 — Countries overflow ordering | **Resolved.** `_other` remains intact until rendering and sorts last. |
9| R2-1 — Feed status writes bypass the read cap | **Still partly resolved; P2 remains.** `geoipfeed.go:174` calls `Put`; `release/store.go:408` reads the entire existing object before checking its precondition. An oversized `feed.json` can exhaust memory. Apply the stat/streamed-hash checks used by `PutFrom`. |
10| R2-2 — Timed-out checks never persist failures | **Resolved for download timeouts.** Status persistence gets a separate bounded context. |
11| R2-3 — Unreadable feed status clears an alert | **Resolved.** Read and parse failures preserve established feed state. |
12| R2-4 — Unusable directory prevents “not loaded” alert | **Resolved.** Pointer state is retained before directory preparation. |
13| R2-5 — Published alert reasons never change while unhealthy | **Resolved.** Reason changes update `Value` and emit an event. |
14| R3-1 — Console follower keeps serving an expired database | **Resolved.** Loaded-reader expiry is checked independently of local state and candidate loading. |
15| R3-2 — Transient status-read failure erases feed history | **Resolved.** Unknown history is not overwritten. |
16| R4-1 — Unbounded bucket operations block startup or publication | **Resolved for the reported operations.** Startup cleanup is asynchronous; maintenance and staging reads have deadlines. |
17| R4-2 — Restart falsely clears a persisted GeoIP alert | **Resolved.** Unknown inputs cannot establish recovery. |
18| R4-3 — Email delivery suppresses active reason changes | **Resolved.** Successful delivery records the last reason per recipient. |
19| R4-4 — Region guesses carry another generation’s attribution | **Resolved.** Location and source are captured under one reader lock. |
20| R5-1 — Unknown feed state suppresses confirmed expiry | **Resolved.** Established unhealthy conditions precede the unknown-state guard. |
21| R6-1 — Failed startup deletion leaves a disabled feed alert firing indefinitely | **Resolved.** Deletion retries with bounded attempts and capped backoff. |
22| R7-1 — Unbounded feed edition metadata makes control records unreadable | **Resolved.** Editions are normalized HTTP dates; oversized serialized pointers are refused. |
23| R7-2 — Unchanged-edition checks leave the feed reported as “not run yet” | **Resolved.** Confirmed editions update success history. |
24| R8-1 — MMDB verification indefinitely holds the publish slot | **Resolved for the reported traversal attacks.** Verification runs in a child killed at its deadline; staging cleanup and slot release are deferred. |
25| R9-1 — Malformed MMDB metadata can crash the entire console | **Resolved for the reported arithmetic panic.** Initial parsing occurs in the verifier child. |
26| R10-1 — Verifier child has no memory bound | **Resolved on Linux for the reported allocation attack.** The child applies a 512 MiB `RLIMIT_DATA` before parsing and refuses verification if setting it fails. |
27| R10-2 — Retained recovery generations survive GeoLite2 expiry | **Resolved for the reported synchronization failure.** Unchanged-pointer polls retry synchronization and cleanup; retained generations receive independent expiry checks. |
28
29New findings:
30
311. **P1 — A database can pass verification and then crash serving processes during a lookup.**
32 [internal/console/geoipverify.go:58](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/geoip-for-apps/internal/console/geoipverify.go:58), [internal/geo/mmdb.go:186](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/geoip-for-apps/internal/geo/mmdb.go:186)
33
34 The pinned library’s `Verify` decodes records into generic values. Live lookups decode into `mmdbRecord`, which takes a different path: `decodeStruct` skips unknown fields using `nextValueOffset`. That function recursively calls itself once per skipped value, without the decoder’s depth limit.
35
36 A crafted database can return ordinary countries for the five probe addresses while placing a large flat boolean array in an unknown field on another public network. Generic verification iterates the array; sixteen million elements require roughly 256 MB of array storage, within the child’s memory budget. The probes never exercise that record’s struct-decoding path.
37
38 After publication, a lookup in that network creates millions of recursive stack frames in the serving process. Stack exhaustion is fatal, so a visitor request can terminate an edge, and an authenticated `/geo` request can terminate the console. The verifier child’s memory limit and deadline do not protect these later lookups.
39
40 **Fix:** validate every reachable record through the production decoding schema inside the bounded child, with a per-record complexity limit, or replace the recursive skip path with bounded iteration. Add a regression containing a large ignored array outside the probe networks and verify that it cannot reach an unsafe serving lookup.
41
42Validation was static, including inspection of the pinned decoder. Targeted Go tests could not start because creating the build directory failed with “read-only file system”; runtime reproduction and cross-compilation remain unverified.
43
44Verdict: revise.
45