import (
"fmt"
+ "math"
"strings"
"internal/trace/v2/event"
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) {
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
}
// 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
// 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
}
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 {
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 {
// 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
// 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
// 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)
}
}
// 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
}
}
+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 {
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x190000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0001")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00-0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x0f00\x120\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
--- /dev/null
+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")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x0100\x8c0\x85\x00\b0000")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x1f0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0000")
\ No newline at end of file
--- /dev/null
+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")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\"0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
--- /dev/null
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01001\x85\x00\b0000")
\ No newline at end of file
--- /dev/null
+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")
\ No newline at end of file
--- /dev/null
+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")
\ No newline at end of file
--- /dev/null
+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")