mirror of
https://github.com/vee1e/subidx.git
synced 2026-09-01 17:57:13 +00:00
store: survive corrupt stored values
apply() sliced record values at fixed offsets and getMeta, Count and Top called Uint64 without checking length, so a truncated or hand-corrupted value panicked the ingest loop or the stats command. All decoders now validate lengths; corrupt records are overwritten, corrupt meta/count values return errors.
This commit is contained in:
parent
d407ac0840
commit
ad4d81fe50
2 changed files with 63 additions and 9 deletions
|
|
@ -182,12 +182,17 @@ func (s *Store) apply(batch *pebble.Batch, r Record) error {
|
|||
if err == pebble.ErrNotFound {
|
||||
st = nil
|
||||
} else {
|
||||
st = &recState{
|
||||
firstSeen: int64(binary.BigEndian.Uint64(existing[:8])),
|
||||
source: existing[8],
|
||||
seq: binary.BigEndian.Uint64(existing[9:17]),
|
||||
if len(existing) < 17 {
|
||||
closer.Close()
|
||||
st = nil
|
||||
} else {
|
||||
st = &recState{
|
||||
firstSeen: int64(binary.BigEndian.Uint64(existing[:8])),
|
||||
source: existing[8],
|
||||
seq: binary.BigEndian.Uint64(existing[9:17]),
|
||||
}
|
||||
closer.Close()
|
||||
}
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
if st == nil {
|
||||
|
|
@ -228,7 +233,11 @@ func (s *Store) pendingCount(apex string) uint64 {
|
|||
s.pendingCounts[apex] = 0
|
||||
return 0
|
||||
}
|
||||
n := binary.BigEndian.Uint64(v)
|
||||
n, ok := beUint64(v)
|
||||
if !ok {
|
||||
s.pendingCounts[apex] = 0
|
||||
return 0
|
||||
}
|
||||
s.pendingCounts[apex] = n
|
||||
return n
|
||||
}
|
||||
|
|
@ -303,7 +312,11 @@ func (s *Store) Count(apex string) (uint64, error) {
|
|||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return binary.BigEndian.Uint64(v), nil
|
||||
n, ok := beUint64(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("corrupt count value for %s", apex)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (s *Store) Total() (uint64, error) {
|
||||
|
|
@ -320,7 +333,11 @@ func (s *Store) Top(n int) ([]ApexCount, error) {
|
|||
defer it.Close()
|
||||
all := make([]ApexCount, 0, 1024)
|
||||
for ok := it.First(); ok; ok = it.Next() {
|
||||
all = append(all, ApexCount{Apex: string(it.Key()[1:]), Count: binary.BigEndian.Uint64(it.Value())})
|
||||
n, ok := beUint64(it.Value())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
all = append(all, ApexCount{Apex: string(it.Key()[1:]), Count: n})
|
||||
}
|
||||
for i := 0; i < len(all); i++ {
|
||||
for j := i + 1; j < len(all); j++ {
|
||||
|
|
@ -471,7 +488,11 @@ func (s *Store) getMeta(name string) (uint64, error) {
|
|||
if err != nil || v == nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint64(v), nil
|
||||
n, ok := beUint64(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("corrupt meta value for %s", name)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func recordKey(apex, sub string) []byte {
|
||||
|
|
@ -498,6 +519,13 @@ func metaKey(name string) []byte {
|
|||
|
||||
func totalKey() []byte { return metaKey("total") }
|
||||
|
||||
func beUint64(b []byte) (uint64, bool) {
|
||||
if len(b) != 8 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.BigEndian.Uint64(b), true
|
||||
}
|
||||
|
||||
func encodeValue(firstSeen int64, source byte, seq uint64) []byte {
|
||||
v := make([]byte, 17)
|
||||
binary.BigEndian.PutUint64(v[:8], uint64(firstSeen))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/pebble/v2"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -99,3 +100,28 @@ func TestRecount(t *testing.T) {
|
|||
t.Errorf("total after recount+ingest = %d, want 4", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorruptValuesDoNotPanic(t *testing.T) {
|
||||
dir := filepath.Join(t.TempDir(), "db")
|
||||
st, err := Open(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.Ingest(Record{Apex: "example.com", Sub: "a.example.com", FirstSeen: 1, Source: 1}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
st.db.Set([]byte("example.com\x00junk"), []byte{1, 2, 3}, nil)
|
||||
st.db.Set(metaKey("total"), []byte{9}, pebble.Sync)
|
||||
res, err := st.Scan("example.com", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("scan = %d results, want 1", len(res))
|
||||
}
|
||||
if _, err := st.Total(); err == nil {
|
||||
t.Error("corrupt meta accepted")
|
||||
}
|
||||
st.Close()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue