Implemented better testing and fixed skipepd frames

This commit is contained in:
Martino Ferrari
2026-07-01 16:39:34 +02:00
parent 7a326c5d78
commit 0bea41f866
46 changed files with 4358 additions and 1739 deletions
+15 -1
View File
@@ -127,7 +127,12 @@ func (s SignalInfo) NumElements() int {
if c == 0 {
c = 1
}
return r * c
/* HI-2: cap at 1M to prevent integer overflow / OOM from crafted packets */
n := r * c
if n < 0 || n > 1024*1024 {
return 1024 * 1024
}
return n
}
// rawTypeSize returns the byte size for one element of the raw (unquantised) type.
@@ -227,6 +232,11 @@ func ParseConfig(payload []byte) ([]SignalInfo, uint8, error) {
return nil, 0, fmt.Errorf("config payload too short")
}
numSigs := binary.LittleEndian.Uint32(payload[0:4])
/* HI-2: validate numSigs against payload length before allocating */
maxSigs := uint32(len(payload) / SigDescSize)
if numSigs > maxSigs {
return nil, 0, fmt.Errorf("config claims %d signals but payload can hold at most %d", numSigs, maxSigs)
}
offset := 4
sigs := make([]SignalInfo, 0, numSigs)
for i := uint32(0); i < numSigs; i++ {
@@ -327,6 +337,10 @@ func ParseData(payload []byte, sigs []SignalInfo, publishMode uint8, arrivalTime
if numSamples == 0 {
return []DataSample{}, nil
}
/* HI-2: sanity-cap numSamples to prevent OOM from crafted packets */
if numSamples < 0 || numSamples > 1024*1024 {
return nil, fmt.Errorf("accumulate numSamples %d out of range", numSamples)
}
// Parse per-signal data blocks (all slots for a signal are contiguous).
accumVals := make(map[string][]float64, len(sigs)) // scalars: numSamples values
@@ -0,0 +1,97 @@
package udpsprotocol
import (
"encoding/binary"
"math"
"testing"
"time"
)
// TestParseConfig_HugeNumSigs_NoOOM — a CONFIG payload claiming 0xFFFFFFFF signals
// must return an error, not panic/OOM.
func TestParseConfig_HugeNumSigs_NoOOM(t *testing.T) {
// 4 bytes: numSigs = 0xFFFFFFFF, then nothing else
payload := make([]byte, 4)
binary.LittleEndian.PutUint32(payload[0:4], 0xFFFFFFFF)
sigs, _, err := ParseConfig(payload)
if err == nil {
t.Fatal("expected error for huge numSigs, got nil")
}
if sigs != nil {
t.Fatalf("expected nil sigs, got %d", len(sigs))
}
}
// TestParseConfig_ValidSmallConfig — a minimal valid CONFIG parses correctly.
func TestParseConfig_ValidSmallConfig(t *testing.T) {
// 1 signal, then publish mode
payload := make([]byte, 4+SigDescSize+1)
binary.LittleEndian.PutUint32(payload[0:4], 1)
// Set typeCode to float32 (8) at offset 64
payload[4+64] = 8
// numRows=1, numCols=1 at offsets 67, 71
binary.LittleEndian.PutUint32(payload[4+67:4+71], 1)
binary.LittleEndian.PutUint32(payload[4+71:4+75], 1)
// publish mode = 0 (Strict)
payload[4+SigDescSize] = 0
sigs, pm, err := ParseConfig(payload)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(sigs) != 1 {
t.Fatalf("expected 1 signal, got %d", len(sigs))
}
if pm != PublishModeStrict {
t.Fatalf("expected Strict mode, got %d", pm)
}
}
// TestNumElements_OverflowCapped — huge numRows*numCols is capped, no panic.
func TestNumElements_OverflowCapped(t *testing.T) {
s := SignalInfo{NumRows: 0xFFFFFFFF, NumCols: 0xFFFFFFFF}
n := s.NumElements()
if n <= 0 || n > 1024*1024 {
t.Fatalf("expected capped value 1M, got %d", n)
}
}
// TestNumElements_Normal — normal values work correctly.
func TestNumElements_Normal(t *testing.T) {
s := SignalInfo{NumRows: 3, NumCols: 4}
if n := s.NumElements(); n != 12 {
t.Fatalf("expected 12, got %d", n)
}
}
// TestParseData_HugeNumSamples_NoOOM — an Accumulate DATA packet with
// numSamples=0xFFFFFFFF must return an error, not OOM.
func TestParseData_HugeNumSamples_NoOOM(t *testing.T) {
sigs := []SignalInfo{
{Name: "test", TypeCode: 8, NumRows: 1, NumCols: 1, QuantType: QuantNone},
}
payload := make([]byte, 12)
binary.LittleEndian.PutUint64(payload[0:8], 0) // HRT
binary.LittleEndian.PutUint32(payload[8:12], 0xFFFFFFFF)
_, err := ParseData(payload, sigs, PublishModeAccumulate, time.Now())
if err == nil {
t.Fatal("expected error for huge numSamples, got nil")
}
}
// TestParseData_ValidStrict — a valid Strict DATA packet parses without error.
func TestParseData_ValidStrict(t *testing.T) {
sigs := []SignalInfo{
{Name: "test", TypeCode: 8, NumRows: 1, NumCols: 1, QuantType: QuantNone},
}
// 8 HRT + 4 bytes float32
payload := make([]byte, 12)
binary.LittleEndian.PutUint64(payload[0:8], 1000)
binary.LittleEndian.PutUint32(payload[8:12], math.Float32bits(3.14))
samples, err := ParseData(payload, sigs, PublishModeStrict, time.Now())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(samples) != 1 {
t.Fatalf("expected 1 sample, got %d", len(samples))
}
}