master
go 593 lines 15 KB
Raw
1 package protocol
2
3 import "bytes"
4
5 const (
6 CgroupLookupKnown uint16 = 0
7 CgroupLookupUnknownRetryLater uint16 = 1
8 CgroupLookupUnknownPermanent uint16 = 2
9
10 CgroupsLookupReqHdr = 16
11 CgroupsLookupRespHdr = 16
12 CgroupsLookupItemHdr = 28
13
14 cgroupsLookupUnknownFixedBytes = CgroupsLookupItemHdr + 1
15 )
16
17 type CgroupsLookupRequestView struct {
18 ItemCount uint32
19 payload []byte
20 }
21
22 type CgroupsLookupResponseView struct {
23 LayoutVersion uint16
24 Flags uint16
25 ItemCount uint32
26 Generation uint64
27 payload []byte
28 }
29
30 type CgroupsLookupItemView struct {
31 Status uint16
32 Orchestrator uint16
33 Path CStringView
34 Name CStringView
35 LabelCount uint16
36 item []byte
37 labelTableOffset int
38 }
39
40 func validateCgroupsLookupSemantics(status, orchestrator uint16, pathLen, nameLen, labelCount int) error {
41 if status != CgroupLookupKnown && status != CgroupLookupUnknownRetryLater && status != CgroupLookupUnknownPermanent {
42 return ErrBadLayout
43 }
44 if pathLen == 0 {
45 return ErrBadLayout
46 }
47 if status != CgroupLookupKnown && (orchestrator != 0 || nameLen != 0 || labelCount != 0) {
48 return ErrBadLayout
49 }
50 return nil
51 }
52
53 func EncodeCgroupsLookupRequest(paths [][]byte, buf []byte) (int, error) {
54 count := len(paths)
55 if uint64(count) > uint64(^uint32(0)) {
56 return 0, ErrOverflow
57 }
58 dirSize, ok := checkedMulInt(count, LookupDirEntrySize)
59 if !ok {
60 return 0, ErrOverflow
61 }
62 packedStart, ok := checkedAddInt(CgroupsLookupReqHdr, dirSize)
63 if !ok {
64 return 0, ErrOverflow
65 }
66 if len(buf) < packedStart {
67 return 0, ErrOverflow
68 }
69 data := packedStart
70 for i, path := range paths {
71 if invalidSourceString(path, true) {
72 return 0, ErrBadLayout
73 }
74 aligned, ok := checkedAlign8(data)
75 if !ok {
76 return 0, ErrOverflow
77 }
78 keyLen, ok := checkedAddInt(len(path), 1)
79 if !ok {
80 return 0, ErrOverflow
81 }
82 end, ok := checkedAddInt(aligned, keyLen)
83 if !ok {
84 return 0, ErrOverflow
85 }
86 if end > len(buf) {
87 return 0, ErrOverflow
88 }
89 clear(buf[data:aligned])
90 offset32, ok := checkedU32Int(aligned - packedStart)
91 if !ok {
92 return 0, ErrOverflow
93 }
94 keyLen32, ok := checkedU32Int(keyLen)
95 if !ok {
96 return 0, ErrOverflow
97 }
98 base := CgroupsLookupReqHdr + i*LookupDirEntrySize
99 ne.PutUint32(buf[base:base+4], offset32)
100 ne.PutUint32(buf[base+4:base+8], keyLen32)
101 copy(buf[aligned:], path)
102 buf[aligned+len(path)] = 0
103 data = end
104 }
105 ne.PutUint16(buf[0:2], 1)
106 ne.PutUint16(buf[2:4], 0)
107 ne.PutUint32(buf[4:8], uint32(count))
108 ne.PutUint32(buf[8:12], 0)
109 ne.PutUint32(buf[12:16], 0)
110 return data, nil
111 }
112
113 func DecodeCgroupsLookupRequest(buf []byte) (*CgroupsLookupRequestView, error) {
114 if len(buf) < CgroupsLookupReqHdr {
115 return nil, ErrTruncated
116 }
117 if ne.Uint16(buf[0:2]) != 1 || ne.Uint16(buf[2:4]) != 0 ||
118 ne.Uint32(buf[8:12]) != 0 || ne.Uint32(buf[12:16]) != 0 {
119 return nil, ErrBadLayout
120 }
121 itemCount := ne.Uint32(buf[4:8])
122 dirSize64 := uint64(itemCount) * uint64(LookupDirEntrySize)
123 dirEnd, ok := checkedInt(uint64(CgroupsLookupReqHdr) + dirSize64)
124 if !ok {
125 return nil, ErrBadItemCount
126 }
127 if dirEnd > len(buf) {
128 return nil, ErrTruncated
129 }
130 if err := validateLookupDir(buf, CgroupsLookupReqHdr, itemCount, len(buf)-dirEnd, 2, -1); err != nil {
131 return nil, err
132 }
133 for i := range itemCount {
134 base := CgroupsLookupReqHdr + int(i)*LookupDirEntrySize
135 off, length, err := lookupDirEntry(buf, base)
136 if err != nil {
137 return nil, err
138 }
139 key, err := lookupPayloadSlice(buf, dirEnd, off, length)
140 if err != nil {
141 return nil, err
142 }
143 if key[length-1] != 0 {
144 return nil, ErrMissingNul
145 }
146 if bytes.Contains(key[:length-1], []byte{0}) {
147 return nil, ErrBadLayout
148 }
149 }
150 return &CgroupsLookupRequestView{ItemCount: itemCount, payload: buf}, nil
151 }
152
153 func (v *CgroupsLookupRequestView) Item(index uint32) (CStringView, error) {
154 if index >= v.ItemCount {
155 return CStringView{}, ErrOutOfBounds
156 }
157 dirEnd, ok := lookupBuilderDataOffset(CgroupsLookupReqHdr, v.ItemCount)
158 if !ok {
159 return CStringView{}, ErrOverflow
160 }
161 base, ok := lookupDirOffset(CgroupsLookupReqHdr, index)
162 if !ok {
163 return CStringView{}, ErrOverflow
164 }
165 off, length, err := lookupDirEntry(v.payload, base)
166 if err != nil {
167 return CStringView{}, err
168 }
169 item, err := lookupPayloadSlice(v.payload, dirEnd, off, length)
170 if err != nil {
171 return CStringView{}, err
172 }
173 stringLen, ok := checkedU32Int(length - 1)
174 if !ok {
175 return CStringView{}, ErrOutOfBounds
176 }
177 return NewCStringView(item, stringLen), nil
178 }
179
180 func DecodeCgroupsLookupResponse(buf []byte) (*CgroupsLookupResponseView, error) {
181 if len(buf) < CgroupsLookupRespHdr {
182 return nil, ErrTruncated
183 }
184 if ne.Uint16(buf[0:2]) != 1 || ne.Uint16(buf[2:4]) != 0 {
185 return nil, ErrBadLayout
186 }
187 itemCount := ne.Uint32(buf[4:8])
188 dirEnd, ok := checkedInt(uint64(CgroupsLookupRespHdr) + uint64(itemCount)*uint64(LookupDirEntrySize))
189 if !ok {
190 return nil, ErrBadItemCount
191 }
192 if dirEnd > len(buf) {
193 return nil, ErrTruncated
194 }
195 if err := validateLookupDir(buf, CgroupsLookupRespHdr, itemCount, len(buf)-dirEnd, CgroupsLookupItemHdr, -1); err != nil {
196 return nil, err
197 }
198 for i := range itemCount {
199 base := CgroupsLookupRespHdr + int(i)*LookupDirEntrySize
200 off, length, err := lookupDirEntry(buf, base)
201 if err != nil {
202 return nil, err
203 }
204 item, err := lookupPayloadSlice(buf, dirEnd, off, length)
205 if err != nil {
206 return nil, err
207 }
208 if _, err := decodeCgroupsLookupItem(item); err != nil {
209 return nil, err
210 }
211 }
212 return &CgroupsLookupResponseView{
213 LayoutVersion: 1,
214 Flags: 0,
215 ItemCount: itemCount,
216 Generation: ne.Uint64(buf[8:16]),
217 payload: buf,
218 }, nil
219 }
220
221 func (v *CgroupsLookupResponseView) Item(index uint32) (*CgroupsLookupItemView, error) {
222 if index >= v.ItemCount {
223 return nil, ErrOutOfBounds
224 }
225 dirEnd, ok := lookupBuilderDataOffset(CgroupsLookupRespHdr, v.ItemCount)
226 if !ok {
227 return nil, ErrOverflow
228 }
229 base, ok := lookupDirOffset(CgroupsLookupRespHdr, index)
230 if !ok {
231 return nil, ErrOverflow
232 }
233 off, length, err := lookupDirEntry(v.payload, base)
234 if err != nil {
235 return nil, err
236 }
237 item, err := lookupPayloadSlice(v.payload, dirEnd, off, length)
238 if err != nil {
239 return nil, err
240 }
241 return decodeCgroupsLookupItem(item)
242 }
243
244 func decodeCgroupsLookupItem(item []byte) (*CgroupsLookupItemView, error) {
245 if len(item) < CgroupsLookupItemHdr {
246 return nil, ErrTruncated
247 }
248 status := ne.Uint16(item[2:4])
249 orchestrator := ne.Uint16(item[4:6])
250 pathOff, err := checkedWireU32Int(item, 8)
251 if err != nil {
252 return nil, err
253 }
254 pathLen, err := checkedWireU32Int(item, 12)
255 if err != nil {
256 return nil, err
257 }
258 nameOff, err := checkedWireU32Int(item, 16)
259 if err != nil {
260 return nil, err
261 }
262 nameLen, err := checkedWireU32Int(item, 20)
263 if err != nil {
264 return nil, err
265 }
266 labelCount := ne.Uint16(item[24:26])
267 if ne.Uint16(item[0:2]) != 1 || ne.Uint16(item[6:8]) != 0 || ne.Uint16(item[26:28]) != 0 {
268 return nil, ErrBadLayout
269 }
270 if err := validateCgroupsLookupSemantics(status, orchestrator, pathLen, nameLen, int(labelCount)); err != nil {
271 return nil, err
272 }
273 if status != CgroupLookupKnown {
274 return decodeCgroupsLookupUnknownItem(item, status, pathOff, pathLen, nameOff)
275 }
276 path, pathEnd, err := lookupString(item, CgroupsLookupItemHdr, pathOff, pathLen)
277 if err != nil {
278 return nil, err
279 }
280 name, nameEnd, err := lookupString(item, CgroupsLookupItemHdr, nameOff, nameLen)
281 if err != nil {
282 return nil, err
283 }
284 if overlap(pathOff, pathEnd, nameOff, nameEnd) {
285 return nil, ErrBadLayout
286 }
287 table, err := validateLabels(item, CgroupsLookupItemHdr, labelCount, max(pathEnd, nameEnd))
288 if err != nil {
289 return nil, err
290 }
291 return &CgroupsLookupItemView{
292 Status: status,
293 Orchestrator: orchestrator,
294 Path: path,
295 Name: name,
296 LabelCount: labelCount,
297 item: item,
298 labelTableOffset: table,
299 }, nil
300 }
301
302 func decodeCgroupsLookupUnknownItem(item []byte, status uint16, pathOff, pathLen, nameOff int) (*CgroupsLookupItemView, error) {
303 path, pathEnd, err := lookupString(item, CgroupsLookupItemHdr, pathOff, pathLen)
304 if err != nil {
305 return nil, err
306 }
307 name, nameEnd, err := lookupEmptyString(item, CgroupsLookupItemHdr, nameOff)
308 if err != nil {
309 return nil, err
310 }
311 if overlap(pathOff, pathEnd, nameOff, nameEnd) {
312 return nil, ErrBadLayout
313 }
314 labelTableOffset := max(pathEnd, nameEnd)
315 if labelTableOffset != len(item) {
316 return nil, ErrBadLayout
317 }
318 return &CgroupsLookupItemView{
319 Status: status,
320 Path: path,
321 Name: name,
322 item: item,
323 labelTableOffset: labelTableOffset,
324 }, nil
325 }
326
327 func (v *CgroupsLookupItemView) Label(index uint32) (LookupLabelView, error) {
328 return lookupLabelAt(v.item, CgroupsLookupItemHdr, v.LabelCount, v.labelTableOffset, index)
329 }
330
331 type CgroupsLookupBuilder struct {
332 buf []byte
333 generation uint64
334 itemCount uint32
335 maxItems uint32
336 dataOffset int
337 err error
338 }
339
340 func NewCgroupsLookupBuilder(buf []byte, maxItems uint32, generation uint64) *CgroupsLookupBuilder {
341 minRequired, ok := lookupBuilderDataOffset(CgroupsLookupRespHdr, maxItems)
342 if !ok {
343 panic("CgroupsLookupBuilder buffer too small")
344 }
345 if len(buf) < minRequired {
346 panic("CgroupsLookupBuilder buffer too small")
347 }
348 return &CgroupsLookupBuilder{buf: buf, generation: generation, maxItems: maxItems, dataOffset: minRequired}
349 }
350
351 func (b *CgroupsLookupBuilder) SetGeneration(generation uint64) {
352 b.generation = generation
353 }
354
355 func (b *CgroupsLookupBuilder) Add(status, orchestrator uint16, path, name []byte, labels []struct{ Key, Value []byte }) error {
356 if b.itemCount >= b.maxItems {
357 b.err = ErrOverflow
358 return ErrOverflow
359 }
360 if err := validateCgroupsLookupSemantics(status, orchestrator, len(path), len(name), len(labels)); err != nil {
361 b.err = err
362 return err
363 }
364 if invalidSourceString(path, true) || invalidSourceString(name, false) {
365 b.err = ErrBadLayout
366 return ErrBadLayout
367 }
368 if status != CgroupLookupKnown {
369 return b.addUnknown(status, path)
370 }
371 labelCount, ok := checkedU16Int(len(labels))
372 if !ok {
373 b.err = ErrOverflow
374 return ErrOverflow
375 }
376 itemStart, ok := checkedAlign8(b.dataOffset)
377 if !ok {
378 b.err = ErrOverflow
379 return ErrOverflow
380 }
381 pathOff := CgroupsLookupItemHdr
382 nameOff, ok := checkedAddInt(pathOff, len(path))
383 if ok {
384 nameOff, ok = checkedAddInt(nameOff, 1)
385 }
386 if !ok {
387 b.err = ErrOverflow
388 return ErrOverflow
389 }
390 fixedEnd, ok := checkedAddInt(nameOff, len(name))
391 if ok {
392 fixedEnd, ok = checkedAddInt(fixedEnd, 1)
393 }
394 if !ok {
395 b.err = ErrOverflow
396 return ErrOverflow
397 }
398 tableStart, tableBytes, itemSize, err := labelLayoutGo(fixedEnd, labels)
399 if err != nil {
400 b.err = err
401 return err
402 }
403 itemEnd, ok := checkedAddInt(itemStart, itemSize)
404 if !ok {
405 b.err = ErrOverflow
406 return ErrOverflow
407 }
408 if itemEnd > len(b.buf) {
409 b.err = ErrOverflow
410 return ErrOverflow
411 }
412 pathOff32, ok := checkedU32Int(pathOff)
413 if !ok {
414 b.err = ErrOverflow
415 return ErrOverflow
416 }
417 pathLen32, ok := checkedU32Int(len(path))
418 if !ok {
419 b.err = ErrOverflow
420 return ErrOverflow
421 }
422 nameOff32, ok := checkedU32Int(nameOff)
423 if !ok {
424 b.err = ErrOverflow
425 return ErrOverflow
426 }
427 nameLen32, ok := checkedU32Int(len(name))
428 if !ok {
429 b.err = ErrOverflow
430 return ErrOverflow
431 }
432 itemStart32, ok := checkedU32Int(itemStart)
433 if !ok {
434 b.err = ErrOverflow
435 return ErrOverflow
436 }
437 itemSize32, ok := checkedU32Int(itemSize)
438 if !ok {
439 b.err = ErrOverflow
440 return ErrOverflow
441 }
442 clear(b.buf[b.dataOffset:itemStart])
443 item := b.buf[itemStart:itemEnd]
444 ne.PutUint16(item[0:2], 1)
445 ne.PutUint16(item[2:4], status)
446 ne.PutUint16(item[4:6], orchestrator)
447 ne.PutUint16(item[6:8], 0)
448 ne.PutUint32(item[8:12], pathOff32)
449 ne.PutUint32(item[12:16], pathLen32)
450 ne.PutUint32(item[16:20], nameOff32)
451 ne.PutUint32(item[20:24], nameLen32)
452 ne.PutUint16(item[24:26], labelCount)
453 ne.PutUint16(item[26:28], 0)
454 copy(item[pathOff:], path)
455 item[pathOff+len(path)] = 0
456 copy(item[nameOff:], name)
457 item[nameOff+len(name)] = 0
458 if len(labels) > 0 {
459 clear(item[fixedEnd:tableStart])
460 next, err := writeLookupLabels(item, tableStart, tableBytes, labels)
461 if err != nil {
462 b.err = err
463 return err
464 }
465 itemSize = next
466 }
467 dir, ok := lookupDirOffset(CgroupsLookupRespHdr, b.itemCount)
468 if !ok {
469 b.err = ErrOverflow
470 return ErrOverflow
471 }
472 ne.PutUint32(b.buf[dir:dir+4], itemStart32)
473 ne.PutUint32(b.buf[dir+4:dir+8], itemSize32)
474 b.dataOffset = itemStart + itemSize
475 b.itemCount++
476 return nil
477 }
478
479 func (b *CgroupsLookupBuilder) addUnknown(status uint16, path []byte) error {
480 itemStart, ok := checkedAlign8(b.dataOffset)
481 if !ok {
482 b.err = ErrOverflow
483 return ErrOverflow
484 }
485 nameOff, ok := checkedAddInt(CgroupsLookupItemHdr, len(path))
486 if ok {
487 nameOff, ok = checkedAddInt(nameOff, 1)
488 }
489 if !ok {
490 b.err = ErrOverflow
491 return ErrOverflow
492 }
493 itemSize, ok := checkedAddInt(nameOff, 1)
494 if !ok {
495 b.err = ErrOverflow
496 return ErrOverflow
497 }
498 itemEnd, ok := checkedAddInt(itemStart, itemSize)
499 if !ok {
500 b.err = ErrOverflow
501 return ErrOverflow
502 }
503 if itemEnd > len(b.buf) {
504 b.err = ErrOverflow
505 return ErrOverflow
506 }
507 pathLen32, ok := checkedU32Int(len(path))
508 if !ok {
509 b.err = ErrOverflow
510 return ErrOverflow
511 }
512 nameOff32, ok := checkedU32Int(nameOff)
513 if !ok {
514 b.err = ErrOverflow
515 return ErrOverflow
516 }
517 itemStart32, ok := checkedU32Int(itemStart)
518 if !ok {
519 b.err = ErrOverflow
520 return ErrOverflow
521 }
522 itemSize32, ok := checkedU32Int(itemSize)
523 if !ok {
524 b.err = ErrOverflow
525 return ErrOverflow
526 }
527 clear(b.buf[b.dataOffset:itemStart])
528 item := b.buf[itemStart:itemEnd]
529 ne.PutUint16(item[0:2], 1)
530 ne.PutUint16(item[2:4], status)
531 ne.PutUint16(item[4:6], 0)
532 ne.PutUint16(item[6:8], 0)
533 ne.PutUint32(item[8:12], CgroupsLookupItemHdr)
534 ne.PutUint32(item[12:16], pathLen32)
535 ne.PutUint32(item[16:20], nameOff32)
536 ne.PutUint32(item[20:24], 0)
537 ne.PutUint16(item[24:26], 0)
538 ne.PutUint16(item[26:28], 0)
539 copy(item[CgroupsLookupItemHdr:], path)
540 item[CgroupsLookupItemHdr+len(path)] = 0
541 item[nameOff] = 0
542 dir, ok := lookupDirOffset(CgroupsLookupRespHdr, b.itemCount)
543 if !ok {
544 b.err = ErrOverflow
545 return ErrOverflow
546 }
547 ne.PutUint32(b.buf[dir:dir+4], itemStart32)
548 ne.PutUint32(b.buf[dir+4:dir+8], itemSize32)
549 b.dataOffset = itemStart + itemSize
550 b.itemCount++
551 return nil
552 }
553
554 func (b *CgroupsLookupBuilder) Finish() int {
555 return finishLookupResponse(b.buf, CgroupsLookupRespHdr, b.itemCount, b.dataOffset, b.generation)
556 }
557
558 func (b *CgroupsLookupBuilder) Error() error {
559 return b.err
560 }
561
562 func (b *CgroupsLookupBuilder) ItemCount() uint32 {
563 return b.itemCount
564 }
565
566 func DispatchCgroupsLookup(req []byte, resp []byte, handler func(*CgroupsLookupRequestView, *CgroupsLookupBuilder) bool) (int, error) {
567 request, err := DecodeCgroupsLookupRequest(req)
568 if err != nil {
569 return 0, err
570 }
571 minRequired, ok := lookupBuilderDataOffset(CgroupsLookupRespHdr, request.ItemCount)
572 if !ok || len(resp) < minRequired {
573 return 0, ErrOverflow
574 }
575 builder := NewCgroupsLookupBuilder(resp, request.ItemCount, 0)
576 if !handler(request, builder) {
577 if builder.Error() != nil {
578 return 0, builder.Error()
579 }
580 return 0, ErrBadLayout
581 }
582 if builder.Error() != nil {
583 return 0, builder.Error()
584 }
585 if builder.itemCount != request.ItemCount {
586 return 0, ErrBadItemCount
587 }
588 n := builder.Finish()
589 if n == 0 {
590 return 0, ErrOverflow
591 }
592 return n, nil
593 }