mirror of https://github.com/golang/go.git
internal/trace/v2: avoid several panics for malformed traces
This addresses some panics (out of bounds slice accesses and nil pointer dereferences) when parsing malformed data. These were found via light fuzzing, not by any rigorous means, and more potential panics probably exist. Fixes #64878. Fixes #64879. Change-Id: I4085788ba7dc91fec62e4abd88f50777577db42f Reviewed-on: https://go-review.googlesource.com/c/go/+/552995 Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
b2dbfbfc23
commit
e58e813950
|
|
@ -9,6 +9,7 @@ package trace
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"internal/trace/v2/event"
|
||||
|
|
@ -123,8 +124,12 @@ func (d *dataTable[EI, E]) compactify() {
|
|||
minID = id
|
||||
}
|
||||
}
|
||||
if maxID >= math.MaxInt {
|
||||
// We can't create a slice big enough to hold maxID elements
|
||||
return
|
||||
}
|
||||
// We're willing to waste at most 2x memory.
|
||||
if int(maxID-minID) > 2*len(d.sparse) {
|
||||
if int(maxID-minID) > max(len(d.sparse), 2*len(d.sparse)) {
|
||||
return
|
||||
}
|
||||
if int(minID) > len(d.sparse) {
|
||||
|
|
@ -146,7 +151,7 @@ func (d *dataTable[EI, E]) get(id EI) (E, bool) {
|
|||
if id == 0 {
|
||||
return *new(E), true
|
||||
}
|
||||
if int(id) < len(d.dense) {
|
||||
if uint64(id) < uint64(len(d.dense)) {
|
||||
if d.present[id/8]&(uint8(1)<<(id%8)) != 0 {
|
||||
return d.dense[id], true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func readTimedBaseEvent(b []byte, e *baseEvent) (int, timestamp, error) {
|
|||
// Get the event type.
|
||||
typ := event.Type(b[0])
|
||||
specs := go122.Specs()
|
||||
if int(typ) > len(specs) {
|
||||
if int(typ) >= len(specs) {
|
||||
return 0, 0, fmt.Errorf("found invalid event type: %v", typ)
|
||||
}
|
||||
e.typ = typ
|
||||
|
|
@ -82,11 +82,17 @@ func readTimedBaseEvent(b []byte, e *baseEvent) (int, timestamp, error) {
|
|||
|
||||
// Read timestamp diff.
|
||||
ts, nb := binary.Uvarint(b[n:])
|
||||
if nb <= 0 {
|
||||
return 0, 0, fmt.Errorf("found invalid uvarint for timestamp")
|
||||
}
|
||||
n += nb
|
||||
|
||||
// Read the rest of the arguments.
|
||||
for i := 0; i < len(spec.Args)-1; i++ {
|
||||
arg, nb := binary.Uvarint(b[n:])
|
||||
if nb <= 0 {
|
||||
return 0, 0, fmt.Errorf("found invalid uvarint")
|
||||
}
|
||||
e.args[i] = arg
|
||||
n += nb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64)
|
|||
case go122.EvProcStatus:
|
||||
pid := ProcID(ev.args[0])
|
||||
status := go122.ProcStatus(ev.args[1])
|
||||
if int(status) >= len(go122ProcStatus2ProcState) {
|
||||
return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status)
|
||||
}
|
||||
oldState := go122ProcStatus2ProcState[status]
|
||||
if s, ok := o.pStates[pid]; ok {
|
||||
if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall {
|
||||
|
|
@ -268,6 +271,10 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64)
|
|||
gid := GoID(ev.args[0])
|
||||
mid := ThreadID(ev.args[1])
|
||||
status := go122.GoStatus(ev.args[2])
|
||||
|
||||
if int(status) >= len(go122GoStatus2GoState) {
|
||||
return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status)
|
||||
}
|
||||
oldState := go122GoStatus2GoState[status]
|
||||
if s, ok := o.gStates[gid]; ok {
|
||||
if s.status != status {
|
||||
|
|
@ -758,7 +765,11 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64)
|
|||
// ever reference curCtx.P. However, be lenient about this like we are with
|
||||
// GCMarkAssistActive; there's no reason the runtime couldn't change to block
|
||||
// in the middle of a sweep.
|
||||
if err := o.pStates[pid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
|
||||
pState, ok := o.pStates[pid]
|
||||
if !ok {
|
||||
return curCtx, false, fmt.Errorf("encountered GCSweepActive for unknown proc %d", pid)
|
||||
}
|
||||
if err := pState.activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
|
||||
return curCtx, false, err
|
||||
}
|
||||
return curCtx, true, nil
|
||||
|
|
@ -790,7 +801,11 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64)
|
|||
// N.B. Like GoStatus, this can happen at any time, because it can
|
||||
// reference a non-running goroutine. Don't check anything about the
|
||||
// current scheduler context.
|
||||
if err := o.gStates[gid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
|
||||
gState, ok := o.gStates[gid]
|
||||
if !ok {
|
||||
return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(typ))
|
||||
}
|
||||
if err := gState.activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
|
||||
return curCtx, false, err
|
||||
}
|
||||
return curCtx, true, nil
|
||||
|
|
@ -917,6 +932,10 @@ func (s *gState) beginRegion(r userRegion) error {
|
|||
|
||||
// endRegion ends a user region on the goroutine.
|
||||
func (s *gState) endRegion(r userRegion) error {
|
||||
if len(s.regions) == 0 {
|
||||
// We do not know about regions that began before tracing started.
|
||||
return nil
|
||||
}
|
||||
if next := s.regions[len(s.regions)-1]; next != r {
|
||||
return fmt.Errorf("misuse of region in goroutine %v: region end %v when the inner-most active region start event is %v", s.id, r, next)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ func (r *Reader) ReadEvent() (e Event, err error) {
|
|||
}
|
||||
// Try to advance the head of the frontier, which should have the minimum timestamp.
|
||||
// This should be by far the most common case
|
||||
if len(r.frontier) == 0 {
|
||||
return Event{}, fmt.Errorf("broken trace: frontier is empty:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order))
|
||||
}
|
||||
bc := r.frontier[0]
|
||||
if ctx, ok, err := r.order.advance(&bc.ev, r.gen.evTable, bc.m, r.gen.gen); err != nil {
|
||||
return Event{}, err
|
||||
|
|
|
|||
|
|
@ -46,6 +46,53 @@ func TestReaderGolden(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func FuzzReader(f *testing.F) {
|
||||
// Currently disabled because the parser doesn't do much validation and most
|
||||
// getters can be made to panic. Turn this on once the parser is meant to
|
||||
// reject invalid traces.
|
||||
const testGetters = false
|
||||
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
r, err := trace.NewReader(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
ev, err := r.ReadEvent()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if !testGetters {
|
||||
continue
|
||||
}
|
||||
// Make sure getters don't do anything that panics
|
||||
switch ev.Kind() {
|
||||
case trace.EventLabel:
|
||||
ev.Label()
|
||||
case trace.EventLog:
|
||||
ev.Log()
|
||||
case trace.EventMetric:
|
||||
ev.Metric()
|
||||
case trace.EventRangeActive, trace.EventRangeBegin:
|
||||
ev.Range()
|
||||
case trace.EventRangeEnd:
|
||||
ev.Range()
|
||||
ev.RangeAttributes()
|
||||
case trace.EventStateTransition:
|
||||
ev.StateTransition()
|
||||
case trace.EventRegionBegin, trace.EventRegionEnd:
|
||||
ev.Region()
|
||||
case trace.EventTaskBegin, trace.EventTaskEnd:
|
||||
ev.Task()
|
||||
case trace.EventSync:
|
||||
case trace.EventStackSample:
|
||||
case trace.EventBad:
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testReader(t *testing.T, tr io.Reader, exp *testtrace.Expectation) {
|
||||
r, err := trace.NewReader(tr)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x190000\x01\x0100\x88\x00\b0000000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0001")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00-0000\x01\x0100\x88\x00\b0000000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x0f00\x120\x01\x0100\x88\x00\b0000000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\b0000\x01\x01\xff00\xb8\x00\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1901\xff\xff\xff\xff\xff\xff\xff\xff0\x800")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x0100\x8c0\x85\x00\b0000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x1f0000\x01\x0100\x88\x00\b0000000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xf6\x9f\n\x9fÕ\xb4\x99\xb2\x06\x11\r\xa7\x02\x00\x01\x19\x05\x01\xf6\x9f\n\x02+\x04\x01\x00\x00")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\"0000\x01\x0100\x88\x00\b0000000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01001\x85\x00\b0000")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\x05\r\xa7\x02\x00E")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\f\x02\x03\xff\xff\xff\xff\xff\xff\xff\x9f\x1d\x00")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xfa\x9f\n\xa5ѕ\xb4\x99\xb2\x06\x0e\n\x97\x96\x96\x96\x96\x96\x96\x96\x96\x96\x01\x01\x01")
|
||||
Loading…
Reference in New Issue