summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-08 08:47:21 +0200
committerPaul Buetow <paul@buetow.org>2026-03-08 08:47:21 +0200
commitc67887f9abbfb726d20d1fa67dca0041a97398bc (patch)
tree406696a8d6a86961b993da6612866e816b724925
parenteca6d22322f99556c77867c11fd5205167af8d98 (diff)
task(ior): harden malformed raw-event decoding (task ed7a7a3f)
-rw-r--r--internal/eventloop.go88
-rw-r--r--internal/eventloop_error_handling_test.go31
-rw-r--r--internal/types/fastdecode.go27
-rw-r--r--internal/types/fastdecode_test.go25
4 files changed, 159 insertions, 12 deletions
diff --git a/internal/eventloop.go b/internal/eventloop.go
index 26eaafc..2def4c0 100644
--- a/internal/eventloop.go
+++ b/internal/eventloop.go
@@ -408,46 +408,106 @@ func (e *eventLoop) initRawHandlers() {
}
e.rawHandlers[types.ENTER_OPEN_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- if ev, ok := e.filter.openEvent(types.NewOpenEventFast(raw)); ok {
+ openEv := types.NewOpenEventFast(raw)
+ if openEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_OPEN_EVENT, raw)
+ return
+ }
+ if ev, ok := e.filter.openEvent(openEv); ok {
e.tracepointEntered(ev)
}
}
e.rawHandlers[types.EXIT_OPEN_EVENT] = func(raw []byte, ch chan<- *event.Pair) {
- e.tracepointExited(types.NewRetEventFast(raw), ch)
+ retEv := types.NewRetEventFast(raw)
+ if retEv == nil {
+ e.dropMalformedRawEvent(types.EXIT_OPEN_EVENT, raw)
+ return
+ }
+ e.tracepointExited(retEv, ch)
}
e.rawHandlers[types.ENTER_FD_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- e.tracepointEntered(types.NewFdEventFast(raw))
+ fdEv := types.NewFdEventFast(raw)
+ if fdEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_FD_EVENT, raw)
+ return
+ }
+ e.tracepointEntered(fdEv)
}
e.rawHandlers[types.EXIT_FD_EVENT] = func(raw []byte, ch chan<- *event.Pair) {
- e.tracepointExited(types.NewFdEventFast(raw), ch)
+ fdEv := types.NewFdEventFast(raw)
+ if fdEv == nil {
+ e.dropMalformedRawEvent(types.EXIT_FD_EVENT, raw)
+ return
+ }
+ e.tracepointExited(fdEv, ch)
}
e.rawHandlers[types.ENTER_NULL_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- e.tracepointEntered(types.NewNullEventFast(raw))
+ nullEv := types.NewNullEventFast(raw)
+ if nullEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_NULL_EVENT, raw)
+ return
+ }
+ e.tracepointEntered(nullEv)
}
e.rawHandlers[types.EXIT_NULL_EVENT] = func(raw []byte, ch chan<- *event.Pair) {
- e.tracepointExited(types.NewNullEventFast(raw), ch)
+ nullEv := types.NewNullEventFast(raw)
+ if nullEv == nil {
+ e.dropMalformedRawEvent(types.EXIT_NULL_EVENT, raw)
+ return
+ }
+ e.tracepointExited(nullEv, ch)
}
e.rawHandlers[types.EXIT_RET_EVENT] = func(raw []byte, ch chan<- *event.Pair) {
- e.tracepointExited(types.NewRetEventFast(raw), ch)
+ retEv := types.NewRetEventFast(raw)
+ if retEv == nil {
+ e.dropMalformedRawEvent(types.EXIT_RET_EVENT, raw)
+ return
+ }
+ e.tracepointExited(retEv, ch)
}
e.rawHandlers[types.ENTER_NAME_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- if ev, ok := e.filter.nameEvent(types.NewNameEventFast(raw)); ok {
+ nameEv := types.NewNameEventFast(raw)
+ if nameEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_NAME_EVENT, raw)
+ return
+ }
+ if ev, ok := e.filter.nameEvent(nameEv); ok {
e.tracepointEntered(ev)
}
}
e.rawHandlers[types.ENTER_PATH_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- if ev, ok := e.filter.pathEvent(types.NewPathEventFast(raw)); ok {
+ pathEv := types.NewPathEventFast(raw)
+ if pathEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_PATH_EVENT, raw)
+ return
+ }
+ if ev, ok := e.filter.pathEvent(pathEv); ok {
e.tracepointEntered(ev)
}
}
e.rawHandlers[types.ENTER_FCNTL_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- e.tracepointEntered(types.NewFcntlEventFast(raw))
+ fcntlEv := types.NewFcntlEventFast(raw)
+ if fcntlEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_FCNTL_EVENT, raw)
+ return
+ }
+ e.tracepointEntered(fcntlEv)
}
e.rawHandlers[types.ENTER_OPEN_BY_HANDLE_AT_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- e.tracepointEntered(types.NewOpenByHandleAtEventFast(raw))
+ openByHandleEv := types.NewOpenByHandleAtEventFast(raw)
+ if openByHandleEv == nil {
+ e.dropMalformedRawEvent(types.ENTER_OPEN_BY_HANDLE_AT_EVENT, raw)
+ return
+ }
+ e.tracepointEntered(openByHandleEv)
}
e.rawHandlers[types.ENTER_DUP3_EVENT] = func(raw []byte, _ chan<- *event.Pair) {
- e.tracepointEntered(types.NewDup3EventFast(raw))
+ dup3Ev := types.NewDup3EventFast(raw)
+ if dup3Ev == nil {
+ e.dropMalformedRawEvent(types.ENTER_DUP3_EVENT, raw)
+ return
+ }
+ e.tracepointEntered(dup3Ev)
}
}
@@ -849,6 +909,10 @@ func (e *eventLoop) notifyWarning(message string) {
e.warningCb(message)
}
+func (e *eventLoop) dropMalformedRawEvent(evType types.EventType, raw []byte) {
+ e.notifyWarning(fmt.Sprintf("Dropped malformed raw event type %d (len=%d)", evType, len(raw)))
+}
+
func (e *eventLoop) resolveFdFile(fd int32, pid uint32) file.File {
if fdFile, ok := e.fdState().get(fd); ok {
return fdFile
diff --git a/internal/eventloop_error_handling_test.go b/internal/eventloop_error_handling_test.go
index 8361dea..7f2c572 100644
--- a/internal/eventloop_error_handling_test.go
+++ b/internal/eventloop_error_handling_test.go
@@ -113,3 +113,34 @@ func TestProcessRawEventUnknownTypeDoesNotPanicAndNotifies(t *testing.T) {
t.Fatalf("expected warning notification")
}
}
+
+func TestProcessRawEventMalformedKnownTypeDoesNotPanicAndNotifies(t *testing.T) {
+ el := mustNewEventLoop(t, eventLoopConfig{})
+ warnings := make(chan string, 1)
+ el.warningCb = func(message string) { warnings <- message }
+
+ pairCh := make(chan *event.Pair, 1)
+
+ defer func() {
+ if r := recover(); r != nil {
+ t.Fatalf("processRawEvent panicked: %v", r)
+ }
+ }()
+
+ el.processRawEvent([]byte{byte(types.ENTER_OPEN_EVENT)}, pairCh)
+
+ select {
+ case ep := <-pairCh:
+ t.Fatalf("unexpected event produced: %v", ep)
+ default:
+ }
+
+ select {
+ case msg := <-warnings:
+ if msg == "" {
+ t.Fatalf("expected non-empty warning message")
+ }
+ default:
+ t.Fatalf("expected warning notification")
+ }
+}
diff --git a/internal/types/fastdecode.go b/internal/types/fastdecode.go
index 92b57c6..a0ce5f1 100644
--- a/internal/types/fastdecode.go
+++ b/internal/types/fastdecode.go
@@ -15,6 +15,9 @@ const (
)
func NewOpenEventFast(raw []byte) *OpenEvent {
+ if len(raw) < openEventSize {
+ return nil
+ }
if len(raw) != openEventSize {
return NewOpenEvent(raw)
}
@@ -31,6 +34,9 @@ func NewOpenEventFast(raw []byte) *OpenEvent {
}
func NewNullEventFast(raw []byte) *NullEvent {
+ if len(raw) < nullEventSize {
+ return nil
+ }
if len(raw) != nullEventSize {
return NewNullEvent(raw)
}
@@ -44,6 +50,9 @@ func NewNullEventFast(raw []byte) *NullEvent {
}
func NewFdEventFast(raw []byte) *FdEvent {
+ if len(raw) < fdEventSize {
+ return nil
+ }
if len(raw) != fdEventSize {
return NewFdEvent(raw)
}
@@ -58,6 +67,9 @@ func NewFdEventFast(raw []byte) *FdEvent {
}
func NewRetEventFast(raw []byte) *RetEvent {
+ if len(raw) < retEventSize {
+ return nil
+ }
if len(raw) != retEventSize {
return NewRetEvent(raw)
}
@@ -73,6 +85,9 @@ func NewRetEventFast(raw []byte) *RetEvent {
}
func NewNameEventFast(raw []byte) *NameEvent {
+ if len(raw) < nameEventSize {
+ return nil
+ }
if len(raw) != nameEventSize {
return NewNameEvent(raw)
}
@@ -88,6 +103,9 @@ func NewNameEventFast(raw []byte) *NameEvent {
}
func NewPathEventFast(raw []byte) *PathEvent {
+ if len(raw) < pathEventSize {
+ return nil
+ }
if len(raw) != pathEventSize {
return NewPathEvent(raw)
}
@@ -102,6 +120,9 @@ func NewPathEventFast(raw []byte) *PathEvent {
}
func NewFcntlEventFast(raw []byte) *FcntlEvent {
+ if len(raw) < fcntlEventSize {
+ return nil
+ }
if len(raw) != fcntlEventSize {
return NewFcntlEvent(raw)
}
@@ -118,6 +139,9 @@ func NewFcntlEventFast(raw []byte) *FcntlEvent {
}
func NewDup3EventFast(raw []byte) *Dup3Event {
+ if len(raw) < dup3EventSize {
+ return nil
+ }
if len(raw) != dup3EventSize {
return NewDup3Event(raw)
}
@@ -133,6 +157,9 @@ func NewDup3EventFast(raw []byte) *Dup3Event {
}
func NewOpenByHandleAtEventFast(raw []byte) *OpenByHandleAtEvent {
+ if len(raw) < openByHandleAtEventSize {
+ return nil
+ }
if len(raw) != openByHandleAtEventSize {
return NewOpenByHandleAtEvent(raw)
}
diff --git a/internal/types/fastdecode_test.go b/internal/types/fastdecode_test.go
index 3ce5592..f40faeb 100644
--- a/internal/types/fastdecode_test.go
+++ b/internal/types/fastdecode_test.go
@@ -125,3 +125,28 @@ func TestFastDecodersMatchGeneratedDecoders(t *testing.T) {
}
})
}
+
+func TestFastDecodersReturnNilOnShortPayload(t *testing.T) {
+ cases := []struct {
+ name string
+ decode func([]byte) bool
+ }{
+ {name: "OpenEvent", decode: func(raw []byte) bool { return NewOpenEventFast(raw) == nil }},
+ {name: "NullEvent", decode: func(raw []byte) bool { return NewNullEventFast(raw) == nil }},
+ {name: "FdEvent", decode: func(raw []byte) bool { return NewFdEventFast(raw) == nil }},
+ {name: "RetEvent", decode: func(raw []byte) bool { return NewRetEventFast(raw) == nil }},
+ {name: "NameEvent", decode: func(raw []byte) bool { return NewNameEventFast(raw) == nil }},
+ {name: "PathEvent", decode: func(raw []byte) bool { return NewPathEventFast(raw) == nil }},
+ {name: "FcntlEvent", decode: func(raw []byte) bool { return NewFcntlEventFast(raw) == nil }},
+ {name: "Dup3Event", decode: func(raw []byte) bool { return NewDup3EventFast(raw) == nil }},
+ {name: "OpenByHandleAtEvent", decode: func(raw []byte) bool { return NewOpenByHandleAtEventFast(raw) == nil }},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ if !tc.decode([]byte{1}) {
+ t.Fatalf("expected nil for short payload")
+ }
+ })
+ }
+}