carlos / platform Public
| Previous finding | Disposition at ad70f841 | |---|---| | R1-1 — Readiness without established database state | Resolved. Unknown pointers block readiness; off and expired pointers require empty mirrors. | | R1-2 — off waits behind publication | Resolved. TryLock returns 409 while publication holds the slot. | | R1-3 — Unbounded console pointer reads | Resolved for reads. Pointer reads are streamed and capped. | | R1-4 — Recovery generation deleted after failed synchronization | Resolved for the reported paths. Installation failures preserve loaded and retained generations. | | R1-5 — Rejected-upload test does not establish zero reads | Resolved at the reservation boundary. The panicking-reader test detects reads before reservation. | | R1-6 — Countries overflow ordering | Resolved. _other remains intact until rendering and sorts last. | | R2-1 — Feed status writes bypass the read cap | Partly resolved; P2 remains. geoipfeed.go:174 still 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. | | R2-2 — Timed-out checks never persist failures | Resolved for download timeouts. Status persistence gets a separate bounded context. | | R2-3 — Unreadable feed status clears an alert | Resolved. Read and parse failures preserve established feed state. | | R2-4 — Unusable directory prevents “not loaded” alert | Resolved. Pointer state is retained before directory preparation. | | R2-5 — Published alert reasons never change while unhealthy | Resolved. Reason changes update Value and emit an event. | | R3-1 — Console follower keeps serving an expired database | Resolved. Loaded-reader expiry is checked independently of local state and candidate loading. | | R3-2 — Transient status-read failure erases feed history | Resolved. Unknown history is not overwritten. | | R4-1 — Unbounded bucket operations block startup or publication | Resolved for the reported bucket operations. Startup cleanup is asynchronous; maintenance and staging reads have deadlines. | | R4-2 — Restart falsely clears a persisted GeoIP alert | Resolved. Unknown inputs cannot establish recovery. | | R4-3 — Email delivery suppresses active reason changes | Resolved. Successful delivery records the last reason per recipient and resets other reason markers. | | R4-4 — Region guesses carry another generation’s attribution | Resolved. Location and source are captured under one reader lock. | | R5-1 — Unknown feed state suppresses confirmed expiry | Resolved. Established unhealthy conditions are evaluated before the unknown-state guard. | | R6-1 — Failed startup deletion leaves a disabled feed alert firing indefinitely | Resolved. Deletion retries with bounded attempts and capped exponential backoff. | | R7-1 — Unbounded feed edition metadata makes control records unreadable | Resolved for the reported input. MaxMind editions are normalized HTTP dates; publication rejects oversized serialized pointers. | | R7-2 — Unchanged-edition checks leave the feed reported as “not run yet” | Resolved. Both providers return the confirmed edition, allowing success and edition history to be recorded. | | R8-1 — MMDB verification indefinitely holds the publish slot | Partly resolved; P1 remains. At internal/geo/mmdb.go:74, the budget and cancellation checks run only after Networks.Next() returns a network. The dependency traverses empty branches internally without returning. A 128-node IPv6 DAG whose children share the next node and terminate at the NodeCount empty sentinel makes the first Next() traverse \(2^{128}\) empty paths. Publication remains stuck beyond its deadline, blocking off and retaining staging. Bound internal traversal steps, including empty branches, or use a killable verifier process; add this variant to the regression tests. |
New findings:
- P1 — Malformed MMDB metadata can crash the entire console. internal/geo/mmdb.go:55, internal/console/apisystemgeoip.go:84
On a 64-bit host, a small MMDB can encode node_count = 2^63, record_size = 24, and ip_version = 6. The pinned library accepts the metadata, then its unchecked NodeCount * RecordSize / 4 calculation wraps to zero. Its IPv4-subtree initialization indexes the resulting empty node buffer and panics, before either verification or the country/type checks.
A hostile feed response or downloaded operator upload therefore terminates the console process: both feed processing and publication run in goroutines without panic recovery. HTTP handler recovery cannot catch the publication goroutine’s panic.
Validate metadata arithmetic and bounds before the unsafe parser runs, or isolate parsing and verification in a killable worker whose crash becomes a refused publication. Any recovery-based solution must also release the mapping allocated before parsing. Add a regression asserting process survival, slot release, staging cleanup, and preservation of the previous pointer.
Validation was static, including inspection of the pinned parser and iterator. Targeted Go tests could not start because creating the build work directory failed with “read-only file system”; cross-compilation remains unverified.
Verdict: revise.