mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
wal: check out of range slice in "ReadAll", "decoder"
wal: add slice bound checks in decoder CHANGELOG-3.5: add wal slice bound check CHANGELOG-3.5: add "decodeRecord" Signed-off-by: Gyuho Lee <leegyuho@amazon.com>
This commit is contained in:
parent
13465d6d3d
commit
b14255c0b4
@ -56,6 +56,11 @@ func (d *decoder) decode(rec *walpb.Record) error {
|
|||||||
return d.decodeRecord(rec)
|
return d.decodeRecord(rec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// raft max message size is set to 1 MB in etcd server
|
||||||
|
// assume projects set reasonable message size limit,
|
||||||
|
// thus entry size should never exceed 10 MB
|
||||||
|
const maxWALEntrySizeLimit = int64(10 * 1024 * 1024)
|
||||||
|
|
||||||
func (d *decoder) decodeRecord(rec *walpb.Record) error {
|
func (d *decoder) decodeRecord(rec *walpb.Record) error {
|
||||||
if len(d.brs) == 0 {
|
if len(d.brs) == 0 {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
@ -76,6 +81,9 @@ func (d *decoder) decodeRecord(rec *walpb.Record) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
recBytes, padBytes := decodeFrameSize(l)
|
recBytes, padBytes := decodeFrameSize(l)
|
||||||
|
if recBytes >= maxWALEntrySizeLimit-padBytes {
|
||||||
|
return ErrMaxWALEntrySizeLimitExceeded
|
||||||
|
}
|
||||||
|
|
||||||
data := make([]byte, recBytes+padBytes)
|
data := make([]byte, recBytes+padBytes)
|
||||||
if _, err = io.ReadFull(d.brs[0], data); err != nil {
|
if _, err = io.ReadFull(d.brs[0], data); err != nil {
|
||||||
|
24
wal/wal.go
24
wal/wal.go
@ -54,13 +54,14 @@ var (
|
|||||||
SegmentSizeBytes int64 = 64 * 1000 * 1000 // 64MB
|
SegmentSizeBytes int64 = 64 * 1000 * 1000 // 64MB
|
||||||
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal")
|
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal")
|
||||||
|
ErrMetadataConflict = errors.New("wal: conflicting metadata found")
|
||||||
ErrMetadataConflict = errors.New("wal: conflicting metadata found")
|
ErrFileNotFound = errors.New("wal: file not found")
|
||||||
ErrFileNotFound = errors.New("wal: file not found")
|
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
||||||
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
ErrSnapshotMismatch = errors.New("wal: snapshot mismatch")
|
||||||
ErrSnapshotMismatch = errors.New("wal: snapshot mismatch")
|
ErrSnapshotNotFound = errors.New("wal: snapshot not found")
|
||||||
ErrSnapshotNotFound = errors.New("wal: snapshot not found")
|
ErrSliceOutOfRange = errors.New("wal: slice bounds out of range")
|
||||||
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
ErrMaxWALEntrySizeLimitExceeded = errors.New("wal: max entry size limit exceeded")
|
||||||
|
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||||
)
|
)
|
||||||
|
|
||||||
// WAL is a logical representation of the stable storage.
|
// WAL is a logical representation of the stable storage.
|
||||||
@ -268,8 +269,15 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
|
|||||||
switch rec.Type {
|
switch rec.Type {
|
||||||
case entryType:
|
case entryType:
|
||||||
e := mustUnmarshalEntry(rec.Data)
|
e := mustUnmarshalEntry(rec.Data)
|
||||||
|
// 0 <= e.Index-w.start.Index - 1 < len(ents)
|
||||||
if e.Index > w.start.Index {
|
if e.Index > w.start.Index {
|
||||||
ents = append(ents[:e.Index-w.start.Index-1], e)
|
// prevent "panic: runtime error: slice bounds out of range [:13038096702221461992] with capacity 0"
|
||||||
|
up := e.Index - w.start.Index - 1
|
||||||
|
if up > uint64(len(ents)) {
|
||||||
|
// return error before append call causes runtime panic
|
||||||
|
return nil, state, nil, ErrSliceOutOfRange
|
||||||
|
}
|
||||||
|
ents = append(ents[:up], e)
|
||||||
}
|
}
|
||||||
w.enti = e.Index
|
w.enti = e.Index
|
||||||
case stateType:
|
case stateType:
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -524,6 +525,35 @@ func TestOpenForRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenWithMaxIndex(t *testing.T) {
|
||||||
|
p, err := ioutil.TempDir(os.TempDir(), "waltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(p)
|
||||||
|
// create WAL
|
||||||
|
w, err := Create(p, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
es := []raftpb.Entry{{Index: uint64(math.MaxInt64)}}
|
||||||
|
if err = w.Save(raftpb.HardState{}, es); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
w, err = Open(p, walpb.Snapshot{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, _, err = w.ReadAll()
|
||||||
|
if err == nil || err != ErrSliceOutOfRange {
|
||||||
|
t.Fatalf("err = %v, want ErrSliceOutOfRange", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSaveEmpty(t *testing.T) {
|
func TestSaveEmpty(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
var est raftpb.HardState
|
var est raftpb.HardState
|
||||||
|
Loading…
x
Reference in New Issue
Block a user