diff --git a/verifier/store.go b/verifier/store.go index 7cb5d3a..07427dc 100644 --- a/verifier/store.go +++ b/verifier/store.go @@ -5,6 +5,7 @@ package verifier import ( "encoding/binary" + "errors" "fmt" "io" "sync/atomic" @@ -90,18 +91,23 @@ func (s *LogStore) StoreLog(log *raft.Log) error { } func encodeCheckpointMeta(startIdx, sum uint64) []byte { - var buf [16]byte - binary.LittleEndian.PutUint64(buf[0:8], startIdx) - binary.LittleEndian.PutUint64(buf[8:16], sum) + var buf [24]byte + binary.LittleEndian.PutUint64(buf[0:8], ExtensionMagicPrefix) + binary.LittleEndian.PutUint64(buf[8:16], startIdx) + binary.LittleEndian.PutUint64(buf[16:24], sum) return buf[:] } func decodeCheckpointMeta(bs []byte) (startIdx, sum uint64, err error) { - if len(bs) < 16 { + if len(bs) < 24 { return 0, 0, io.ErrShortBuffer } - startIdx = binary.LittleEndian.Uint64(bs[0:8]) - sum = binary.LittleEndian.Uint64(bs[8:16]) + magic := binary.LittleEndian.Uint64(bs[0:8]) + if magic != ExtensionMagicPrefix { + return 0, 0, errors.New("invalid extension data") + } + startIdx = binary.LittleEndian.Uint64(bs[8:16]) + sum = binary.LittleEndian.Uint64(bs[16:24]) return startIdx, sum, nil } diff --git a/verifier/verifier.go b/verifier/verifier.go index 181231a..b955bad 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -13,6 +13,25 @@ import ( "github.com/segmentio/fasthash/fnv1a" ) +const ( + // ExtensionMagicPrefix is the prefix we append to log Extensions fields to + // disambiguate from other middleware that may use extensions. This value is + // carefully constructed to be completely invalid as the beginning of a + // protobuf (3) wire protocol message since the other known user of this field + // encodes its data that way. If the first byte were 0xa8 this would be a + // valid protobuf field encoding for an int field, however currently the 3 + // least significant bits encode the field type as 7, which is not a valid + // type in the current spec. Even if this does change in the future, the + // field's tag number encoded here is 123456789 so it's extremely unlikely + // that any valid protobuf schema will ever have enough fields or arbitrarily + // decide to assign field tags that large (though unrecognized tags would be + // ignored on decode). Finally, the value of the field is the varint encoding + // of the randomly chosen value 53906 so if type 7 is ever valid in the future + // and used as a length-prefixed type, the length decoded would be way longer + // than the buffer making it invalid. + ExtensionMagicPrefix uint64 = 0xafd1f9d60392a503 +) + // IsCheckpointFn is a function that can decide whether the contents of a raft // log's Data represents a checkpoint message. It is called on every append so // it must be relatively fast in the common case. If it returns true for a log,