From d5278ba7e8390afcf25f7cd9cdda3c5b6b3f78b4 Mon Sep 17 00:00:00 2001 From: Calin Teodor Date: Sun, 7 Jun 2026 03:02:04 +0300 Subject: [PATCH] fix(keyexchange): zero salvage plaintext on evict (PILOT-146) RecordSalvage trims aged entries from the head and bounds size by dropping the oldest, but those reslicings only move the slice header; the backing array still holds the cleartext until GC reaches it. Same for DrainSalvage's filter pass that drops aged entries without returning them. Explicitly zero each evicted entry's Plaintext bytes before the reslice, so a heap-walking attacker or use-after-free probe can't recover plaintext that was supposed to be ephemeral. --- pkg/daemon/keyexchange/store.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/daemon/keyexchange/store.go b/pkg/daemon/keyexchange/store.go index b99b7fec..06a5d320 100644 --- a/pkg/daemon/keyexchange/store.go +++ b/pkg/daemon/keyexchange/store.go @@ -310,11 +310,21 @@ func (s *Store) RecordSalvage(c *Crypto, plaintext []byte) { cutoff := now.Add(-SalvageMaxAge) c.SalvageMu.Lock() // Trim aged entries from the head. + // PILOT-146: zero the plaintext bytes before reslicing — otherwise the + // backing array still holds the cleartext until GC walks it, which an + // arena-co-resident attacker could (theoretically) observe before + // reuse. for len(c.Salvage) > 0 && c.Salvage[0].When.Before(cutoff) { + for i := range c.Salvage[0].Plaintext { + c.Salvage[0].Plaintext[i] = 0 + } c.Salvage = c.Salvage[1:] } // Bound size — drop oldest. if len(c.Salvage) >= SalvageMaxEntries { + for i := range c.Salvage[0].Plaintext { + c.Salvage[0].Plaintext[i] = 0 + } c.Salvage = c.Salvage[1:] } cp := make([]byte, len(plaintext)) @@ -342,6 +352,11 @@ func (s *Store) DrainSalvage(c *Crypto) []SalvageEntry { out := entries[:0] for _, e := range entries { if e.When.Before(cutoff) { + // PILOT-146: aged entry not returned to caller — zero its + // plaintext before the slice header drops the reference. + for i := range e.Plaintext { + e.Plaintext[i] = 0 + } continue } out = append(out, e)