1 package ebpf
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "errors"
7 "fmt"
8 "math"
9 "path/filepath"
10 "runtime"
11 "strings"
12 "time"
13
14 "github.com/cilium/ebpf/asm"
15 "github.com/cilium/ebpf/btf"
16 "github.com/cilium/ebpf/internal"
17 "github.com/cilium/ebpf/internal/sys"
18 "github.com/cilium/ebpf/internal/unix"
19 )
20
21
22 var ErrNotSupported = internal.ErrNotSupported
23
24
25 type ProgramID uint32
26
27 const (
28
29
30
31 outputPad = 256 + 2
32 )
33
34
35
36 const DefaultVerifierLogSize = 64 * 1024
37
38
39 type ProgramOptions struct {
40
41
42 LogLevel uint32
43
44
45 LogSize int
46
47
48
49
50
51
52 KernelTypes *btf.Spec
53 }
54
55
56 type ProgramSpec struct {
57
58
59 Name string
60
61
62 Type ProgramType
63
64
65
66
67
68 AttachType AttachType
69
70
71
72 AttachTo string
73
74
75 AttachTarget *Program
76
77
78 SectionName string
79
80 Instructions asm.Instructions
81
82
83
84 Flags uint32
85
86
87
88
89
90 License string
91
92
93
94
95
96 KernelVersion uint32
97
98
99
100
101 BTF *btf.Spec
102
103
104 ByteOrder binary.ByteOrder
105 }
106
107
108 func (ps *ProgramSpec) Copy() *ProgramSpec {
109 if ps == nil {
110 return nil
111 }
112
113 cpy := *ps
114 cpy.Instructions = make(asm.Instructions, len(ps.Instructions))
115 copy(cpy.Instructions, ps.Instructions)
116 return &cpy
117 }
118
119
120
121
122 func (ps *ProgramSpec) Tag() (string, error) {
123 return ps.Instructions.Tag(internal.NativeEndian)
124 }
125
126 type VerifierError = internal.VerifierError
127
128
129
130
131 type Program struct {
132
133
134 VerifierLog string
135
136 fd *sys.FD
137 name string
138 pinnedPath string
139 typ ProgramType
140 }
141
142
143
144
145 func NewProgram(spec *ProgramSpec) (*Program, error) {
146 return NewProgramWithOptions(spec, ProgramOptions{})
147 }
148
149
150
151
152
153
154
155
156 func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {
157 if spec == nil {
158 return nil, errors.New("can't load a program from a nil spec")
159 }
160
161 handles := newHandleCache()
162 defer handles.close()
163
164 prog, err := newProgramWithOptions(spec, opts, handles)
165 if errors.Is(err, asm.ErrUnsatisfiedMapReference) {
166 return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err)
167 }
168 return prog, err
169 }
170
171 func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) {
172 if len(spec.Instructions) == 0 {
173 return nil, errors.New("instructions cannot be empty")
174 }
175
176 if spec.Type == UnspecifiedProgram {
177 return nil, errors.New("can't load program of unspecified type")
178 }
179
180 if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
181 return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
182 }
183
184
185
186
187
188 kv := spec.KernelVersion
189 if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) {
190 v, err := internal.KernelVersion()
191 if err != nil {
192 return nil, fmt.Errorf("detecting kernel version: %w", err)
193 }
194 kv = v.Kernel()
195 }
196
197 attr := &sys.ProgLoadAttr{
198 ProgType: sys.ProgType(spec.Type),
199 ProgFlags: spec.Flags,
200 ExpectedAttachType: sys.AttachType(spec.AttachType),
201 License: sys.NewStringPointer(spec.License),
202 KernVersion: kv,
203 }
204
205 if haveObjName() == nil {
206 attr.ProgName = sys.NewObjName(spec.Name)
207 }
208
209 kernelTypes := opts.KernelTypes
210
211 insns := make(asm.Instructions, len(spec.Instructions))
212 copy(insns, spec.Instructions)
213
214 var btfDisabled bool
215 if spec.BTF != nil {
216 if err := applyRelocations(insns, spec.BTF, kernelTypes); err != nil {
217 return nil, fmt.Errorf("apply CO-RE relocations: %w", err)
218 }
219
220 handle, err := handles.btfHandle(spec.BTF)
221 btfDisabled = errors.Is(err, btf.ErrNotSupported)
222 if err != nil && !btfDisabled {
223 return nil, fmt.Errorf("load BTF: %w", err)
224 }
225
226 if handle != nil {
227 attr.ProgBtfFd = uint32(handle.FD())
228
229 fib, lib, err := btf.MarshalExtInfos(insns, spec.BTF.TypeID)
230 if err != nil {
231 return nil, err
232 }
233
234 attr.FuncInfoRecSize = btf.FuncInfoSize
235 attr.FuncInfoCnt = uint32(len(fib)) / btf.FuncInfoSize
236 attr.FuncInfo = sys.NewSlicePointer(fib)
237
238 attr.LineInfoRecSize = btf.LineInfoSize
239 attr.LineInfoCnt = uint32(len(lib)) / btf.LineInfoSize
240 attr.LineInfo = sys.NewSlicePointer(lib)
241 }
242 }
243
244 if err := fixupAndValidate(insns); err != nil {
245 return nil, err
246 }
247
248 buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
249 err := insns.Marshal(buf, internal.NativeEndian)
250 if err != nil {
251 return nil, err
252 }
253
254 bytecode := buf.Bytes()
255 attr.Insns = sys.NewSlicePointer(bytecode)
256 attr.InsnCnt = uint32(len(bytecode) / asm.InstructionSize)
257
258 if spec.AttachTarget != nil {
259 targetID, err := findTargetInProgram(spec.AttachTarget, spec.AttachTo, spec.Type, spec.AttachType)
260 if err != nil {
261 return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err)
262 }
263
264 attr.AttachBtfId = uint32(targetID)
265 attr.AttachProgFd = uint32(spec.AttachTarget.FD())
266 defer runtime.KeepAlive(spec.AttachTarget)
267 } else if spec.AttachTo != "" {
268 targetID, err := findTargetInKernel(kernelTypes, spec.AttachTo, spec.Type, spec.AttachType)
269 if err != nil && !errors.Is(err, errUnrecognizedAttachType) {
270
271
272 return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err)
273 }
274
275 attr.AttachBtfId = uint32(targetID)
276 }
277
278 logSize := DefaultVerifierLogSize
279 if opts.LogSize > 0 {
280 logSize = opts.LogSize
281 }
282
283 var logBuf []byte
284 if opts.LogLevel > 0 {
285 logBuf = make([]byte, logSize)
286 attr.LogLevel = opts.LogLevel
287 attr.LogSize = uint32(len(logBuf))
288 attr.LogBuf = sys.NewSlicePointer(logBuf)
289 }
290
291 fd, err := sys.ProgLoad(attr)
292 if err == nil {
293 return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil
294 }
295
296 if opts.LogLevel == 0 && opts.LogSize >= 0 {
297
298 logBuf = make([]byte, logSize)
299 attr.LogLevel = 1
300 attr.LogSize = uint32(len(logBuf))
301 attr.LogBuf = sys.NewSlicePointer(logBuf)
302 _, _ = sys.ProgLoad(attr)
303 }
304
305 switch {
306 case errors.Is(err, unix.EPERM):
307 if len(logBuf) > 0 && logBuf[0] == 0 {
308
309
310 return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err)
311 }
312
313 fallthrough
314
315 case errors.Is(err, unix.EINVAL):
316 if hasFunctionReferences(spec.Instructions) {
317 if err := haveBPFToBPFCalls(); err != nil {
318 return nil, fmt.Errorf("load program: %w", err)
319 }
320 }
321 }
322
323 err = internal.ErrorWithLog(err, logBuf)
324 if btfDisabled {
325 return nil, fmt.Errorf("load program: %w (BTF disabled)", err)
326 }
327 return nil, fmt.Errorf("load program: %w", err)
328 }
329
330
331
332
333
334
335 func NewProgramFromFD(fd int) (*Program, error) {
336 f, err := sys.NewFD(fd)
337 if err != nil {
338 return nil, err
339 }
340
341 return newProgramFromFD(f)
342 }
343
344
345
346
347 func NewProgramFromID(id ProgramID) (*Program, error) {
348 fd, err := sys.ProgGetFdById(&sys.ProgGetFdByIdAttr{
349 Id: uint32(id),
350 })
351 if err != nil {
352 return nil, fmt.Errorf("get program by id: %w", err)
353 }
354
355 return newProgramFromFD(fd)
356 }
357
358 func newProgramFromFD(fd *sys.FD) (*Program, error) {
359 info, err := newProgramInfoFromFd(fd)
360 if err != nil {
361 fd.Close()
362 return nil, fmt.Errorf("discover program type: %w", err)
363 }
364
365 return &Program{"", fd, "", "", info.Type}, nil
366 }
367
368 func (p *Program) String() string {
369 if p.name != "" {
370 return fmt.Sprintf("%s(%s)#%v", p.typ, p.name, p.fd)
371 }
372 return fmt.Sprintf("%s(%v)", p.typ, p.fd)
373 }
374
375
376 func (p *Program) Type() ProgramType {
377 return p.typ
378 }
379
380
381
382
383 func (p *Program) Info() (*ProgramInfo, error) {
384 return newProgramInfoFromFd(p.fd)
385 }
386
387
388
389
390
391 func (p *Program) Handle() (*btf.Handle, error) {
392 info, err := p.Info()
393 if err != nil {
394 return nil, err
395 }
396
397 id, ok := info.BTFID()
398 if !ok {
399 return nil, fmt.Errorf("program %s: retrieve BTF ID: %w", p, ErrNotSupported)
400 }
401
402 return btf.NewHandleFromID(id)
403 }
404
405
406
407
408 func (p *Program) FD() int {
409 return p.fd.Int()
410 }
411
412
413
414
415
416
417 func (p *Program) Clone() (*Program, error) {
418 if p == nil {
419 return nil, nil
420 }
421
422 dup, err := p.fd.Dup()
423 if err != nil {
424 return nil, fmt.Errorf("can't clone program: %w", err)
425 }
426
427 return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil
428 }
429
430
431
432
433
434
435
436
437 func (p *Program) Pin(fileName string) error {
438 if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil {
439 return err
440 }
441 p.pinnedPath = fileName
442 return nil
443 }
444
445
446
447
448
449
450 func (p *Program) Unpin() error {
451 if err := internal.Unpin(p.pinnedPath); err != nil {
452 return err
453 }
454 p.pinnedPath = ""
455 return nil
456 }
457
458
459 func (p *Program) IsPinned() bool {
460 return p.pinnedPath != ""
461 }
462
463
464
465
466 func (p *Program) Close() error {
467 if p == nil {
468 return nil
469 }
470
471 return p.fd.Close()
472 }
473
474
475 type RunOptions struct {
476
477 Data []byte
478
479 DataOut []byte
480
481 Context interface{}
482
483 ContextOut interface{}
484
485 Repeat uint32
486
487 Flags uint32
488
489
490 CPU uint32
491
492
493 Reset func()
494 }
495
496
497
498
499
500
501
502
503 func (p *Program) Test(in []byte) (uint32, []byte, error) {
504
505
506
507
508
509 var out []byte
510 if len(in) > 0 {
511 out = make([]byte, len(in)+outputPad)
512 }
513
514 opts := RunOptions{
515 Data: in,
516 DataOut: out,
517 Repeat: 1,
518 }
519
520 ret, _, err := p.testRun(&opts)
521 if err != nil {
522 return ret, nil, fmt.Errorf("can't test program: %w", err)
523 }
524 return ret, opts.DataOut, nil
525 }
526
527
528
529
530 func (p *Program) Run(opts *RunOptions) (uint32, error) {
531 ret, _, err := p.testRun(opts)
532 if err != nil {
533 return ret, fmt.Errorf("can't test program: %w", err)
534 }
535 return ret, nil
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549 func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {
550 if uint(repeat) > math.MaxUint32 {
551 return 0, 0, fmt.Errorf("repeat is too high")
552 }
553
554 opts := RunOptions{
555 Data: in,
556 Repeat: uint32(repeat),
557 Reset: reset,
558 }
559
560 ret, total, err := p.testRun(&opts)
561 if err != nil {
562 return ret, total, fmt.Errorf("can't benchmark program: %w", err)
563 }
564 return ret, total, nil
565 }
566
567 var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() error {
568 prog, err := NewProgram(&ProgramSpec{
569
570 Type: SocketFilter,
571 Instructions: asm.Instructions{
572 asm.LoadImm(asm.R0, 0, asm.DWord),
573 asm.Return(),
574 },
575 License: "MIT",
576 })
577 if err != nil {
578
579 return err
580 }
581 defer prog.Close()
582
583
584 in := make([]byte, 14)
585 attr := sys.ProgRunAttr{
586 ProgFd: uint32(prog.FD()),
587 DataSizeIn: uint32(len(in)),
588 DataIn: sys.NewSlicePointer(in),
589 }
590
591 err = sys.ProgRun(&attr)
592 switch {
593 case errors.Is(err, unix.EINVAL):
594
595
596 return internal.ErrNotSupported
597
598 case errors.Is(err, unix.EINTR):
599
600 return nil
601
602 case errors.Is(err, unix.ENOTSUPP):
603
604
605
606 return nil
607 }
608
609 return err
610 })
611
612 func (p *Program) testRun(opts *RunOptions) (uint32, time.Duration, error) {
613 if uint(len(opts.Data)) > math.MaxUint32 {
614 return 0, 0, fmt.Errorf("input is too long")
615 }
616
617 if err := haveProgTestRun(); err != nil {
618 return 0, 0, err
619 }
620
621 var ctxBytes []byte
622 if opts.Context != nil {
623 ctx := new(bytes.Buffer)
624 if err := binary.Write(ctx, internal.NativeEndian, opts.Context); err != nil {
625 return 0, 0, fmt.Errorf("cannot serialize context: %v", err)
626 }
627 ctxBytes = ctx.Bytes()
628 }
629
630 var ctxOut []byte
631 if opts.ContextOut != nil {
632 ctxOut = make([]byte, binary.Size(opts.ContextOut))
633 }
634
635 attr := sys.ProgRunAttr{
636 ProgFd: p.fd.Uint(),
637 DataSizeIn: uint32(len(opts.Data)),
638 DataSizeOut: uint32(len(opts.DataOut)),
639 DataIn: sys.NewSlicePointer(opts.Data),
640 DataOut: sys.NewSlicePointer(opts.DataOut),
641 Repeat: uint32(opts.Repeat),
642 CtxSizeIn: uint32(len(ctxBytes)),
643 CtxSizeOut: uint32(len(ctxOut)),
644 CtxIn: sys.NewSlicePointer(ctxBytes),
645 CtxOut: sys.NewSlicePointer(ctxOut),
646 Flags: opts.Flags,
647 Cpu: opts.CPU,
648 }
649
650 for {
651 err := sys.ProgRun(&attr)
652 if err == nil {
653 break
654 }
655
656 if errors.Is(err, unix.EINTR) {
657 if opts.Reset != nil {
658 opts.Reset()
659 }
660 continue
661 }
662
663 if errors.Is(err, unix.ENOTSUPP) {
664 return 0, 0, fmt.Errorf("kernel doesn't support testing program type %s: %w", p.Type(), ErrNotSupported)
665 }
666
667 return 0, 0, fmt.Errorf("can't run test: %w", err)
668 }
669
670 if opts.DataOut != nil {
671 if int(attr.DataSizeOut) > cap(opts.DataOut) {
672
673
674 panic("kernel wrote past end of output buffer")
675 }
676 opts.DataOut = opts.DataOut[:int(attr.DataSizeOut)]
677 }
678
679 if len(ctxOut) != 0 {
680 b := bytes.NewReader(ctxOut)
681 if err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil {
682 return 0, 0, fmt.Errorf("failed to decode ContextOut: %v", err)
683 }
684 }
685
686 total := time.Duration(attr.Duration) * time.Nanosecond
687 return attr.Retval, total, nil
688 }
689
690 func unmarshalProgram(buf []byte) (*Program, error) {
691 if len(buf) != 4 {
692 return nil, errors.New("program id requires 4 byte value")
693 }
694
695
696
697 id := internal.NativeEndian.Uint32(buf)
698 return NewProgramFromID(ProgramID(id))
699 }
700
701 func marshalProgram(p *Program, length int) ([]byte, error) {
702 if length != 4 {
703 return nil, fmt.Errorf("can't marshal program to %d bytes", length)
704 }
705
706 buf := make([]byte, 4)
707 internal.NativeEndian.PutUint32(buf, p.fd.Uint())
708 return buf, nil
709 }
710
711
712
713
714 func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) {
715 fd, err := sys.ObjGet(&sys.ObjGetAttr{
716 Pathname: sys.NewStringPointer(fileName),
717 FileFlags: opts.Marshal(),
718 })
719 if err != nil {
720 return nil, err
721 }
722
723 info, err := newProgramInfoFromFd(fd)
724 if err != nil {
725 _ = fd.Close()
726 return nil, fmt.Errorf("info for %s: %w", fileName, err)
727 }
728
729 return &Program{"", fd, filepath.Base(fileName), fileName, info.Type}, nil
730 }
731
732
733
734
735
736
737
738
739 func SanitizeName(name string, replacement rune) string {
740 return strings.Map(func(char rune) rune {
741 if invalidBPFObjNameChar(char) {
742 return replacement
743 }
744 return char
745 }, name)
746 }
747
748
749
750
751 func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
752 attr := &sys.ProgGetNextIdAttr{Id: uint32(startID)}
753 return ProgramID(attr.NextId), sys.ProgGetNextId(attr)
754 }
755
756
757
758
759
760 func (p *Program) BindMap(m *Map) error {
761 attr := &sys.ProgBindMapAttr{
762 ProgFd: uint32(p.FD()),
763 MapFd: uint32(m.FD()),
764 }
765
766 return sys.ProgBindMap(attr)
767 }
768
769 var errUnrecognizedAttachType = errors.New("unrecognized attach type")
770
771
772
773
774
775
776
777 func findTargetInKernel(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) {
778 type match struct {
779 p ProgramType
780 a AttachType
781 }
782
783 var (
784 typeName, featureName string
785 isBTFTypeFunc = true
786 )
787
788 switch (match{progType, attachType}) {
789 case match{LSM, AttachLSMMac}:
790 typeName = "bpf_lsm_" + name
791 featureName = name + " LSM hook"
792 case match{Tracing, AttachTraceIter}:
793 typeName = "bpf_iter_" + name
794 featureName = name + " iterator"
795 case match{Tracing, AttachTraceFEntry}:
796 typeName = name
797 featureName = fmt.Sprintf("fentry %s", name)
798 case match{Tracing, AttachTraceFExit}:
799 typeName = name
800 featureName = fmt.Sprintf("fexit %s", name)
801 case match{Tracing, AttachModifyReturn}:
802 typeName = name
803 featureName = fmt.Sprintf("fmod_ret %s", name)
804 case match{Tracing, AttachTraceRawTp}:
805 typeName = fmt.Sprintf("btf_trace_%s", name)
806 featureName = fmt.Sprintf("raw_tp %s", name)
807 isBTFTypeFunc = false
808 default:
809 return 0, errUnrecognizedAttachType
810 }
811
812 spec, err := maybeLoadKernelBTF(spec)
813 if err != nil {
814 return 0, fmt.Errorf("load kernel spec: %w", err)
815 }
816
817 var target btf.Type
818 if isBTFTypeFunc {
819 var targetFunc *btf.Func
820 err = spec.TypeByName(typeName, &targetFunc)
821 target = targetFunc
822 } else {
823 var targetTypedef *btf.Typedef
824 err = spec.TypeByName(typeName, &targetTypedef)
825 target = targetTypedef
826 }
827
828 if err != nil {
829 if errors.Is(err, btf.ErrNotFound) {
830 return 0, &internal.UnsupportedFeatureError{
831 Name: featureName,
832 }
833 }
834 return 0, fmt.Errorf("find target for %s: %w", featureName, err)
835 }
836
837 return spec.TypeID(target)
838 }
839
840
841
842
843 func findTargetInProgram(prog *Program, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) {
844 type match struct {
845 p ProgramType
846 a AttachType
847 }
848
849 var typeName string
850 switch (match{progType, attachType}) {
851 case match{Extension, AttachNone}:
852 typeName = name
853 default:
854 return 0, errUnrecognizedAttachType
855 }
856
857 btfHandle, err := prog.Handle()
858 if err != nil {
859 return 0, fmt.Errorf("load target BTF: %w", err)
860 }
861 defer btfHandle.Close()
862
863 spec, err := btfHandle.Spec(nil)
864 if err != nil {
865 return 0, err
866 }
867
868 var targetFunc *btf.Func
869 err = spec.TypeByName(typeName, &targetFunc)
870 if err != nil {
871 return 0, fmt.Errorf("find target %s: %w", typeName, err)
872 }
873
874 return spec.TypeID(targetFunc)
875 }
876
View as plain text