master
go 419 lines 9.88 KB
Raw
1 package protocol
2
3 import (
4 "testing"
5 )
6
7 // ---------------------------------------------------------------------------
8 // Fuzz targets for all decode paths.
9 //
10 // Each target feeds arbitrary bytes to a decode function and, if the decode
11 // succeeds, exercises the result. The invariant: no input may cause a panic.
12 // ---------------------------------------------------------------------------
13
14 func FuzzDecodeHeader(f *testing.F) {
15 // Seed with a valid header.
16 var seed [HeaderSize]byte
17 h := Header{
18 Magic: MagicMsg, Version: Version, HeaderLen: HeaderLen,
19 Kind: KindRequest, Code: MethodIncrement, PayloadLen: 0,
20 ItemCount: 1, MessageID: 1,
21 }
22 h.Encode(seed[:])
23 f.Add(seed[:])
24
25 // Seed with truncated and zeroed inputs.
26 f.Add([]byte{})
27 f.Add(make([]byte, 31))
28 f.Add(make([]byte, 32))
29
30 f.Fuzz(func(t *testing.T, data []byte) {
31 hdr, err := DecodeHeader(data)
32 if err != nil {
33 return
34 }
35 // Exercise the decoded result.
36 _ = hdr.Magic
37 _ = hdr.Version
38 _ = hdr.Kind
39 _ = hdr.Flags
40 _ = hdr.Code
41 _ = hdr.TransportStatus
42 _ = hdr.PayloadLen
43 _ = hdr.ItemCount
44 _ = hdr.MessageID
45 })
46 }
47
48 func FuzzDecodeChunkHeader(f *testing.F) {
49 var seed [HeaderSize]byte
50 c := ChunkHeader{
51 Magic: MagicChunk, Version: Version, Flags: 0,
52 MessageID: 1, TotalMessageLen: 256,
53 ChunkIndex: 0, ChunkCount: 3, ChunkPayloadLen: 100,
54 }
55 c.Encode(seed[:])
56 f.Add(seed[:])
57
58 f.Add([]byte{})
59 f.Add(make([]byte, 31))
60 f.Add(make([]byte, 32))
61
62 f.Fuzz(func(t *testing.T, data []byte) {
63 chk, err := DecodeChunkHeader(data)
64 if err != nil {
65 return
66 }
67 _ = chk.Magic
68 _ = chk.Version
69 _ = chk.Flags
70 _ = chk.MessageID
71 _ = chk.TotalMessageLen
72 _ = chk.ChunkIndex
73 _ = chk.ChunkCount
74 _ = chk.ChunkPayloadLen
75 })
76 }
77
78 func FuzzDecodeHello(f *testing.F) {
79 var seed [64]byte
80 h := Hello{
81 LayoutVersion: 1, SupportedProfiles: ProfileBaseline,
82 PreferredProfiles: ProfileBaseline,
83 MaxRequestPayloadBytes: 1024, MaxRequestBatchItems: 1,
84 MaxResponsePayloadBytes: 1024, MaxResponseBatchItems: 1,
85 AuthToken: 0xABCD, PacketSize: 65536,
86 }
87 h.Encode(seed[:])
88 f.Add(seed[:44])
89
90 f.Add([]byte{})
91 f.Add(make([]byte, 43))
92 f.Add(make([]byte, 44))
93
94 f.Fuzz(func(t *testing.T, data []byte) {
95 hello, err := DecodeHello(data)
96 if err != nil {
97 return
98 }
99 _ = hello.LayoutVersion
100 _ = hello.Flags
101 _ = hello.SupportedProfiles
102 _ = hello.PreferredProfiles
103 _ = hello.MaxRequestPayloadBytes
104 _ = hello.MaxRequestBatchItems
105 _ = hello.MaxResponsePayloadBytes
106 _ = hello.MaxResponseBatchItems
107 _ = hello.AuthToken
108 _ = hello.PacketSize
109 })
110 }
111
112 func FuzzDecodeHelloAck(f *testing.F) {
113 var seed [64]byte
114 h := HelloAck{
115 LayoutVersion: 1, ServerSupportedProfiles: 0x07,
116 IntersectionProfiles: 0x05, SelectedProfile: ProfileSHMFutex,
117 AgreedMaxRequestPayloadBytes: 2048, AgreedMaxRequestBatchItems: 50,
118 AgreedMaxResponsePayloadBytes: 65536, AgreedMaxResponseBatchItems: 1,
119 AgreedPacketSize: 32768,
120 }
121 h.Encode(seed[:])
122 f.Add(seed[:48])
123
124 f.Add([]byte{})
125 f.Add(make([]byte, 47))
126 f.Add(make([]byte, 48))
127
128 f.Fuzz(func(t *testing.T, data []byte) {
129 ack, err := DecodeHelloAck(data)
130 if err != nil {
131 return
132 }
133 _ = ack.LayoutVersion
134 _ = ack.Flags
135 _ = ack.ServerSupportedProfiles
136 _ = ack.IntersectionProfiles
137 _ = ack.SelectedProfile
138 _ = ack.AgreedMaxRequestPayloadBytes
139 _ = ack.AgreedMaxRequestBatchItems
140 _ = ack.AgreedMaxResponsePayloadBytes
141 _ = ack.AgreedMaxResponseBatchItems
142 _ = ack.AgreedPacketSize
143 })
144 }
145
146 func FuzzDecodeCgroupsRequest(f *testing.F) {
147 var seed [4]byte
148 r := CgroupsRequest{LayoutVersion: 1, Flags: 0}
149 r.Encode(seed[:])
150 f.Add(seed[:])
151
152 f.Add([]byte{})
153 f.Add(make([]byte, 3))
154 f.Add(make([]byte, 4))
155
156 f.Fuzz(func(t *testing.T, data []byte) {
157 req, err := DecodeCgroupsRequest(data)
158 if err != nil {
159 return
160 }
161 _ = req.LayoutVersion
162 _ = req.Flags
163 })
164 }
165
166 func FuzzDecodeCgroupsResponse(f *testing.F) {
167 // Seed: empty snapshot (24 bytes).
168 var emptyBuf [4096]byte
169 eb := NewCgroupsBuilder(emptyBuf[:], 0, 1, 42)
170 emptyTotal := eb.Finish()
171 f.Add(emptyBuf[:emptyTotal])
172
173 // Seed: single-item snapshot.
174 var singleBuf [4096]byte
175 sb := NewCgroupsBuilder(singleBuf[:], 1, 0, 100)
176 sb.Add(12345, 0x01, 1, []byte("docker-abc123"), []byte("/sys/fs/cgroup/docker/abc123"))
177 singleTotal := sb.Finish()
178 f.Add(singleBuf[:singleTotal])
179
180 // Seed: garbage inputs.
181 f.Add([]byte{})
182 f.Add(make([]byte, 23))
183 f.Add(make([]byte, 64))
184
185 f.Fuzz(func(t *testing.T, data []byte) {
186 view, err := DecodeCgroupsResponse(data)
187 if err != nil {
188 return
189 }
190 // Exercise all items.
191 for i := uint32(0); i < view.ItemCount; i++ {
192 item, ierr := view.Item(i)
193 if ierr != nil {
194 continue
195 }
196 _ = item.LayoutVersion
197 _ = item.Flags
198 _ = item.Hash
199 _ = item.Options
200 _ = item.Enabled
201 _ = item.Name.Bytes()
202 _ = item.Name.Len()
203 _ = item.Name.String()
204 _ = item.Path.Bytes()
205 _ = item.Path.Len()
206 _ = item.Path.String()
207 }
208 // Out-of-bounds item access should not panic.
209 _, _ = view.Item(view.ItemCount)
210 if view.ItemCount > 0 {
211 _, _ = view.Item(view.ItemCount + 1)
212 }
213 })
214 }
215
216 func FuzzDecodeCgroupsLookupRequest(f *testing.F) {
217 var seed [128]byte
218 n, err := EncodeCgroupsLookupRequest([][]byte{[]byte("/a"), []byte("/b/c")}, seed[:])
219 if err == nil {
220 f.Add(seed[:n])
221 }
222 f.Add([]byte{})
223 f.Add(make([]byte, CgroupsLookupReqHdr-1))
224 f.Add(make([]byte, CgroupsLookupReqHdr))
225
226 f.Fuzz(func(t *testing.T, data []byte) {
227 view, err := DecodeCgroupsLookupRequest(data)
228 if err != nil {
229 return
230 }
231 for i := uint32(0); i < view.ItemCount; i++ {
232 item, ierr := view.Item(i)
233 if ierr == nil {
234 _ = item.Bytes()
235 _ = item.String()
236 }
237 }
238 _, _ = view.Item(view.ItemCount)
239 })
240 }
241
242 func FuzzDecodeCgroupsLookupResponse(f *testing.F) {
243 var empty [128]byte
244 eb := NewCgroupsLookupBuilder(empty[:], 0, 0)
245 f.Add(empty[:eb.Finish()])
246
247 var seed [512]byte
248 builder := NewCgroupsLookupBuilder(seed[:], 1, 1)
249 if err := builder.Add(
250 CgroupLookupKnown,
251 OrchestratorK8s,
252 []byte("/a"),
253 []byte("pod-a"),
254 []struct{ Key, Value []byte }{{Key: []byte("namespace"), Value: []byte("default")}},
255 ); err == nil {
256 f.Add(seed[:builder.Finish()])
257 }
258 f.Add([]byte{})
259 f.Add(make([]byte, CgroupsLookupRespHdr-1))
260 f.Add(make([]byte, CgroupsLookupRespHdr))
261
262 f.Fuzz(func(t *testing.T, data []byte) {
263 view, err := DecodeCgroupsLookupResponse(data)
264 if err != nil {
265 return
266 }
267 for i := uint32(0); i < view.ItemCount; i++ {
268 item, ierr := view.Item(i)
269 if ierr != nil {
270 continue
271 }
272 _ = item.Path.Bytes()
273 _ = item.Name.String()
274 for j := uint32(0); j < uint32(item.LabelCount); j++ {
275 label, lerr := item.Label(j)
276 if lerr == nil {
277 _ = label.Key.String()
278 _ = label.Value.String()
279 }
280 }
281 }
282 _, _ = view.Item(view.ItemCount)
283 })
284 }
285
286 func FuzzDecodeAppsLookupRequest(f *testing.F) {
287 var seed [128]byte
288 n, err := EncodeAppsLookupRequest([]uint32{0, 1234}, seed[:])
289 if err == nil {
290 f.Add(seed[:n])
291 }
292 f.Add([]byte{})
293 f.Add(make([]byte, AppsLookupReqHdr-1))
294 f.Add(make([]byte, AppsLookupReqHdr))
295
296 f.Fuzz(func(t *testing.T, data []byte) {
297 view, err := DecodeAppsLookupRequest(data)
298 if err != nil {
299 return
300 }
301 for i := uint32(0); i < view.ItemCount; i++ {
302 pid, ierr := view.Item(i)
303 if ierr == nil {
304 _ = pid
305 }
306 }
307 _, _ = view.Item(view.ItemCount)
308 })
309 }
310
311 func FuzzDecodeAppsLookupResponse(f *testing.F) {
312 var empty [128]byte
313 eb := NewAppsLookupBuilder(empty[:], 0, 0)
314 f.Add(empty[:eb.Finish()])
315
316 var seed [1024]byte
317 builder := NewAppsLookupBuilder(seed[:], 1, 1)
318 if err := builder.Add(
319 PidLookupKnown,
320 AppsCgroupKnown,
321 OrchestratorDocker,
322 1234,
323 1,
324 1000,
325 42,
326 []byte("nginx"),
327 []byte("/docker/abc"),
328 []byte("container-a"),
329 []struct{ Key, Value []byte }{{Key: []byte("image"), Value: []byte("nginx:latest")}},
330 ); err == nil {
331 f.Add(seed[:builder.Finish()])
332 }
333 f.Add([]byte{})
334 f.Add(make([]byte, AppsLookupRespHdr-1))
335 f.Add(make([]byte, AppsLookupRespHdr))
336
337 f.Fuzz(func(t *testing.T, data []byte) {
338 view, err := DecodeAppsLookupResponse(data)
339 if err != nil {
340 return
341 }
342 for i := uint32(0); i < view.ItemCount; i++ {
343 item, ierr := view.Item(i)
344 if ierr != nil {
345 continue
346 }
347 _ = item.Comm.String()
348 _ = item.CgroupPath.Bytes()
349 _ = item.CgroupName.String()
350 for j := uint32(0); j < uint32(item.LabelCount); j++ {
351 label, lerr := item.Label(j)
352 if lerr == nil {
353 _ = label.Key.String()
354 _ = label.Value.String()
355 }
356 }
357 }
358 _, _ = view.Item(view.ItemCount)
359 })
360 }
361
362 func FuzzBatchDirDecode(f *testing.F) {
363 // Seed: valid 2-entry directory.
364 var seed [16]byte
365 ne.PutUint32(seed[0:4], 0) // offset=0, aligned
366 ne.PutUint32(seed[4:8], 10) // length=10
367 ne.PutUint32(seed[8:12], 16) // offset=16, aligned
368 ne.PutUint32(seed[12:16], 5) // length=5
369 f.Add(seed[:], uint32(2), uint32(100))
370
371 // Seed: empty.
372 f.Add([]byte{}, uint32(0), uint32(0))
373 f.Add(make([]byte, 8), uint32(1), uint32(50))
374 f.Add(make([]byte, 4), uint32(1), uint32(50)) // truncated
375
376 f.Fuzz(func(t *testing.T, data []byte, itemCount uint32, packedAreaLen uint32) {
377 // Clamp item_count to prevent huge allocations that slow the fuzzer.
378 if itemCount > 1024 {
379 return
380 }
381 entries, err := BatchDirDecode(data, itemCount, packedAreaLen)
382 if err != nil {
383 return
384 }
385 for _, e := range entries {
386 _ = e.Offset
387 _ = e.Length
388 }
389 })
390 }
391
392 func FuzzBatchItemGet(f *testing.F) {
393 // Seed: valid single-item batch payload built via BatchBuilder.
394 var batchBuf [256]byte
395 bb := NewBatchBuilder(batchBuf[:], 2)
396 bb.Add([]byte{1, 2, 3, 4, 5})
397 bb.Add([]byte{10, 20, 30})
398 total, count := bb.Finish()
399 f.Add(batchBuf[:total], count, uint32(0))
400 f.Add(batchBuf[:total], count, uint32(1))
401
402 f.Add([]byte{}, uint32(0), uint32(0))
403 f.Add(make([]byte, 8), uint32(1), uint32(0))
404
405 f.Fuzz(func(t *testing.T, payload []byte, itemCount uint32, index uint32) {
406 // Clamp to prevent huge allocations from itemCount.
407 if itemCount > 1024 {
408 return
409 }
410 item, err := BatchItemGet(payload, itemCount, index)
411 if err != nil {
412 return
413 }
414 _ = len(item)
415 if len(item) > 0 {
416 _ = item[0]
417 }
418 })
419 }