From 09ae10be57b3b0c6b15089413ab762cc162273a6 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 16 Jun 2020 16:48:12 -0700 Subject: [PATCH 01/16] tests/int/checkpoint: whitespace cleanups Remove whitespace at EOL Signed-off-by: Kir Kolyshkin --- tests/integration/checkpoint.bats | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/checkpoint.bats b/tests/integration/checkpoint.bats index 3e5cdf10224..0c4241af64a 100644 --- a/tests/integration/checkpoint.bats +++ b/tests/integration/checkpoint.bats @@ -17,7 +17,7 @@ function teardown() { function setup_pipes() { # The changes to 'terminal' are needed for running in detached mode update_config ' (.. | select(.terminal? != null)) .terminal |= false - | (.. | select(.[]? == "sh")) += ["-c", "for i in `seq 10`; do read xxx || continue; echo ponG $xxx; done"]' + | (.. | select(.[]? == "sh")) += ["-c", "for i in `seq 10`; do read xxx || continue; echo ponG $xxx; done"]' # Create two sets of pipes # for stdout/stderr @@ -76,7 +76,7 @@ function simple_cr() { requires cgroups_v1 # enable CGROUPNS - update_config '.linux.namespaces += [{"type": "cgroup"}]' + update_config '.linux.namespaces += [{"type": "cgroup"}]' simple_cr } @@ -134,7 +134,7 @@ function simple_cr() { setup_pipes # This should not be necessary: https://github.com/checkpoint-restore/criu/issues/575 - update_config '(.. | select(.readonly? != null)) .readonly |= false' + update_config '(.. | select(.readonly? != null)) .readonly |= false' # TCP port for lazy migration port=27277 @@ -268,7 +268,7 @@ function simple_cr() { tmplog2=`basename $tmplog2` # This adds the annotation 'org.criu.config' to set a container # specific CRIU config file. - update_config '.annotations += {"org.criu.config": "'"$tmp"'"}' + update_config '.annotations += {"org.criu.config": "'"$tmp"'"}' # Tell CRIU to use another configuration file mkdir -p /etc/criu From 27475c414b9ab1f8e7f83fff06b2e092daabb643 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 16 Jun 2020 16:56:00 -0700 Subject: [PATCH 02/16] tests/int/checkpoint: fix checks, add logs 1. When using `runc`, we should check `$status` and not `$?`. 2. Before exit code check, let's (try to) show errors from CRIU log. Signed-off-by: Kir Kolyshkin --- tests/integration/checkpoint.bats | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/integration/checkpoint.bats b/tests/integration/checkpoint.bats index 0c4241af64a..00947427cd2 100644 --- a/tests/integration/checkpoint.bats +++ b/tests/integration/checkpoint.bats @@ -58,9 +58,8 @@ function simple_cr() { # restore from checkpoint runc --criu "$CRIU" restore -d --work-path ./work-dir --console-socket $CONSOLE_SOCKET test_busybox - ret=$? cat ./work-dir/restore.log | grep -B 5 Error || true - [ "$ret" -eq 0 ] + [ "$status" -eq 0 ] # busybox should be back up and running testcontainer test_busybox running @@ -181,7 +180,9 @@ function simple_cr() { # Killing the CRIU on the checkpoint side will let the container # continue to run if the migration failed at some point. __runc --criu "$CRIU" restore -d --work-path ./image-dir --image-path ./image-dir --lazy-pages test_busybox_restore <&60 >&51 2>&51 - [ $? -eq 0 ] + ret=$? + cat ./work-dir/restore.log | grep -B 5 Error || true + [ $ret -eq 0 ] # busybox should be back up and running testcontainer test_busybox_restore running @@ -283,6 +284,7 @@ function simple_cr() { # checkpoint the running container runc --criu "$CRIU" checkpoint --work-path ./work-dir test_busybox + cat ./work-dir/dump.log | grep -B 5 Error || true [ "$status" -eq 0 ] ! test -f ./work-dir/$tmplog1 test -f ./work-dir/$tmplog2 @@ -293,6 +295,7 @@ function simple_cr() { test -f ./work-dir/$tmplog2 && unlink ./work-dir/$tmplog2 # restore from checkpoint runc --criu "$CRIU" restore -d --work-path ./work-dir --console-socket $CONSOLE_SOCKET test_busybox + cat ./work-dir/restore.log | grep -B 5 Error || true [ "$status" -eq 0 ] ! test -f ./work-dir/$tmplog1 test -f ./work-dir/$tmplog2 From d88bdd34d5bcc9bfea31d7f43b9de5a3c9fdbb70 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Tue, 30 Jun 2020 08:24:30 -0700 Subject: [PATCH 03/16] VERSION: release 1.0.0-rc91 Signed-off-by: Mrunal Patel --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index aeb3a87c243..3d24f977db2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-rc10+dev +1.0.0-rc91 From 3097e53b11851de4d1e78b31919df98e6539918d Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Tue, 30 Jun 2020 08:25:05 -0700 Subject: [PATCH 04/16] VERSION: back to development Signed-off-by: Mrunal Patel --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3d24f977db2..3bde53ab14b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-rc91 +1.0.0-rc91+dev From 5a05fe7d85f8a3394827264c4162469ed32a8dc4 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 2 Jul 2020 14:44:55 +0200 Subject: [PATCH 05/16] vendor: update cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 full diff: https://github.com/cilium/ebpf/compare/a9f01edf17e3...1c8d4c9ef775 drops support for go1.12, and removes dependency on the golang.org/x/xerrors transitional package. Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 12 +- vendor/github.com/cilium/ebpf/abi.go | 15 +- .../github.com/cilium/ebpf/asm/instruction.go | 77 ++-- vendor/github.com/cilium/ebpf/asm/opcode.go | 2 +- vendor/github.com/cilium/ebpf/collection.go | 29 +- vendor/github.com/cilium/ebpf/elf_reader.go | 375 ++++++++++-------- vendor/github.com/cilium/ebpf/go.mod | 7 +- vendor/github.com/cilium/ebpf/go.sum | 4 - .../cilium/ebpf/internal/btf/btf.go | 261 ++++++++---- .../cilium/ebpf/internal/btf/btf_types.go | 45 ++- .../cilium/ebpf/internal/btf/ext_info.go | 44 +- .../cilium/ebpf/internal/btf/strings.go | 20 +- .../cilium/ebpf/internal/btf/types.go | 37 +- .../github.com/cilium/ebpf/internal/errors.go | 4 +- vendor/github.com/cilium/ebpf/internal/fd.go | 14 +- .../cilium/ebpf/internal/feature.go | 67 +++- vendor/github.com/cilium/ebpf/internal/io.go | 4 +- .../cilium/ebpf/internal/syscall.go | 118 +++++- .../cilium/ebpf/internal/syscall_string.go | 56 +++ .../cilium/ebpf/internal/unix/types_linux.go | 2 + .../cilium/ebpf/internal/unix/types_other.go | 2 + vendor/github.com/cilium/ebpf/linker.go | 8 +- vendor/github.com/cilium/ebpf/map.go | 108 ++--- vendor/github.com/cilium/ebpf/marshalers.go | 28 +- vendor/github.com/cilium/ebpf/prog.go | 174 +++++--- vendor/github.com/cilium/ebpf/run-tests.sh | 32 +- vendor/github.com/cilium/ebpf/syscalls.go | 138 +++---- vendor/github.com/cilium/ebpf/types.go | 34 +- vendor/github.com/cilium/ebpf/types_string.go | 48 ++- vendor/golang.org/x/xerrors/LICENSE | 27 -- vendor/golang.org/x/xerrors/PATENTS | 22 - vendor/golang.org/x/xerrors/README | 2 - vendor/golang.org/x/xerrors/adaptor.go | 193 --------- vendor/golang.org/x/xerrors/codereview.cfg | 1 - vendor/golang.org/x/xerrors/doc.go | 22 - vendor/golang.org/x/xerrors/errors.go | 33 -- vendor/golang.org/x/xerrors/fmt.go | 187 --------- vendor/golang.org/x/xerrors/format.go | 34 -- vendor/golang.org/x/xerrors/frame.go | 56 --- vendor/golang.org/x/xerrors/go.mod | 3 - .../golang.org/x/xerrors/internal/internal.go | 8 - vendor/golang.org/x/xerrors/wrap.go | 106 ----- vendor/modules.txt | 5 +- 44 files changed, 1095 insertions(+), 1371 deletions(-) create mode 100644 vendor/github.com/cilium/ebpf/internal/syscall_string.go delete mode 100644 vendor/golang.org/x/xerrors/LICENSE delete mode 100644 vendor/golang.org/x/xerrors/PATENTS delete mode 100644 vendor/golang.org/x/xerrors/README delete mode 100644 vendor/golang.org/x/xerrors/adaptor.go delete mode 100644 vendor/golang.org/x/xerrors/codereview.cfg delete mode 100644 vendor/golang.org/x/xerrors/doc.go delete mode 100644 vendor/golang.org/x/xerrors/errors.go delete mode 100644 vendor/golang.org/x/xerrors/fmt.go delete mode 100644 vendor/golang.org/x/xerrors/format.go delete mode 100644 vendor/golang.org/x/xerrors/frame.go delete mode 100644 vendor/golang.org/x/xerrors/go.mod delete mode 100644 vendor/golang.org/x/xerrors/internal/internal.go delete mode 100644 vendor/golang.org/x/xerrors/wrap.go diff --git a/go.mod b/go.mod index 3c6f6d47e11..44b8777c143 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/checkpoint-restore/go-criu/v4 v4.0.2 - github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 + github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 github.com/containerd/console v1.0.0 github.com/coreos/go-systemd/v22 v22.0.0 github.com/cyphar/filepath-securejoin v0.2.2 diff --git a/go.sum b/go.sum index ca3e491fd02..383495df858 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/checkpoint-restore/go-criu/v4 v4.0.2 h1:jt+rnBIhFtPw0fhtpYGcUOilh4aO9Hj7r+YLEtf30uA= github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f h1:W1RQPz3nR8RxUw/Uqk71GU3JlZ7pNa1pXrHs98h0o9U= -github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= -github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 h1:qcqzLJa2xCo9sgdCzpT/SJSYxROTEstuhf7ZBHMirms= -github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 h1:cHzBGGVew0ezFsq2grfy2RsB8hO/eNyBgOLHBCqfR1U= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ= github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= @@ -27,12 +25,8 @@ github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1Ws github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6SlFjaXFubv0xX0HsuC9qJsdqm7bNQpYM= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 h1:9mv9SC7GWmRWE0J/+oD8w3GsN2KYGKtg6uwLN7hfP5E= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo= -github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y= github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -65,7 +59,5 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/cilium/ebpf/abi.go b/vendor/github.com/cilium/ebpf/abi.go index fd6139c5973..d432eb46f42 100644 --- a/vendor/github.com/cilium/ebpf/abi.go +++ b/vendor/github.com/cilium/ebpf/abi.go @@ -3,14 +3,13 @@ package ebpf import ( "bufio" "bytes" + "errors" "fmt" "io" "os" "syscall" "github.com/cilium/ebpf/internal" - - "golang.org/x/xerrors" ) // MapABI are the attributes of a Map which are available across all supported kernels. @@ -35,7 +34,7 @@ func newMapABIFromSpec(spec *MapSpec) *MapABI { func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) { info, err := bpfGetMapInfoByFD(fd) if err != nil { - if xerrors.Is(err, syscall.EINVAL) { + if errors.Is(err, syscall.EINVAL) { abi, err := newMapABIFromProc(fd) return "", abi, err } @@ -98,7 +97,7 @@ func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI { func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) { info, err := bpfGetProgInfoByFD(fd) if err != nil { - if xerrors.Is(err, syscall.EINVAL) { + if errors.Is(err, syscall.EINVAL) { return newProgramABIFromProc(fd) } @@ -127,7 +126,7 @@ func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) { "prog_type": &abi.Type, "prog_tag": &name, }) - if xerrors.Is(err, errMissingFields) { + if errors.Is(err, errMissingFields) { return "", nil, &internal.UnsupportedFeatureError{ Name: "reading ABI from /proc/self/fdinfo", MinimumVersion: internal.Version{4, 11, 0}, @@ -153,12 +152,12 @@ func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { defer fh.Close() if err := scanFdInfoReader(fh, fields); err != nil { - return xerrors.Errorf("%s: %w", fh.Name(), err) + return fmt.Errorf("%s: %w", fh.Name(), err) } return nil } -var errMissingFields = xerrors.New("missing fields") +var errMissingFields = errors.New("missing fields") func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { var ( @@ -179,7 +178,7 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { } if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 { - return xerrors.Errorf("can't parse field %s: %v", name, err) + return fmt.Errorf("can't parse field %s: %v", name, err) } scanned++ diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go index 8058c157694..8fbcf56647c 100644 --- a/vendor/github.com/cilium/ebpf/asm/instruction.go +++ b/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -2,13 +2,11 @@ package asm import ( "encoding/binary" + "errors" "fmt" - "github.com/cilium/ebpf/internal" "io" "math" "strings" - - "golang.org/x/xerrors" ) // InstructionSize is the size of a BPF instruction in bytes @@ -40,10 +38,12 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err } ins.OpCode = bi.OpCode - ins.Dst = bi.Registers.Dst() - ins.Src = bi.Registers.Src() ins.Offset = bi.Offset ins.Constant = int64(bi.Constant) + ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo) + if err != nil { + return 0, fmt.Errorf("can't unmarshal registers: %s", err) + } if !bi.OpCode.isDWordLoad() { return InstructionSize, nil @@ -52,10 +52,10 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err var bi2 bpfInstruction if err := binary.Read(r, bo, &bi2); err != nil { // No Wrap, to avoid io.EOF clash - return 0, xerrors.New("64bit immediate is missing second half") + return 0, errors.New("64bit immediate is missing second half") } if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 { - return 0, xerrors.New("64bit immediate has non-zero fields") + return 0, errors.New("64bit immediate has non-zero fields") } ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant))) @@ -65,7 +65,7 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err // Marshal encodes a BPF instruction. func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { if ins.OpCode == InvalidOpCode { - return 0, xerrors.New("invalid opcode") + return 0, errors.New("invalid opcode") } isDWordLoad := ins.OpCode.isDWordLoad() @@ -76,9 +76,14 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) cons = int32(uint32(ins.Constant)) } + regs, err := newBPFRegisters(ins.Dst, ins.Src, bo) + if err != nil { + return 0, fmt.Errorf("can't marshal registers: %s", err) + } + bpfi := bpfInstruction{ ins.OpCode, - newBPFRegisters(ins.Dst, ins.Src), + regs, ins.Offset, cons, } @@ -107,11 +112,11 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) // Returns an error if the instruction doesn't load a map. func (ins *Instruction) RewriteMapPtr(fd int) error { if !ins.OpCode.isDWordLoad() { - return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) + return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) } if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue { - return xerrors.New("not a load from a map") + return errors.New("not a load from a map") } // Preserve the offset value for direct map loads. @@ -130,11 +135,11 @@ func (ins *Instruction) mapPtr() uint32 { // Returns an error if the instruction is not a direct load. func (ins *Instruction) RewriteMapOffset(offset uint32) error { if !ins.OpCode.isDWordLoad() { - return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) + return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) } if ins.Src != PseudoMapValue { - return xerrors.New("not a direct load from a map") + return errors.New("not a direct load from a map") } fd := uint64(ins.Constant) & math.MaxUint32 @@ -245,7 +250,7 @@ func (insns Instructions) String() string { // Returns an error if the symbol isn't used, see IsUnreferencedSymbol. func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { if symbol == "" { - return xerrors.New("empty symbol") + return errors.New("empty symbol") } found := false @@ -280,7 +285,7 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) { } if _, ok := offsets[ins.Symbol]; ok { - return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) + return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol) } offsets[ins.Symbol] = i @@ -318,7 +323,7 @@ func (insns Instructions) marshalledOffsets() (map[string]int, error) { } if _, ok := symbols[ins.Symbol]; ok { - return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) + return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol) } symbols[ins.Symbol] = currentPos @@ -399,7 +404,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { // Rewrite bpf to bpf call offset, ok := absoluteOffsets[ins.Reference] if !ok { - return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) + return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) } ins.Constant = int64(offset - num - 1) @@ -408,7 +413,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { // Rewrite jump to label offset, ok := absoluteOffsets[ins.Reference] if !ok { - return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) + return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) } ins.Offset = int16(offset - num - 1) @@ -416,7 +421,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { n, err := ins.Marshal(w, bo) if err != nil { - return xerrors.Errorf("instruction %d: %w", i, err) + return fmt.Errorf("instruction %d: %w", i, err) } num += int(n / InstructionSize) @@ -433,27 +438,25 @@ type bpfInstruction struct { type bpfRegisters uint8 -func newBPFRegisters(dst, src Register) bpfRegisters { - if internal.NativeEndian == binary.LittleEndian { - return bpfRegisters((src << 4) | (dst & 0xF)) - } else { - return bpfRegisters((dst << 4) | (src & 0xF)) - } -} - -func (r bpfRegisters) Dst() Register { - if internal.NativeEndian == binary.LittleEndian { - return Register(r & 0xF) - }else { - return Register(r >> 4) +func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) { + switch bo { + case binary.LittleEndian: + return bpfRegisters((src << 4) | (dst & 0xF)), nil + case binary.BigEndian: + return bpfRegisters((dst << 4) | (src & 0xF)), nil + default: + return 0, fmt.Errorf("unrecognized ByteOrder %T", bo) } } -func (r bpfRegisters) Src() Register { - if internal.NativeEndian == binary.LittleEndian { - return Register(r >> 4) - } else { - return Register(r & 0xf) +func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) { + switch bo { + case binary.LittleEndian: + return Register(r & 0xF), Register(r >> 4), nil + case binary.BigEndian: + return Register(r >> 4), Register(r & 0xf), nil + default: + return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo) } } diff --git a/vendor/github.com/cilium/ebpf/asm/opcode.go b/vendor/github.com/cilium/ebpf/asm/opcode.go index d796de3fe0c..c99b6595ac5 100644 --- a/vendor/github.com/cilium/ebpf/asm/opcode.go +++ b/vendor/github.com/cilium/ebpf/asm/opcode.go @@ -225,7 +225,7 @@ func (op OpCode) String() string { } default: - fmt.Fprintf(&f, "%#x", op) + fmt.Fprintf(&f, "OpCode(%#x)", uint8(op)) } return f.String() diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 5b3ed58f166..0c8b65d94de 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -1,12 +1,13 @@ package ebpf import ( + "errors" + "fmt" "math" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" - "golang.org/x/xerrors" ) // CollectionOptions control loading a collection into the kernel. @@ -64,12 +65,12 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { // Not all programs need to use the map default: - return xerrors.Errorf("program %s: %w", progName, err) + return fmt.Errorf("program %s: %w", progName, err) } } if !seen { - return xerrors.Errorf("map %s not referenced by any programs", symbol) + return fmt.Errorf("map %s not referenced by any programs", symbol) } // Prevent NewCollection from creating rewritten maps @@ -96,21 +97,21 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { rodata := cs.Maps[".rodata"] if rodata == nil { - return xerrors.New("missing .rodata section") + return errors.New("missing .rodata section") } if rodata.BTF == nil { - return xerrors.New(".rodata section has no BTF") + return errors.New(".rodata section has no BTF") } if n := len(rodata.Contents); n != 1 { - return xerrors.Errorf("expected one key in .rodata, found %d", n) + return fmt.Errorf("expected one key in .rodata, found %d", n) } kv := rodata.Contents[0] value, ok := kv.Value.([]byte) if !ok { - return xerrors.Errorf("first value in .rodata is %T not []byte", kv.Value) + return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value) } buf := make([]byte, len(value)) @@ -185,14 +186,14 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (col var handle *btf.Handle if mapSpec.BTF != nil { handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) - if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + if err != nil && !errors.Is(err, btf.ErrNotSupported) { return nil, err } } m, err := newMapWithBTF(mapSpec, handle) if err != nil { - return nil, xerrors.Errorf("map %s: %w", mapName, err) + return nil, fmt.Errorf("map %s: %w", mapName, err) } maps[mapName] = m } @@ -216,29 +217,29 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (col m := maps[ins.Reference] if m == nil { - return nil, xerrors.Errorf("program %s: missing map %s", progName, ins.Reference) + return nil, fmt.Errorf("program %s: missing map %s", progName, ins.Reference) } fd := m.FD() if fd < 0 { - return nil, xerrors.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) + return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) } if err := ins.RewriteMapPtr(m.FD()); err != nil { - return nil, xerrors.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) + return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) } } var handle *btf.Handle if progSpec.BTF != nil { handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) - if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + if err != nil && !errors.Is(err, btf.ErrNotSupported) { return nil, err } } prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) if err != nil { - return nil, xerrors.Errorf("program %s: %w", progName, err) + return nil, fmt.Errorf("program %s: %w", progName, err) } progs[progName] = prog } diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index 344615eaf9e..77acaed8d96 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -4,6 +4,8 @@ import ( "bytes" "debug/elf" "encoding/binary" + "errors" + "fmt" "io" "math" "os" @@ -13,8 +15,6 @@ import ( "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) type elfCode struct { @@ -35,7 +35,7 @@ func LoadCollectionSpec(file string) (*CollectionSpec, error) { spec, err := LoadCollectionSpecFromReader(f) if err != nil { - return nil, xerrors.Errorf("file %s: %w", file, err) + return nil, fmt.Errorf("file %s: %w", file, err) } return spec, nil } @@ -50,7 +50,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { symbols, err := f.Symbols() if err != nil { - return nil, xerrors.Errorf("load symbols: %v", err) + return nil, fmt.Errorf("load symbols: %v", err) } ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0} @@ -79,13 +79,13 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { dataSections[elf.SectionIndex(i)] = sec case sec.Type == elf.SHT_REL: if int(sec.Info) >= len(ec.Sections) { - return nil, xerrors.Errorf("found relocation section %v for missing section %v", i, sec.Info) + return nil, fmt.Errorf("found relocation section %v for missing section %v", i, sec.Info) } // Store relocations under the section index of the target idx := elf.SectionIndex(sec.Info) if relSections[idx] != nil { - return nil, xerrors.Errorf("section %d has multiple relocation sections", sec.Info) + return nil, fmt.Errorf("section %d has multiple relocation sections", sec.Info) } relSections[idx] = sec case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: @@ -95,44 +95,52 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { ec.license, err = loadLicense(licenseSection) if err != nil { - return nil, xerrors.Errorf("load license: %w", err) + return nil, fmt.Errorf("load license: %w", err) } ec.version, err = loadVersion(versionSection, ec.ByteOrder) if err != nil { - return nil, xerrors.Errorf("load version: %w", err) + return nil, fmt.Errorf("load version: %w", err) } btfSpec, err := btf.LoadSpecFromReader(rd) if err != nil { - return nil, xerrors.Errorf("load BTF: %w", err) + return nil, fmt.Errorf("load BTF: %w", err) + } + + relocations, referencedSections, err := ec.loadRelocations(relSections) + if err != nil { + return nil, fmt.Errorf("load relocations: %w", err) } maps := make(map[string]*MapSpec) if err := ec.loadMaps(maps, mapSections); err != nil { - return nil, xerrors.Errorf("load maps: %w", err) + return nil, fmt.Errorf("load maps: %w", err) } if len(btfMaps) > 0 { if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil { - return nil, xerrors.Errorf("load BTF maps: %w", err) + return nil, fmt.Errorf("load BTF maps: %w", err) } } if len(dataSections) > 0 { - if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil { - return nil, xerrors.Errorf("load data sections: %w", err) + for idx := range dataSections { + if !referencedSections[idx] { + // Prune data sections which are not referenced by any + // instructions. + delete(dataSections, idx) + } } - } - relocations, err := ec.loadRelocations(relSections) - if err != nil { - return nil, xerrors.Errorf("load relocations: %w", err) + if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil { + return nil, fmt.Errorf("load data sections: %w", err) + } } progs, err := ec.loadPrograms(progSections, relocations, btfSpec) if err != nil { - return nil, xerrors.Errorf("load programs: %w", err) + return nil, fmt.Errorf("load programs: %w", err) } return &CollectionSpec{maps, progs}, nil @@ -140,11 +148,12 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { func loadLicense(sec *elf.Section) (string, error) { if sec == nil { - return "", xerrors.New("missing license section") + return "", nil } + data, err := sec.Data() if err != nil { - return "", xerrors.Errorf("section %s: %v", sec.Name, err) + return "", fmt.Errorf("section %s: %v", sec.Name, err) } return string(bytes.TrimRight(data, "\000")), nil } @@ -156,12 +165,12 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { var version uint32 if err := binary.Read(sec.Open(), bo, &version); err != nil { - return 0, xerrors.Errorf("section %s: %v", sec.Name, err) + return 0, fmt.Errorf("section %s: %v", sec.Name, err) } return version, nil } -func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btf *btf.Spec) (map[string]*ProgramSpec, error) { +func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btfSpec *btf.Spec) (map[string]*ProgramSpec, error) { var ( progs []*ProgramSpec libs []*ProgramSpec @@ -170,34 +179,36 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, for idx, sec := range progSections { syms := ec.symbolsPerSection[idx] if len(syms) == 0 { - return nil, xerrors.Errorf("section %v: missing symbols", sec.Name) + return nil, fmt.Errorf("section %v: missing symbols", sec.Name) } funcSym, ok := syms[0] if !ok { - return nil, xerrors.Errorf("section %v: no label at start", sec.Name) + return nil, fmt.Errorf("section %v: no label at start", sec.Name) } insns, length, err := ec.loadInstructions(sec, syms, relocations[idx]) if err != nil { - return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err) + return nil, fmt.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err) } - progType, attachType := getProgType(sec.Name) + progType, attachType, attachTo := getProgType(sec.Name) spec := &ProgramSpec{ Name: funcSym.Name, Type: progType, AttachType: attachType, + AttachTo: attachTo, License: ec.license, KernelVersion: ec.version, Instructions: insns, + ByteOrder: ec.ByteOrder, } - if btf != nil { - spec.BTF, err = btf.Program(sec.Name, length) - if err != nil { - return nil, xerrors.Errorf("BTF for section %s (program %s): %w", sec.Name, funcSym.Name, err) + if btfSpec != nil { + spec.BTF, err = btfSpec.Program(sec.Name, length) + if err != nil && !errors.Is(err, btf.ErrNoExtendedInfo) { + return nil, fmt.Errorf("program %s: %w", funcSym.Name, err) } } @@ -215,7 +226,7 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, for _, prog := range progs { err := link(prog, libs) if err != nil { - return nil, xerrors.Errorf("program %s: %w", prog.Name, err) + return nil, fmt.Errorf("program %s: %w", prog.Name, err) } res[prog.Name] = prog } @@ -236,14 +247,14 @@ func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations m return insns, offset, nil } if err != nil { - return nil, 0, xerrors.Errorf("offset %d: %w", offset, err) + return nil, 0, fmt.Errorf("offset %d: %w", offset, err) } ins.Symbol = symbols[offset].Name if rel, ok := relocations[offset]; ok { if err = ec.relocateInstruction(&ins, rel); err != nil { - return nil, 0, xerrors.Errorf("offset %d: can't relocate instruction: %w", offset, err) + return nil, 0, fmt.Errorf("offset %d: can't relocate instruction: %w", offset, err) } } @@ -264,7 +275,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err // from the section itself. idx := int(rel.Section) if idx > len(ec.Sections) { - return xerrors.New("out-of-bounds section index") + return errors.New("out-of-bounds section index") } name = ec.Sections[idx].Name @@ -284,7 +295,7 @@ outer: // section. Weirdly, the offset of the real symbol in the // section is encoded in the instruction stream. if bind != elf.STB_LOCAL { - return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind) + return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind) } // For some reason, clang encodes the offset of the symbol its @@ -306,13 +317,13 @@ outer: case elf.STT_OBJECT: if bind != elf.STB_GLOBAL { - return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind) + return fmt.Errorf("load: %s: unsupported binding: %s", name, bind) } ins.Src = asm.PseudoMapFD default: - return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ) + return fmt.Errorf("load: %s: unsupported relocation: %s", name, typ) } // Mark the instruction as needing an update when creating the @@ -323,18 +334,18 @@ outer: case ins.OpCode.JumpOp() == asm.Call: if ins.Src != asm.PseudoCall { - return xerrors.Errorf("call: %s: incorrect source register", name) + return fmt.Errorf("call: %s: incorrect source register", name) } switch typ { case elf.STT_NOTYPE, elf.STT_FUNC: if bind != elf.STB_GLOBAL { - return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) + return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) } case elf.STT_SECTION: if bind != elf.STB_LOCAL { - return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) + return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) } // The function we want to call is in the indicated section, @@ -343,23 +354,23 @@ outer: // A value of -1 references the first instruction in the section. offset := int64(int32(ins.Constant)+1) * asm.InstructionSize if offset < 0 { - return xerrors.Errorf("call: %s: invalid offset %d", name, offset) + return fmt.Errorf("call: %s: invalid offset %d", name, offset) } sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)] if !ok { - return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset) + return fmt.Errorf("call: %s: no symbol at offset %d", name, offset) } ins.Constant = -1 name = sym.Name default: - return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ) + return fmt.Errorf("call: %s: invalid symbol type %s", name, typ) } default: - return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode) + return fmt.Errorf("relocation for unsupported instruction: %s", ins.OpCode) } ins.Reference = name @@ -370,11 +381,11 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio for idx, sec := range mapSections { syms := ec.symbolsPerSection[idx] if len(syms) == 0 { - return xerrors.Errorf("section %v: no symbols", sec.Name) + return fmt.Errorf("section %v: no symbols", sec.Name) } if sec.Size%uint64(len(syms)) != 0 { - return xerrors.Errorf("section %v: map descriptors are not of equal size", sec.Name) + return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name) } var ( @@ -384,11 +395,11 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size { mapSym, ok := syms[offset] if !ok { - return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) + return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) } if maps[mapSym.Name] != nil { - return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym) + return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym) } lr := io.LimitReader(r, int64(size)) @@ -398,19 +409,19 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio } switch { case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: - return xerrors.Errorf("map %v: missing type", mapSym) + return fmt.Errorf("map %v: missing type", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: - return xerrors.Errorf("map %v: missing key size", mapSym) + return fmt.Errorf("map %v: missing key size", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: - return xerrors.Errorf("map %v: missing value size", mapSym) + return fmt.Errorf("map %v: missing value size", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: - return xerrors.Errorf("map %v: missing max entries", mapSym) + return fmt.Errorf("map %v: missing max entries", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: - return xerrors.Errorf("map %v: missing flags", mapSym) + return fmt.Errorf("map %v: missing flags", mapSym) } if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { - return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) + return fmt.Errorf("map %v: unknown and non-zero fields in definition", mapSym) } maps[mapSym.Name] = &spec @@ -422,84 +433,116 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { if spec == nil { - return xerrors.Errorf("missing BTF") + return fmt.Errorf("missing BTF") } for idx, sec := range mapSections { syms := ec.symbolsPerSection[idx] if len(syms) == 0 { - return xerrors.Errorf("section %v: no symbols", sec.Name) + return fmt.Errorf("section %v: no symbols", sec.Name) } for _, sym := range syms { name := sym.Name if maps[name] != nil { - return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym) - } - - btfMap, btfMapMembers, err := spec.Map(name) - if err != nil { - return xerrors.Errorf("map %v: can't get BTF: %w", name, err) + return fmt.Errorf("section %v: map %v already exists", sec.Name, sym) } - spec, err := mapSpecFromBTF(btfMap, btfMapMembers) + mapSpec, err := mapSpecFromBTF(spec, name) if err != nil { - return xerrors.Errorf("map %v: %w", name, err) + return fmt.Errorf("map %v: %w", name, err) } - maps[name] = spec + maps[name] = mapSpec } } return nil } -func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, error) { +func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) { + btfMap, btfMapMembers, err := spec.Map(name) + if err != nil { + return nil, fmt.Errorf("can't get BTF: %w", err) + } + + keyType := btf.MapKey(btfMap) + size, err := btf.Sizeof(keyType) + if err != nil { + return nil, fmt.Errorf("can't get size of BTF key: %w", err) + } + keySize := uint32(size) + + valueType := btf.MapValue(btfMap) + size, err = btf.Sizeof(valueType) + if err != nil { + return nil, fmt.Errorf("can't get size of BTF value: %w", err) + } + valueSize := uint32(size) + var ( mapType, flags, maxEntries uint32 - err error ) for _, member := range btfMapMembers { switch member.Name { case "type": mapType, err = uintFromBTF(member.Type) if err != nil { - return nil, xerrors.Errorf("can't get type: %w", err) + return nil, fmt.Errorf("can't get type: %w", err) } case "map_flags": flags, err = uintFromBTF(member.Type) if err != nil { - return nil, xerrors.Errorf("can't get BTF map flags: %w", err) + return nil, fmt.Errorf("can't get BTF map flags: %w", err) } case "max_entries": maxEntries, err = uintFromBTF(member.Type) if err != nil { - return nil, xerrors.Errorf("can't get BTF map max entries: %w", err) + return nil, fmt.Errorf("can't get BTF map max entries: %w", err) } - case "key": - case "value": - default: - return nil, xerrors.Errorf("unrecognized field %s in BTF map definition", member.Name) - } - } + case "key_size": + if _, isVoid := keyType.(*btf.Void); !isVoid { + return nil, errors.New("both key and key_size given") + } - keySize, err := btf.Sizeof(btf.MapKey(btfMap)) - if err != nil { - return nil, xerrors.Errorf("can't get size of BTF key: %w", err) - } + keySize, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF key size: %w", err) + } - valueSize, err := btf.Sizeof(btf.MapValue(btfMap)) - if err != nil { - return nil, xerrors.Errorf("can't get size of BTF value: %w", err) + case "value_size": + if _, isVoid := valueType.(*btf.Void); !isVoid { + return nil, errors.New("both value and value_size given") + } + + valueSize, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF value size: %w", err) + } + + case "pinning": + pinning, err := uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get pinning: %w", err) + } + + if pinning != 0 { + return nil, fmt.Errorf("'pinning' attribute not supported: %w", ErrNotSupported) + } + + case "key", "value": + default: + return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) + } } return &MapSpec{ Type: MapType(mapType), - KeySize: uint32(keySize), - ValueSize: uint32(valueSize), + KeySize: keySize, + ValueSize: valueSize, MaxEntries: maxEntries, Flags: flags, BTF: btfMap, @@ -511,12 +554,12 @@ func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, erro func uintFromBTF(typ btf.Type) (uint32, error) { ptr, ok := typ.(*btf.Pointer) if !ok { - return 0, xerrors.Errorf("not a pointer: %v", typ) + return 0, fmt.Errorf("not a pointer: %v", typ) } arr, ok := ptr.Target.(*btf.Array) if !ok { - return 0, xerrors.Errorf("not a pointer to array: %v", typ) + return 0, fmt.Errorf("not a pointer to array: %v", typ) } return arr.Nelems, nil @@ -524,7 +567,7 @@ func uintFromBTF(typ btf.Type) (uint32, error) { func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { if spec == nil { - return xerrors.New("data sections require BTF") + return errors.New("data sections require BTF, make sure all consts are marked as static") } for _, sec := range dataSections { @@ -535,11 +578,11 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[e data, err := sec.Data() if err != nil { - return xerrors.Errorf("data section %s: can't get contents: %w", sec.Name, err) + return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) } if uint64(len(data)) > math.MaxUint32 { - return xerrors.Errorf("data section %s: contents exceed maximum size", sec.Name) + return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) } mapSpec := &MapSpec{ @@ -566,91 +609,79 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[e return nil } -func getProgType(v string) (ProgramType, AttachType) { - types := map[string]ProgramType{ - // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568 - "socket": SocketFilter, - "seccomp": SocketFilter, - "kprobe/": Kprobe, - "uprobe/": Kprobe, - "kretprobe/": Kprobe, - "uretprobe/": Kprobe, - "tracepoint/": TracePoint, - "raw_tracepoint/": RawTracepoint, - "xdp": XDP, - "perf_event": PerfEvent, - "lwt_in": LWTIn, - "lwt_out": LWTOut, - "lwt_xmit": LWTXmit, - "lwt_seg6local": LWTSeg6Local, - "sockops": SockOps, - "sk_skb": SkSKB, - "sk_msg": SkMsg, - "lirc_mode2": LircMode2, - "flow_dissector": FlowDissector, - - "cgroup_skb/": CGroupSKB, - "cgroup/dev": CGroupDevice, - "cgroup/skb": CGroupSKB, - "cgroup/sock": CGroupSock, - "cgroup/post_bind": CGroupSock, - "cgroup/bind": CGroupSockAddr, - "cgroup/connect": CGroupSockAddr, - "cgroup/sendmsg": CGroupSockAddr, - "cgroup/recvmsg": CGroupSockAddr, - "cgroup/sysctl": CGroupSysctl, - "cgroup/getsockopt": CGroupSockopt, - "cgroup/setsockopt": CGroupSockopt, - "classifier": SchedCLS, - "action": SchedACT, - } - attachTypes := map[string]AttachType{ - "cgroup_skb/ingress": AttachCGroupInetIngress, - "cgroup_skb/egress": AttachCGroupInetEgress, - "cgroup/sock": AttachCGroupInetSockCreate, - "cgroup/post_bind4": AttachCGroupInet4PostBind, - "cgroup/post_bind6": AttachCGroupInet6PostBind, - "cgroup/dev": AttachCGroupDevice, - "sockops": AttachCGroupSockOps, - "sk_skb/stream_parser": AttachSkSKBStreamParser, - "sk_skb/stream_verdict": AttachSkSKBStreamVerdict, - "sk_msg": AttachSkSKBStreamVerdict, - "lirc_mode2": AttachLircMode2, - "flow_dissector": AttachFlowDissector, - "cgroup/bind4": AttachCGroupInet4Bind, - "cgroup/bind6": AttachCGroupInet6Bind, - "cgroup/connect4": AttachCGroupInet4Connect, - "cgroup/connect6": AttachCGroupInet6Connect, - "cgroup/sendmsg4": AttachCGroupUDP4Sendmsg, - "cgroup/sendmsg6": AttachCGroupUDP6Sendmsg, - "cgroup/recvmsg4": AttachCGroupUDP4Recvmsg, - "cgroup/recvmsg6": AttachCGroupUDP6Recvmsg, - "cgroup/sysctl": AttachCGroupSysctl, - "cgroup/getsockopt": AttachCGroupGetsockopt, - "cgroup/setsockopt": AttachCGroupSetsockopt, - } - attachType := AttachNone - for k, t := range attachTypes { - if strings.HasPrefix(v, k) { - attachType = t - } - } - - for k, t := range types { - if strings.HasPrefix(v, k) { - return t, attachType - } - } - return UnspecifiedProgram, AttachNone +func getProgType(sectionName string) (ProgramType, AttachType, string) { + types := map[string]struct { + progType ProgramType + attachType AttachType + }{ + // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c + "socket": {SocketFilter, AttachNone}, + "seccomp": {SocketFilter, AttachNone}, + "kprobe/": {Kprobe, AttachNone}, + "uprobe/": {Kprobe, AttachNone}, + "kretprobe/": {Kprobe, AttachNone}, + "uretprobe/": {Kprobe, AttachNone}, + "tracepoint/": {TracePoint, AttachNone}, + "raw_tracepoint/": {RawTracepoint, AttachNone}, + "xdp": {XDP, AttachNone}, + "perf_event": {PerfEvent, AttachNone}, + "lwt_in": {LWTIn, AttachNone}, + "lwt_out": {LWTOut, AttachNone}, + "lwt_xmit": {LWTXmit, AttachNone}, + "lwt_seg6local": {LWTSeg6Local, AttachNone}, + "sockops": {SockOps, AttachCGroupSockOps}, + "sk_skb/stream_parser": {SkSKB, AttachSkSKBStreamParser}, + "sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser}, + "sk_msg": {SkMsg, AttachSkSKBStreamVerdict}, + "lirc_mode2": {LircMode2, AttachLircMode2}, + "flow_dissector": {FlowDissector, AttachFlowDissector}, + "iter/": {Tracing, AttachTraceIter}, + + "cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress}, + "cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress}, + "cgroup/dev": {CGroupDevice, AttachCGroupDevice}, + "cgroup/skb": {CGroupSKB, AttachNone}, + "cgroup/sock": {CGroupSock, AttachCGroupInetSockCreate}, + "cgroup/post_bind4": {CGroupSock, AttachCGroupInet4PostBind}, + "cgroup/post_bind6": {CGroupSock, AttachCGroupInet6PostBind}, + "cgroup/bind4": {CGroupSockAddr, AttachCGroupInet4Bind}, + "cgroup/bind6": {CGroupSockAddr, AttachCGroupInet6Bind}, + "cgroup/connect4": {CGroupSockAddr, AttachCGroupInet4Connect}, + "cgroup/connect6": {CGroupSockAddr, AttachCGroupInet6Connect}, + "cgroup/sendmsg4": {CGroupSockAddr, AttachCGroupUDP4Sendmsg}, + "cgroup/sendmsg6": {CGroupSockAddr, AttachCGroupUDP6Sendmsg}, + "cgroup/recvmsg4": {CGroupSockAddr, AttachCGroupUDP4Recvmsg}, + "cgroup/recvmsg6": {CGroupSockAddr, AttachCGroupUDP6Recvmsg}, + "cgroup/sysctl": {CGroupSysctl, AttachCGroupSysctl}, + "cgroup/getsockopt": {CGroupSockopt, AttachCGroupGetsockopt}, + "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt}, + "classifier": {SchedCLS, AttachNone}, + "action": {SchedACT, AttachNone}, + } + + for prefix, t := range types { + if !strings.HasPrefix(sectionName, prefix) { + continue + } + + if !strings.HasSuffix(prefix, "/") { + return t.progType, t.attachType, "" + } + + return t.progType, t.attachType, sectionName[len(prefix):] + } + + return UnspecifiedProgram, AttachNone, "" } -func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, error) { +func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, map[elf.SectionIndex]bool, error) { result := make(map[elf.SectionIndex]map[uint64]elf.Symbol) + targets := make(map[elf.SectionIndex]bool) for idx, sec := range sections { rels := make(map[uint64]elf.Symbol) if sec.Entsize < 16 { - return nil, xerrors.Errorf("section %s: relocations are less than 16 bytes", sec.Name) + return nil, nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name) } r := sec.Open() @@ -659,20 +690,22 @@ func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) ( var rel elf.Rel64 if binary.Read(ent, ec.ByteOrder, &rel) != nil { - return nil, xerrors.Errorf("can't parse relocation at offset %v", off) + return nil, nil, fmt.Errorf("can't parse relocation at offset %v", off) } symNo := int(elf.R_SYM64(rel.Info) - 1) if symNo >= len(ec.symbols) { - return nil, xerrors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo) + return nil, nil, fmt.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo) } + symbol := ec.symbols[symNo] + targets[symbol.Section] = true rels[rel.Off] = ec.symbols[symNo] } result[idx] = rels } - return result, nil + return result, targets, nil } func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol { diff --git a/vendor/github.com/cilium/ebpf/go.mod b/vendor/github.com/cilium/ebpf/go.mod index 1d3420b8595..a05cf85eddd 100644 --- a/vendor/github.com/cilium/ebpf/go.mod +++ b/vendor/github.com/cilium/ebpf/go.mod @@ -1,8 +1,5 @@ module github.com/cilium/ebpf -go 1.12 +go 1.13 -require ( - golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 -) +require golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 diff --git a/vendor/github.com/cilium/ebpf/go.sum b/vendor/github.com/cilium/ebpf/go.sum index c4e24f505cb..e8b62417e81 100644 --- a/vendor/github.com/cilium/ebpf/go.sum +++ b/vendor/github.com/cilium/ebpf/go.sum @@ -1,6 +1,2 @@ -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf.go b/vendor/github.com/cilium/ebpf/internal/btf/btf.go index 0103f4c2d2d..3dd000a284e 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf.go @@ -4,23 +4,27 @@ import ( "bytes" "debug/elf" "encoding/binary" + "errors" + "fmt" "io" "io/ioutil" "math" + "os" "reflect" + "sync" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) const btfMagic = 0xeB9F // Errors returned by BTF functions. var ( - ErrNotSupported = internal.ErrNotSupported + ErrNotSupported = internal.ErrNotSupported + ErrNotFound = errors.New("not found") + ErrNoExtendedInfo = errors.New("no extended info") ) // Spec represents decoded BTF. @@ -30,6 +34,7 @@ type Spec struct { types map[string][]Type funcInfos map[string]extInfo lineInfos map[string]extInfo + byteOrder binary.ByteOrder } type btfHeader struct { @@ -72,7 +77,7 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { } if sec.Size > math.MaxUint32 { - return nil, xerrors.Errorf("section %s exceeds maximum size", sec.Name) + return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) } sectionSizes[sec.Name] = uint32(sec.Size) @@ -85,7 +90,7 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { symbols, err := file.Symbols() if err != nil { - return nil, xerrors.Errorf("can't read symbols: %v", err) + return nil, fmt.Errorf("can't read symbols: %v", err) } variableOffsets := make(map[variable]uint32) @@ -101,97 +106,138 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { } if symbol.Value > math.MaxUint32 { - return nil, xerrors.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) + return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) } variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) } - rawTypes, rawStrings, err := parseBTF(btfSection.Open(), file.ByteOrder) + spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) if err != nil { return nil, err } - err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets) + if btfExtSection == nil { + return spec, nil + } + + spec.funcInfos, spec.lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) + if err != nil { + return nil, fmt.Errorf("can't read ext info: %w", err) + } + + return spec, nil +} + +func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { + rawTypes, rawStrings, err := parseBTF(btf, bo) if err != nil { return nil, err } - types, err := inflateRawTypes(rawTypes, rawStrings) + err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets) if err != nil { return nil, err } - var ( - funcInfos = make(map[string]extInfo) - lineInfos = make(map[string]extInfo) - ) - if btfExtSection != nil { - funcInfos, lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, rawStrings) - if err != nil { - return nil, xerrors.Errorf("can't read ext info: %w", err) - } + types, err := inflateRawTypes(rawTypes, rawStrings) + if err != nil { + return nil, err } return &Spec{ rawTypes: rawTypes, types: types, strings: rawStrings, - funcInfos: funcInfos, - lineInfos: lineInfos, + byteOrder: bo, }, nil } +var kernelBTF struct { + sync.Mutex + *Spec +} + +// LoadKernelSpec returns the current kernel's BTF information. +// +// Requires a >= 5.5 kernel with CONFIG_DEBUG_INFO_BTF enabled. Returns +// ErrNotSupported if BTF is not enabled. +func LoadKernelSpec() (*Spec, error) { + kernelBTF.Lock() + defer kernelBTF.Unlock() + + if kernelBTF.Spec != nil { + return kernelBTF.Spec, nil + } + + var err error + kernelBTF.Spec, err = loadKernelSpec() + return kernelBTF.Spec, err +} + +func loadKernelSpec() (*Spec, error) { + fh, err := os.Open("/sys/kernel/btf/vmlinux") + if os.IsNotExist(err) { + return nil, fmt.Errorf("can't open kernel BTF at /sys/kernel/btf/vmlinux: %w", ErrNotFound) + } + if err != nil { + return nil, fmt.Errorf("can't read kernel BTF: %s", err) + } + defer fh.Close() + + return loadNakedSpec(fh, internal.NativeEndian, nil, nil) +} + func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { rawBTF, err := ioutil.ReadAll(btf) if err != nil { - return nil, nil, xerrors.Errorf("can't read BTF: %v", err) + return nil, nil, fmt.Errorf("can't read BTF: %v", err) } rd := bytes.NewReader(rawBTF) var header btfHeader if err := binary.Read(rd, bo, &header); err != nil { - return nil, nil, xerrors.Errorf("can't read header: %v", err) + return nil, nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { - return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) + return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { - return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) + return nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { - return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) + return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { - return nil, nil, xerrors.New("header is too short") + return nil, nil, errors.New("header is too short") } if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { - return nil, nil, xerrors.Errorf("header padding: %v", err) + return nil, nil, fmt.Errorf("header padding: %v", err) } if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { - return nil, nil, xerrors.Errorf("can't seek to start of string section: %v", err) + return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err) } rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) if err != nil { - return nil, nil, xerrors.Errorf("can't read type names: %w", err) + return nil, nil, fmt.Errorf("can't read type names: %w", err) } if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { - return nil, nil, xerrors.Errorf("can't seek to start of type section: %v", err) + return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err) } rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) if err != nil { - return nil, nil, xerrors.Errorf("can't read types: %w", err) + return nil, nil, fmt.Errorf("can't read types: %w", err) } return rawTypes, rawStrings, nil @@ -213,9 +259,13 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s return err } + if name == ".kconfig" || name == ".ksym" { + return fmt.Errorf("reference to %s: %w", name, ErrNotSupported) + } + size, ok := sectionSizes[name] if !ok { - return xerrors.Errorf("data section %s: missing size", name) + return fmt.Errorf("data section %s: missing size", name) } rawTypes[i].SizeType = size @@ -224,17 +274,17 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s for j, secInfo := range secinfos { id := int(secInfo.Type - 1) if id >= len(rawTypes) { - return xerrors.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) + return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) } varName, err := rawStrings.Lookup(rawTypes[id].NameOff) if err != nil { - return xerrors.Errorf("data section %s: can't get name for type %d: %w", name, id, err) + return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err) } offset, ok := variableOffsets[variable{name, varName}] if !ok { - return xerrors.Errorf("data section %s: missing offset for variable %s", name, varName) + return fmt.Errorf("data section %s: missing offset for variable %s", name, varName) } secinfos[j].Offset = offset @@ -244,7 +294,12 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s return nil } -func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { +type marshalOpts struct { + ByteOrder binary.ByteOrder + StripFuncLinkage bool +} + +func (s *Spec) marshal(opts marshalOpts) ([]byte, error) { var ( buf bytes.Buffer header = new(btfHeader) @@ -256,9 +311,14 @@ func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { _, _ = buf.Write(make([]byte, headerLen)) // Write type section, just after the header. - for _, typ := range s.rawTypes { - if err := typ.Marshal(&buf, bo); err != nil { - return nil, xerrors.Errorf("can't marshal BTF: %w", err) + for _, raw := range s.rawTypes { + switch { + case opts.StripFuncLinkage && raw.Kind() == kindFunc: + raw.SetLinkage(linkageStatic) + } + + if err := raw.Marshal(&buf, opts.ByteOrder); err != nil { + return nil, fmt.Errorf("can't marshal BTF: %w", err) } } @@ -280,9 +340,9 @@ func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { } raw := buf.Bytes() - err := binary.Write(sliceWriter(raw[:headerLen]), bo, header) + err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header) if err != nil { - return nil, xerrors.Errorf("can't write header: %v", err) + return nil, fmt.Errorf("can't write header: %v", err) } return raw, nil @@ -292,7 +352,7 @@ type sliceWriter []byte func (sw sliceWriter) Write(p []byte) (int, error) { if len(p) != len(sw) { - return 0, xerrors.New("size doesn't match") + return 0, errors.New("size doesn't match") } return copy(sw, p), nil @@ -302,17 +362,22 @@ func (sw sliceWriter) Write(p []byte) (int, error) { // // Length is the number of bytes in the raw BPF instruction stream. // -// Returns an error if there is no BTF. +// Returns an error which may wrap ErrNoExtendedInfo if the Spec doesn't +// contain extended BTF info. func (s *Spec) Program(name string, length uint64) (*Program, error) { if length == 0 { - return nil, xerrors.New("length musn't be zero") + return nil, errors.New("length musn't be zero") + } + + if s.funcInfos == nil && s.lineInfos == nil { + return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo) } funcInfos, funcOK := s.funcInfos[name] lineInfos, lineOK := s.lineInfos[name] if !funcOK && !lineOK { - return nil, xerrors.Errorf("no BTF for program %s", name) + return nil, fmt.Errorf("no extended BTF info for section %s", name) } return &Program{s, length, funcInfos, lineInfos}, nil @@ -329,7 +394,7 @@ func (s *Spec) Map(name string) (*Map, []Member, error) { mapStruct, ok := mapVar.Type.(*Struct) if !ok { - return nil, nil, xerrors.Errorf("expected struct, have %s", mapVar.Type) + return nil, nil, fmt.Errorf("expected struct, have %s", mapVar.Type) } var key, value Type @@ -344,11 +409,11 @@ func (s *Spec) Map(name string) (*Map, []Member, error) { } if key == nil { - return nil, nil, xerrors.Errorf("map %s: missing 'key' in type", name) + key = (*Void)(nil) } if value == nil { - return nil, nil, xerrors.Errorf("map %s: missing 'value' in type", name) + value = (*Void)(nil) } return &Map{s, key, value}, mapStruct.Members, nil @@ -358,19 +423,18 @@ func (s *Spec) Map(name string) (*Map, []Member, error) { func (s *Spec) Datasec(name string) (*Map, error) { var datasec Datasec if err := s.FindType(name, &datasec); err != nil { - return nil, xerrors.Errorf("data section %s: can't get BTF: %w", name, err) + return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err) } return &Map{s, &Void{}, &datasec}, nil } -var errNotFound = xerrors.New("not found") - // FindType searches for a type with a specific name. // // hint determines the type of the returned Type. // -// Returns an error if there is no or multiple matches. +// Returns an error wrapping ErrNotFound if no matching +// type exists in spec. func (s *Spec) FindType(name string, typ Type) error { var ( wanted = reflect.TypeOf(typ) @@ -383,14 +447,14 @@ func (s *Spec) FindType(name string, typ Type) error { } if candidate != nil { - return xerrors.Errorf("type %s: multiple candidates for %T", name, typ) + return fmt.Errorf("type %s: multiple candidates for %T", name, typ) } candidate = typ } if candidate == nil { - return xerrors.Errorf("type %s: %w", name, errNotFound) + return fmt.Errorf("type %s: %w", name, ErrNotFound) } value := reflect.Indirect(reflect.ValueOf(copyType(candidate))) @@ -411,13 +475,20 @@ func NewHandle(spec *Spec) (*Handle, error) { return nil, err } - btf, err := spec.marshal(internal.NativeEndian) + if spec.byteOrder != internal.NativeEndian { + return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian) + } + + btf, err := spec.marshal(marshalOpts{ + ByteOrder: internal.NativeEndian, + StripFuncLinkage: haveFuncLinkage() != nil, + }) if err != nil { - return nil, xerrors.Errorf("can't marshal BTF: %w", err) + return nil, fmt.Errorf("can't marshal BTF: %w", err) } if uint64(len(btf)) > math.MaxUint32 { - return nil, xerrors.New("BTF exceeds the maximum size") + return nil, errors.New("BTF exceeds the maximum size") } attr := &bpfLoadBTFAttr{ @@ -501,12 +572,12 @@ func ProgramSpec(s *Program) *Spec { func ProgramAppend(s, other *Program) error { funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) if err != nil { - return xerrors.Errorf("func infos: %w", err) + return fmt.Errorf("func infos: %w", err) } lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) if err != nil { - return xerrors.Errorf("line infos: %w", err) + return fmt.Errorf("line infos: %w", err) } s.length += other.length @@ -560,26 +631,36 @@ func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) { return internal.NewFD(uint32(fd)), nil } -func minimalBTF(bo binary.ByteOrder) []byte { +func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte { const minHeaderLength = 24 + typesLen := uint32(binary.Size(types)) + header := btfHeader{ + Magic: btfMagic, + Version: 1, + HdrLen: minHeaderLength, + TypeOff: 0, + TypeLen: typesLen, + StringOff: typesLen, + StringLen: uint32(len(strings)), + } + + buf := new(bytes.Buffer) + _ = binary.Write(buf, bo, &header) + _ = binary.Write(buf, bo, types) + buf.Write(strings) + + return buf.Bytes() +} + +var haveBTF = internal.FeatureTest("BTF", "5.1", func() (bool, error) { var ( types struct { Integer btfType Var btfType btfVar struct{ Linkage uint32 } } - typLen = uint32(binary.Size(&types)) strings = []byte{0, 'a', 0} - header = btfHeader{ - Magic: btfMagic, - Version: 1, - HdrLen: minHeaderLength, - TypeOff: 0, - TypeLen: typLen, - StringOff: typLen, - StringLen: uint32(len(strings)), - } ) // We use a BTF_KIND_VAR here, to make sure that @@ -590,16 +671,37 @@ func minimalBTF(bo binary.ByteOrder) []byte { types.Var.SetKind(kindVar) types.Var.SizeType = 1 - buf := new(bytes.Buffer) - _ = binary.Write(buf, bo, &header) - _ = binary.Write(buf, bo, &types) - buf.Write(strings) + btf := marshalBTF(&types, strings, internal.NativeEndian) - return buf.Bytes() -} + fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + }) + if err == nil { + fd.Close() + } + // Check for EINVAL specifically, rather than err != nil since we + // otherwise misdetect due to insufficient permissions. + return !errors.Is(err, unix.EINVAL), nil +}) + +var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() (bool, error) { + var ( + types struct { + FuncProto btfType + Func btfType + } + strings = []byte{0, 'a', 0} + ) + + types.FuncProto.SetKind(kindFuncProto) + types.Func.SetKind(kindFunc) + types.Func.SizeType = 1 // aka FuncProto + types.Func.NameOff = 1 + types.Func.SetLinkage(linkageGlobal) + + btf := marshalBTF(&types, strings, internal.NativeEndian) -var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool { - btf := minimalBTF(internal.NativeEndian) fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ btf: internal.NewSlicePointer(btf), btfSize: uint32(len(btf)), @@ -607,7 +709,8 @@ var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool { if err == nil { fd.Close() } + // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. - return !xerrors.Is(err, unix.EINVAL) + return !errors.Is(err, unix.EINVAL), nil }) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go index 37c940bbd6a..e34a87ecb45 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go @@ -4,8 +4,6 @@ import ( "encoding/binary" "fmt" "io" - - "golang.org/x/xerrors" ) // btfKind describes a Type. @@ -33,6 +31,14 @@ const ( kindDatasec ) +type btfFuncLinkage uint8 + +const ( + linkageStatic btfFuncLinkage = iota + linkageGlobal + linkageExtern +) + const ( btfTypeKindShift = 24 btfTypeKindLen = 4 @@ -44,7 +50,7 @@ const ( type btfType struct { NameOff uint32 /* "info" bits arrangement - * bits 0-15: vlen (e.g. # of struct's members) + * bits 0-15: vlen (e.g. # of struct's members), linkage * bits 16-23: unused * bits 24-27: kind (e.g. int, ptr, array...etc) * bits 28-30: unused @@ -130,6 +136,14 @@ func (bt *btfType) SetVlen(vlen int) { bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) } +func (bt *btfType) Linkage() btfFuncLinkage { + return btfFuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift)) +} + +func (bt *btfType) SetLinkage(linkage btfFuncLinkage) { + bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift) +} + func (bt *btfType) Type() TypeID { // TODO: Panic here if wrong kind? return TypeID(bt.SizeType) @@ -179,6 +193,16 @@ type btfVariable struct { Linkage uint32 } +type btfEnum struct { + NameOff uint32 + Val int32 +} + +type btfParam struct { + NameOff uint32 + Type TypeID +} + func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { var ( header btfType @@ -189,14 +213,13 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { if err := binary.Read(r, bo, &header); err == io.EOF { return types, nil } else if err != nil { - return nil, xerrors.Errorf("can't read type info for id %v: %v", id, err) + return nil, fmt.Errorf("can't read type info for id %v: %v", id, err) } var data interface{} switch header.Kind() { case kindInt: - // sizeof(uint32) - data = make([]byte, 4) + data = new(uint32) case kindPointer: case kindArray: data = new(btfArray) @@ -205,8 +228,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { case kindUnion: data = make([]btfMember, header.Vlen()) case kindEnum: - // sizeof(struct btf_enum) - data = make([]byte, header.Vlen()*4*2) + data = make([]btfEnum, header.Vlen()) case kindForward: case kindTypedef: case kindVolatile: @@ -214,14 +236,13 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { case kindRestrict: case kindFunc: case kindFuncProto: - // sizeof(struct btf_param) - data = make([]byte, header.Vlen()*4*2) + data = make([]btfParam, header.Vlen()) case kindVar: data = new(btfVariable) case kindDatasec: data = make([]btfVarSecinfo, header.Vlen()) default: - return nil, xerrors.Errorf("type id %v: unknown kind: %v", id, header.Kind()) + return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind()) } if data == nil { @@ -230,7 +251,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { } if err := binary.Read(r, bo, data); err != nil { - return nil, xerrors.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) + return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) } types = append(types, rawType{header, data}) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go index 1b1e33c4d42..28918ae9e0c 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go @@ -3,13 +3,13 @@ package btf import ( "bytes" "encoding/binary" + "errors" + "fmt" "io" "io/ioutil" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" - - "golang.org/x/xerrors" ) type btfExtHeader struct { @@ -27,49 +27,49 @@ type btfExtHeader struct { func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) { var header btfExtHeader if err := binary.Read(r, bo, &header); err != nil { - return nil, nil, xerrors.Errorf("can't read header: %v", err) + return nil, nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { - return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) + return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { - return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) + return nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { - return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) + return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { - return nil, nil, xerrors.New("header is too short") + return nil, nil, errors.New("header is too short") } // Of course, the .BTF.ext header has different semantics than the // .BTF ext header. We need to ignore non-null values. _, err = io.CopyN(ioutil.Discard, r, remainder) if err != nil { - return nil, nil, xerrors.Errorf("header padding: %v", err) + return nil, nil, fmt.Errorf("header padding: %v", err) } if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { - return nil, nil, xerrors.Errorf("can't seek to function info section: %v", err) + return nil, nil, fmt.Errorf("can't seek to function info section: %v", err) } funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings) if err != nil { - return nil, nil, xerrors.Errorf("function info: %w", err) + return nil, nil, fmt.Errorf("function info: %w", err) } if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { - return nil, nil, xerrors.Errorf("can't seek to line info section: %v", err) + return nil, nil, fmt.Errorf("can't seek to line info section: %v", err) } lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings) if err != nil { - return nil, nil, xerrors.Errorf("line info: %w", err) + return nil, nil, fmt.Errorf("line info: %w", err) } return funcInfo, lineInfo, nil @@ -92,7 +92,7 @@ type extInfo struct { func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { if other.recordSize != ei.recordSize { - return extInfo{}, xerrors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) + return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) } records := make([]extInfoRecord, 0, len(ei.records)+len(other.records)) @@ -117,7 +117,7 @@ func (ei extInfo) MarshalBinary() ([]byte, error) { // while the ELF tracks it in bytes. insnOff := uint32(info.InsnOff / asm.InstructionSize) if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { - return nil, xerrors.Errorf("can't write instruction offset: %v", err) + return nil, fmt.Errorf("can't write instruction offset: %v", err) } buf.Write(info.Opaque) @@ -129,12 +129,12 @@ func (ei extInfo) MarshalBinary() ([]byte, error) { func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { var recordSize uint32 if err := binary.Read(r, bo, &recordSize); err != nil { - return nil, xerrors.Errorf("can't read record size: %v", err) + return nil, fmt.Errorf("can't read record size: %v", err) } if recordSize < 4 { // Need at least insnOff - return nil, xerrors.New("record size too short") + return nil, errors.New("record size too short") } result := make(map[string]extInfo) @@ -143,32 +143,32 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st if err := binary.Read(r, bo, &infoHeader); err == io.EOF { return result, nil } else if err != nil { - return nil, xerrors.Errorf("can't read ext info header: %v", err) + return nil, fmt.Errorf("can't read ext info header: %v", err) } secName, err := strings.Lookup(infoHeader.SecNameOff) if err != nil { - return nil, xerrors.Errorf("can't get section name: %w", err) + return nil, fmt.Errorf("can't get section name: %w", err) } if infoHeader.NumInfo == 0 { - return nil, xerrors.Errorf("section %s has invalid number of records", secName) + return nil, fmt.Errorf("section %s has invalid number of records", secName) } var records []extInfoRecord for i := uint32(0); i < infoHeader.NumInfo; i++ { var byteOff uint32 if err := binary.Read(r, bo, &byteOff); err != nil { - return nil, xerrors.Errorf("section %v: can't read extended info offset: %v", secName, err) + return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err) } buf := make([]byte, int(recordSize-4)) if _, err := io.ReadFull(r, buf); err != nil { - return nil, xerrors.Errorf("section %v: can't read record: %v", secName, err) + return nil, fmt.Errorf("section %v: can't read record: %v", secName, err) } if byteOff%asm.InstructionSize != 0 { - return nil, xerrors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) + return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) } records = append(records, extInfoRecord{uint64(byteOff), buf}) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/strings.go b/vendor/github.com/cilium/ebpf/internal/btf/strings.go index a5f905f5676..8782643a043 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/strings.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/strings.go @@ -2,10 +2,10 @@ package btf import ( "bytes" + "errors" + "fmt" "io" "io/ioutil" - - "golang.org/x/xerrors" ) type stringTable []byte @@ -13,19 +13,19 @@ type stringTable []byte func readStringTable(r io.Reader) (stringTable, error) { contents, err := ioutil.ReadAll(r) if err != nil { - return nil, xerrors.Errorf("can't read string table: %v", err) + return nil, fmt.Errorf("can't read string table: %v", err) } if len(contents) < 1 { - return nil, xerrors.New("string table is empty") + return nil, errors.New("string table is empty") } if contents[0] != '\x00' { - return nil, xerrors.New("first item in string table is non-empty") + return nil, errors.New("first item in string table is non-empty") } if contents[len(contents)-1] != '\x00' { - return nil, xerrors.New("string table isn't null terminated") + return nil, errors.New("string table isn't null terminated") } return stringTable(contents), nil @@ -33,22 +33,22 @@ func readStringTable(r io.Reader) (stringTable, error) { func (st stringTable) Lookup(offset uint32) (string, error) { if int64(offset) > int64(^uint(0)>>1) { - return "", xerrors.Errorf("offset %d overflows int", offset) + return "", fmt.Errorf("offset %d overflows int", offset) } pos := int(offset) if pos >= len(st) { - return "", xerrors.Errorf("offset %d is out of bounds", offset) + return "", fmt.Errorf("offset %d is out of bounds", offset) } if pos > 0 && st[pos-1] != '\x00' { - return "", xerrors.Errorf("offset %d isn't start of a string", offset) + return "", fmt.Errorf("offset %d isn't start of a string", offset) } str := st[pos:] end := bytes.IndexByte(str, '\x00') if end == -1 { - return "", xerrors.Errorf("offset %d isn't null terminated", offset) + return "", fmt.Errorf("offset %d isn't null terminated", offset) } return string(str[:end]), nil diff --git a/vendor/github.com/cilium/ebpf/internal/btf/types.go b/vendor/github.com/cilium/ebpf/internal/btf/types.go index 101a40f3213..264142abc0a 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/types.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/types.go @@ -1,9 +1,9 @@ package btf import ( + "errors" + "fmt" "math" - - "golang.org/x/xerrors" ) const maxTypeDepth = 32 @@ -38,9 +38,10 @@ func (n Name) name() string { // Void is the unit type of BTF. type Void struct{} -func (v Void) ID() TypeID { return 0 } -func (v Void) copy() Type { return Void{} } -func (v Void) walk(*copyStack) {} +func (v *Void) ID() TypeID { return 0 } +func (v *Void) size() uint32 { return 0 } +func (v *Void) copy() Type { return (*Void)(nil) } +func (v *Void) walk(*copyStack) {} // Int is an integer of a given length. type Int struct { @@ -310,7 +311,7 @@ func Sizeof(typ Type) (int, error) { switch v := typ.(type) { case *Array: if n > 0 && int64(v.Nelems) > math.MaxInt64/n { - return 0, xerrors.New("overflow") + return 0, errors.New("overflow") } // Arrays may be of zero length, which allows @@ -336,22 +337,22 @@ func Sizeof(typ Type) (int, error) { continue default: - return 0, xerrors.Errorf("unrecognized type %T", typ) + return 0, fmt.Errorf("unrecognized type %T", typ) } if n > 0 && elem > math.MaxInt64/n { - return 0, xerrors.New("overflow") + return 0, errors.New("overflow") } size := n * elem if int64(int(size)) != size { - return 0, xerrors.New("overflow") + return 0, errors.New("overflow") } return int(size), nil } - return 0, xerrors.New("exceeded type depth") + return 0, errors.New("exceeded type depth") } // copy a Type recursively. @@ -433,7 +434,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map for i, btfMember := range raw { name, err := rawStrings.LookupName(btfMember.NameOff) if err != nil { - return nil, xerrors.Errorf("can't get name for member %d: %w", i, err) + return nil, fmt.Errorf("can't get name for member %d: %w", i, err) } members = append(members, Member{ Name: name, @@ -447,7 +448,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map } types := make([]Type, 0, len(rawTypes)) - types = append(types, Void{}) + types = append(types, (*Void)(nil)) namedTypes = make(map[string][]Type) for i, raw := range rawTypes { @@ -460,7 +461,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map name, err := rawStrings.LookupName(raw.NameOff) if err != nil { - return nil, xerrors.Errorf("can't get name for type id %d: %w", id, err) + return nil, fmt.Errorf("can't get name for type id %d: %w", id, err) } switch raw.Kind() { @@ -484,14 +485,14 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map case kindStruct: members, err := convertMembers(raw.data.([]btfMember)) if err != nil { - return nil, xerrors.Errorf("struct %s (id %d): %w", name, id, err) + return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } typ = &Struct{id, name, raw.Size(), members} case kindUnion: members, err := convertMembers(raw.data.([]btfMember)) if err != nil { - return nil, xerrors.Errorf("union %s (id %d): %w", name, id, err) + return nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } typ = &Union{id, name, raw.Size(), members} @@ -551,7 +552,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map typ = &Datasec{id, name, raw.SizeType, vars} default: - return nil, xerrors.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) + return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) } types = append(types, typ) @@ -566,7 +567,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map for _, fixup := range fixups { i := int(fixup.id) if i >= len(types) { - return nil, xerrors.Errorf("reference to invalid type id: %d", fixup.id) + return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id) } // Default void (id 0) to unknown @@ -576,7 +577,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map } if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected { - return nil, xerrors.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) + return nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) } *fixup.typ = types[i] diff --git a/vendor/github.com/cilium/ebpf/internal/errors.go b/vendor/github.com/cilium/ebpf/internal/errors.go index 72dedb47772..b6aee81f7de 100644 --- a/vendor/github.com/cilium/ebpf/internal/errors.go +++ b/vendor/github.com/cilium/ebpf/internal/errors.go @@ -2,11 +2,11 @@ package internal import ( "bytes" + "errors" "fmt" "strings" "github.com/cilium/ebpf/internal/unix" - "golang.org/x/xerrors" ) // ErrorWithLog returns an error that includes logs from the @@ -16,7 +16,7 @@ import ( // the log. It is used to check for truncation of the output. func ErrorWithLog(err error, log []byte, logErr error) error { logStr := strings.Trim(CString(log), "\t\r\n ") - if xerrors.Is(logErr, unix.ENOSPC) { + if errors.Is(logErr, unix.ENOSPC) { logStr += " (truncated...)" } diff --git a/vendor/github.com/cilium/ebpf/internal/fd.go b/vendor/github.com/cilium/ebpf/internal/fd.go index f27a1f260ba..af04955bd53 100644 --- a/vendor/github.com/cilium/ebpf/internal/fd.go +++ b/vendor/github.com/cilium/ebpf/internal/fd.go @@ -1,15 +1,16 @@ package internal import ( + "errors" + "fmt" + "os" "runtime" "strconv" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) -var ErrClosedFd = xerrors.New("use of closed file descriptor") +var ErrClosedFd = errors.New("use of closed file descriptor") type FD struct { raw int64 @@ -56,8 +57,13 @@ func (fd *FD) Dup() (*FD, error) { dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) if err != nil { - return nil, xerrors.Errorf("can't dup fd: %v", err) + return nil, fmt.Errorf("can't dup fd: %v", err) } return NewFD(uint32(dup)), nil } + +func (fd *FD) File(name string) *os.File { + fd.Forget() + return os.NewFile(uintptr(fd.raw), name) +} diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go index 5ecf954773e..7375b21ef98 100644 --- a/vendor/github.com/cilium/ebpf/internal/feature.go +++ b/vendor/github.com/cilium/ebpf/internal/feature.go @@ -1,14 +1,13 @@ package internal import ( + "errors" "fmt" "sync" - - "golang.org/x/xerrors" ) // ErrNotSupported indicates that a feature is not supported by the current kernel. -var ErrNotSupported = xerrors.New("not supported") +var ErrNotSupported = errors.New("not supported") // UnsupportedFeatureError is returned by FeatureTest() functions. type UnsupportedFeatureError struct { @@ -29,33 +28,63 @@ func (ufe *UnsupportedFeatureError) Is(target error) bool { return target == ErrNotSupported } +type featureTest struct { + sync.Mutex + successful bool + result error +} + +// FeatureTestFn is used to determine whether the kernel supports +// a certain feature. +// +// The return values have the following semantics: +// +// err != nil: the test couldn't be executed +// err == nil && available: the feature is available +// err == nil && !available: the feature isn't available +type FeatureTestFn func() (available bool, err error) + // FeatureTest wraps a function so that it is run at most once. // // name should identify the tested feature, while version must be in the // form Major.Minor[.Patch]. // -// Returns a descriptive UnsupportedFeatureError if the feature is not available. -func FeatureTest(name, version string, fn func() bool) func() error { +// Returns an error wrapping ErrNotSupported if the feature is not supported. +func FeatureTest(name, version string, fn FeatureTestFn) func() error { v, err := NewVersion(version) if err != nil { return func() error { return err } } - var ( - once sync.Once - result error - ) - + ft := new(featureTest) return func() error { - once.Do(func() { - if !fn() { - result = &UnsupportedFeatureError{ - MinimumVersion: v, - Name: name, - } + ft.Lock() + defer ft.Unlock() + + if ft.successful { + return ft.result + } + + available, err := fn() + if errors.Is(err, ErrNotSupported) { + // The feature test aborted because a dependent feature + // is missing, which we should cache. + available = false + } else if err != nil { + // We couldn't execute the feature test to a point + // where it could make a determination. + // Don't cache the result, just return it. + return fmt.Errorf("can't detect support for %s: %w", name, err) + } + + ft.successful = true + if !available { + ft.result = &UnsupportedFeatureError{ + MinimumVersion: v, + Name: name, } - }) - return result + } + return ft.result } } @@ -69,7 +98,7 @@ func NewVersion(ver string) (Version, error) { var major, minor, patch uint16 n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) if n < 2 { - return Version{}, xerrors.Errorf("invalid version: %s", ver) + return Version{}, fmt.Errorf("invalid version: %s", ver) } return Version{major, minor, patch}, nil } diff --git a/vendor/github.com/cilium/ebpf/internal/io.go b/vendor/github.com/cilium/ebpf/internal/io.go index f364f2110a2..fa7402782d7 100644 --- a/vendor/github.com/cilium/ebpf/internal/io.go +++ b/vendor/github.com/cilium/ebpf/internal/io.go @@ -1,6 +1,6 @@ package internal -import "golang.org/x/xerrors" +import "errors" // DiscardZeroes makes sure that all written bytes are zero // before discarding them. @@ -9,7 +9,7 @@ type DiscardZeroes struct{} func (DiscardZeroes) Write(p []byte) (int, error) { for _, b := range p { if b != 0 { - return 0, xerrors.New("encountered non-zero byte") + return 0, errors.New("encountered non-zero byte") } } return len(p), nil diff --git a/vendor/github.com/cilium/ebpf/internal/syscall.go b/vendor/github.com/cilium/ebpf/internal/syscall.go index b32cf3bceb3..efbf40327c6 100644 --- a/vendor/github.com/cilium/ebpf/internal/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/syscall.go @@ -1,16 +1,61 @@ package internal import ( + "fmt" + "path/filepath" "runtime" "unsafe" "github.com/cilium/ebpf/internal/unix" ) +//go:generate stringer -output syscall_string.go -type=BPFCmd + +// BPFCmd identifies a subcommand of the bpf syscall. +type BPFCmd int + +// Well known BPF commands. +const ( + BPF_MAP_CREATE BPFCmd = iota + BPF_MAP_LOOKUP_ELEM + BPF_MAP_UPDATE_ELEM + BPF_MAP_DELETE_ELEM + BPF_MAP_GET_NEXT_KEY + BPF_PROG_LOAD + BPF_OBJ_PIN + BPF_OBJ_GET + BPF_PROG_ATTACH + BPF_PROG_DETACH + BPF_PROG_TEST_RUN + BPF_PROG_GET_NEXT_ID + BPF_MAP_GET_NEXT_ID + BPF_PROG_GET_FD_BY_ID + BPF_MAP_GET_FD_BY_ID + BPF_OBJ_GET_INFO_BY_FD + BPF_PROG_QUERY + BPF_RAW_TRACEPOINT_OPEN + BPF_BTF_LOAD + BPF_BTF_GET_FD_BY_ID + BPF_TASK_FD_QUERY + BPF_MAP_LOOKUP_AND_DELETE_ELEM + BPF_MAP_FREEZE + BPF_BTF_GET_NEXT_ID + BPF_MAP_LOOKUP_BATCH + BPF_MAP_LOOKUP_AND_DELETE_BATCH + BPF_MAP_UPDATE_BATCH + BPF_MAP_DELETE_BATCH + BPF_LINK_CREATE + BPF_LINK_UPDATE + BPF_LINK_GET_FD_BY_ID + BPF_LINK_GET_NEXT_ID + BPF_ENABLE_STATS + BPF_ITER_CREATE +) + // BPF wraps SYS_BPF. // // Any pointers contained in attr must use the Pointer type from this package. -func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { +func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) runtime.KeepAlive(attr) @@ -21,3 +66,74 @@ func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { return r1, err } + +type BPFProgAttachAttr struct { + TargetFd uint32 + AttachBpfFd uint32 + AttachType uint32 + AttachFlags uint32 + ReplaceBpfFd uint32 +} + +func BPFProgAttach(attr *BPFProgAttachAttr) error { + _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +type BPFProgDetachAttr struct { + TargetFd uint32 + AttachBpfFd uint32 + AttachType uint32 +} + +func BPFProgDetach(attr *BPFProgDetachAttr) error { + _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +type bpfObjAttr struct { + fileName Pointer + fd uint32 + fileFlags uint32 +} + +const bpfFSType = 0xcafe4a11 + +// BPFObjPin wraps BPF_OBJ_PIN. +func BPFObjPin(fileName string, fd *FD) error { + dirName := filepath.Dir(fileName) + var statfs unix.Statfs_t + if err := unix.Statfs(dirName, &statfs); err != nil { + return err + } + if uint64(statfs.Type) != bpfFSType { + return fmt.Errorf("%s is not on a bpf filesystem", fileName) + } + + value, err := fd.Value() + if err != nil { + return err + } + + attr := bpfObjAttr{ + fileName: NewStringPointer(fileName), + fd: value, + } + _, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return fmt.Errorf("pin object %s: %w", fileName, err) + } + return nil +} + +// BPFObjGet wraps BPF_OBJ_GET. +func BPFObjGet(fileName string) (*FD, error) { + attr := bpfObjAttr{ + fileName: NewStringPointer(fileName), + } + ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return nil, fmt.Errorf("get object %s: %w", fileName, err) + } + return NewFD(uint32(ptr)), nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/syscall_string.go b/vendor/github.com/cilium/ebpf/internal/syscall_string.go new file mode 100644 index 00000000000..85df0477973 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/syscall_string.go @@ -0,0 +1,56 @@ +// Code generated by "stringer -output syscall_string.go -type=BPFCmd"; DO NOT EDIT. + +package internal + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BPF_MAP_CREATE-0] + _ = x[BPF_MAP_LOOKUP_ELEM-1] + _ = x[BPF_MAP_UPDATE_ELEM-2] + _ = x[BPF_MAP_DELETE_ELEM-3] + _ = x[BPF_MAP_GET_NEXT_KEY-4] + _ = x[BPF_PROG_LOAD-5] + _ = x[BPF_OBJ_PIN-6] + _ = x[BPF_OBJ_GET-7] + _ = x[BPF_PROG_ATTACH-8] + _ = x[BPF_PROG_DETACH-9] + _ = x[BPF_PROG_TEST_RUN-10] + _ = x[BPF_PROG_GET_NEXT_ID-11] + _ = x[BPF_MAP_GET_NEXT_ID-12] + _ = x[BPF_PROG_GET_FD_BY_ID-13] + _ = x[BPF_MAP_GET_FD_BY_ID-14] + _ = x[BPF_OBJ_GET_INFO_BY_FD-15] + _ = x[BPF_PROG_QUERY-16] + _ = x[BPF_RAW_TRACEPOINT_OPEN-17] + _ = x[BPF_BTF_LOAD-18] + _ = x[BPF_BTF_GET_FD_BY_ID-19] + _ = x[BPF_TASK_FD_QUERY-20] + _ = x[BPF_MAP_LOOKUP_AND_DELETE_ELEM-21] + _ = x[BPF_MAP_FREEZE-22] + _ = x[BPF_BTF_GET_NEXT_ID-23] + _ = x[BPF_MAP_LOOKUP_BATCH-24] + _ = x[BPF_MAP_LOOKUP_AND_DELETE_BATCH-25] + _ = x[BPF_MAP_UPDATE_BATCH-26] + _ = x[BPF_MAP_DELETE_BATCH-27] + _ = x[BPF_LINK_CREATE-28] + _ = x[BPF_LINK_UPDATE-29] + _ = x[BPF_LINK_GET_FD_BY_ID-30] + _ = x[BPF_LINK_GET_NEXT_ID-31] + _ = x[BPF_ENABLE_STATS-32] + _ = x[BPF_ITER_CREATE-33] +} + +const _BPFCmd_name = "BPF_MAP_CREATEBPF_MAP_LOOKUP_ELEMBPF_MAP_UPDATE_ELEMBPF_MAP_DELETE_ELEMBPF_MAP_GET_NEXT_KEYBPF_PROG_LOADBPF_OBJ_PINBPF_OBJ_GETBPF_PROG_ATTACHBPF_PROG_DETACHBPF_PROG_TEST_RUNBPF_PROG_GET_NEXT_IDBPF_MAP_GET_NEXT_IDBPF_PROG_GET_FD_BY_IDBPF_MAP_GET_FD_BY_IDBPF_OBJ_GET_INFO_BY_FDBPF_PROG_QUERYBPF_RAW_TRACEPOINT_OPENBPF_BTF_LOADBPF_BTF_GET_FD_BY_IDBPF_TASK_FD_QUERYBPF_MAP_LOOKUP_AND_DELETE_ELEMBPF_MAP_FREEZEBPF_BTF_GET_NEXT_IDBPF_MAP_LOOKUP_BATCHBPF_MAP_LOOKUP_AND_DELETE_BATCHBPF_MAP_UPDATE_BATCHBPF_MAP_DELETE_BATCHBPF_LINK_CREATEBPF_LINK_UPDATEBPF_LINK_GET_FD_BY_IDBPF_LINK_GET_NEXT_IDBPF_ENABLE_STATSBPF_ITER_CREATE" + +var _BPFCmd_index = [...]uint16{0, 14, 33, 52, 71, 91, 104, 115, 126, 141, 156, 173, 193, 212, 233, 253, 275, 289, 312, 324, 344, 361, 391, 405, 424, 444, 475, 495, 515, 530, 545, 566, 586, 602, 617} + +func (i BPFCmd) String() string { + if i < 0 || i >= BPFCmd(len(_BPFCmd_index)-1) { + return "BPFCmd(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BPFCmd_name[_BPFCmd_index[i]:_BPFCmd_index[i+1]] +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go index 89baae2b7e0..9363d0be81c 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -10,11 +10,13 @@ import ( const ( ENOENT = linux.ENOENT + EEXIST = linux.EEXIST EAGAIN = linux.EAGAIN ENOSPC = linux.ENOSPC EINVAL = linux.EINVAL EPOLLIN = linux.EPOLLIN EINTR = linux.EINTR + EPERM = linux.EPERM ESRCH = linux.ESRCH ENODEV = linux.ENODEV BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go index 8fc8e77ab67..2dea950f888 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -12,10 +12,12 @@ var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime const ( ENOENT = syscall.ENOENT + EEXIST = syscall.EEXIST EAGAIN = syscall.EAGAIN ENOSPC = syscall.ENOSPC EINVAL = syscall.EINVAL EINTR = syscall.EINTR + EPERM = syscall.EPERM ESRCH = syscall.ESRCH ENODEV = syscall.ENODEV BPF_F_RDONLY_PROG = 0 diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index b9a54daec82..1bb8f61c25a 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -1,10 +1,10 @@ package ebpf import ( + "fmt" + "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/btf" - - "golang.org/x/xerrors" ) // link resolves bpf-to-bpf calls. @@ -28,7 +28,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error { needed, err := needSection(insns, lib.Instructions) if err != nil { - return xerrors.Errorf("linking %s: %w", lib.Name, err) + return fmt.Errorf("linking %s: %w", lib.Name, err) } if !needed { @@ -41,7 +41,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error { if prog.BTF != nil && lib.BTF != nil { if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { - return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err) + return fmt.Errorf("linking BTF of %s: %w", lib.Name, err) } } } diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index ddb80542bd5..461b995a54b 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -1,20 +1,20 @@ package ebpf import ( + "errors" "fmt" "strings" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) // Errors returned by Map and MapIterator methods. var ( - ErrKeyNotExist = xerrors.New("key does not exist") - ErrIterationAborted = xerrors.New("iteration aborted") + ErrKeyNotExist = errors.New("key does not exist") + ErrKeyExist = errors.New("key already exists") + ErrIterationAborted = errors.New("iteration aborted") ) // MapID represents the unique ID of an eBPF map @@ -91,7 +91,7 @@ type Map struct { // You should not use fd after calling this function. func NewMapFromFD(fd int) (*Map, error) { if fd < 0 { - return nil, xerrors.New("invalid fd") + return nil, errors.New("invalid fd") } bpfFd := internal.NewFD(uint32(fd)) @@ -107,14 +107,18 @@ func NewMapFromFD(fd int) (*Map, error) { // // Creating a map for the first time will perform feature detection // by creating small, temporary maps. +// +// The caller is responsible for ensuring the process' rlimit is set +// sufficiently high for locking memory during map creation. This can be done +// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMap. func NewMap(spec *MapSpec) (*Map, error) { if spec.BTF == nil { return newMapWithBTF(spec, nil) } handle, err := btf.NewHandle(btf.MapSpec(spec.BTF)) - if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { - return nil, xerrors.Errorf("can't load BTF: %w", err) + if err != nil && !errors.Is(err, btf.ErrNotSupported) { + return nil, fmt.Errorf("can't load BTF: %w", err) } return newMapWithBTF(spec, handle) @@ -126,7 +130,7 @@ func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) { } if spec.InnerMap == nil { - return nil, xerrors.Errorf("%s requires InnerMap", spec.Type) + return nil, fmt.Errorf("%s requires InnerMap", spec.Type) } template, err := createMap(spec.InnerMap, nil, handle) @@ -150,25 +154,25 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err } if abi.ValueSize != 0 && abi.ValueSize != 4 { - return nil, xerrors.New("ValueSize must be zero or four for map of map") + return nil, errors.New("ValueSize must be zero or four for map of map") } abi.ValueSize = 4 case PerfEventArray: if abi.KeySize != 0 && abi.KeySize != 4 { - return nil, xerrors.New("KeySize must be zero or four for perf event array") + return nil, errors.New("KeySize must be zero or four for perf event array") } abi.KeySize = 4 if abi.ValueSize != 0 && abi.ValueSize != 4 { - return nil, xerrors.New("ValueSize must be zero or four for perf event array") + return nil, errors.New("ValueSize must be zero or four for perf event array") } abi.ValueSize = 4 if abi.MaxEntries == 0 { n, err := internal.PossibleCPUs() if err != nil { - return nil, xerrors.Errorf("perf event array: %w", err) + return nil, fmt.Errorf("perf event array: %w", err) } abi.MaxEntries = uint32(n) } @@ -176,7 +180,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { if err := haveMapMutabilityModifiers(); err != nil { - return nil, xerrors.Errorf("map create: %w", err) + return nil, fmt.Errorf("map create: %w", err) } } @@ -192,7 +196,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err var err error attr.innerMapFd, err = inner.Value() if err != nil { - return nil, xerrors.Errorf("map create: %w", err) + return nil, fmt.Errorf("map create: %w", err) } } @@ -208,7 +212,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err fd, err := bpfMapCreate(&attr) if err != nil { - return nil, xerrors.Errorf("map create: %w", err) + return nil, fmt.Errorf("map create: %w", err) } m, err := newMap(fd, spec.Name, abi) @@ -218,13 +222,13 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err if err := m.populate(spec.Contents); err != nil { m.Close() - return nil, xerrors.Errorf("map create: can't set initial contents: %w", err) + return nil, fmt.Errorf("map create: can't set initial contents: %w", err) } if spec.Freeze { if err := m.Freeze(); err != nil { m.Close() - return nil, xerrors.Errorf("can't freeze map: %w", err) + return nil, fmt.Errorf("can't freeze map: %w", err) } } @@ -296,9 +300,9 @@ func (m *Map) Lookup(key, valueOut interface{}) error { *value = m return nil case *Map: - return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) + return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) case Map: - return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) + return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) case **Program: p, err := unmarshalProgram(valueBytes) @@ -310,9 +314,9 @@ func (m *Map) Lookup(key, valueOut interface{}) error { *value = p return nil case *Program: - return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) + return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) case Program: - return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) + return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) default: return unmarshalBytes(valueOut, valueBytes) @@ -327,11 +331,11 @@ func (m *Map) LookupAndDelete(key, valueOut interface{}) error { keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) if err != nil { - return xerrors.Errorf("can't marshal key: %w", err) + return fmt.Errorf("can't marshal key: %w", err) } if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { - return xerrors.Errorf("lookup and delete failed: %w", err) + return fmt.Errorf("lookup and delete failed: %w", err) } return unmarshalBytes(valueOut, valueBytes) @@ -345,7 +349,7 @@ func (m *Map) LookupBytes(key interface{}) ([]byte, error) { valuePtr := internal.NewSlicePointer(valueBytes) err := m.lookup(key, valuePtr) - if xerrors.Is(err, ErrKeyNotExist) { + if errors.Is(err, ErrKeyNotExist) { return nil, nil } @@ -355,11 +359,11 @@ func (m *Map) LookupBytes(key interface{}) ([]byte, error) { func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) if err != nil { - return xerrors.Errorf("can't marshal key: %w", err) + return fmt.Errorf("can't marshal key: %w", err) } if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil { - return xerrors.Errorf("lookup failed: %w", err) + return fmt.Errorf("lookup failed: %w", err) } return nil } @@ -389,7 +393,7 @@ func (m *Map) Put(key, value interface{}) error { func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) if err != nil { - return xerrors.Errorf("can't marshal key: %w", err) + return fmt.Errorf("can't marshal key: %w", err) } var valuePtr internal.Pointer @@ -399,11 +403,11 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { valuePtr, err = marshalPtr(value, int(m.abi.ValueSize)) } if err != nil { - return xerrors.Errorf("can't marshal value: %w", err) + return fmt.Errorf("can't marshal value: %w", err) } if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil { - return xerrors.Errorf("update failed: %w", err) + return fmt.Errorf("update failed: %w", err) } return nil @@ -415,11 +419,11 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { func (m *Map) Delete(key interface{}) error { keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) if err != nil { - return xerrors.Errorf("can't marshal key: %w", err) + return fmt.Errorf("can't marshal key: %w", err) } if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil { - return xerrors.Errorf("delete failed: %w", err) + return fmt.Errorf("delete failed: %w", err) } return nil } @@ -441,7 +445,7 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error { } if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil { - return xerrors.Errorf("can't unmarshal next key: %w", err) + return fmt.Errorf("can't unmarshal next key: %w", err) } return nil } @@ -458,7 +462,7 @@ func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { nextKeyPtr := internal.NewSlicePointer(nextKey) err := m.nextKey(key, nextKeyPtr) - if xerrors.Is(err, ErrKeyNotExist) { + if errors.Is(err, ErrKeyNotExist) { return nil, nil } @@ -474,12 +478,12 @@ func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { if key != nil { keyPtr, err = marshalPtr(key, int(m.abi.KeySize)) if err != nil { - return xerrors.Errorf("can't marshal key: %w", err) + return fmt.Errorf("can't marshal key: %w", err) } } if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil { - return xerrors.Errorf("next key failed: %w", err) + return fmt.Errorf("next key failed: %w", err) } return nil } @@ -532,7 +536,7 @@ func (m *Map) Clone() (*Map, error) { dup, err := m.fd.Dup() if err != nil { - return nil, xerrors.Errorf("can't clone map: %w", err) + return nil, fmt.Errorf("can't clone map: %w", err) } return newMap(dup, m.name, &m.abi) @@ -542,7 +546,7 @@ func (m *Map) Clone() (*Map, error) { // // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional func (m *Map) Pin(fileName string) error { - return bpfPinObject(fileName, m.fd) + return internal.BPFObjPin(fileName, m.fd) } // Freeze prevents a map to be modified from user space. @@ -550,11 +554,11 @@ func (m *Map) Pin(fileName string) error { // It makes no changes to kernel-side restrictions. func (m *Map) Freeze() error { if err := haveMapMutabilityModifiers(); err != nil { - return xerrors.Errorf("can't freeze map: %w", err) + return fmt.Errorf("can't freeze map: %w", err) } if err := bpfMapFreeze(m.fd); err != nil { - return xerrors.Errorf("can't freeze map: %w", err) + return fmt.Errorf("can't freeze map: %w", err) } return nil } @@ -562,7 +566,7 @@ func (m *Map) Freeze() error { func (m *Map) populate(contents []MapKV) error { for _, kv := range contents { if err := m.Put(kv.Key, kv.Value); err != nil { - return xerrors.Errorf("key %v: %w", kv.Key, err) + return fmt.Errorf("key %v: %w", kv.Key, err) } } return nil @@ -573,7 +577,7 @@ func (m *Map) populate(contents []MapKV) error { // The function is not compatible with nested maps. // Use LoadPinnedMapExplicit in these situations. func LoadPinnedMap(fileName string) (*Map, error) { - fd, err := bpfGetObject(fileName) + fd, err := internal.BPFObjGet(fileName) if err != nil { return nil, err } @@ -587,7 +591,7 @@ func LoadPinnedMap(fileName string) (*Map, error) { // LoadPinnedMapExplicit loads a map with explicit parameters. func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) { - fd, err := bpfGetObject(fileName) + fd, err := internal.BPFObjGet(fileName) if err != nil { return nil, err } @@ -596,7 +600,7 @@ func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) { func unmarshalMap(buf []byte) (*Map, error) { if len(buf) != 4 { - return nil, xerrors.New("map id requires 4 byte value") + return nil, errors.New("map id requires 4 byte value") } // Looking up an entry in a nested map or prog array returns an id, @@ -621,12 +625,12 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) replaced := make(map[string]bool) replace := func(name string, offset, size int, replacement interface{}) error { if offset+size > len(value) { - return xerrors.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) + return fmt.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) } buf, err := marshalBytes(replacement, size) if err != nil { - return xerrors.Errorf("marshal %s: %w", name, err) + return fmt.Errorf("marshal %s: %w", name, err) } copy(value[offset:offset+size], buf) @@ -650,7 +654,7 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) } default: - return xerrors.Errorf("patching %T is not supported", typ) + return fmt.Errorf("patching %T is not supported", typ) } if len(replaced) == len(replacements) { @@ -665,10 +669,10 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) } if len(missing) == 1 { - return xerrors.Errorf("unknown field: %s", missing[0]) + return fmt.Errorf("unknown field: %s", missing[0]) } - return xerrors.Errorf("unknown fields: %s", strings.Join(missing, ",")) + return fmt.Errorf("unknown fields: %s", strings.Join(missing, ",")) } // MapIterator iterates a Map. @@ -726,7 +730,7 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { mi.prevKey = mi.prevBytes mi.err = mi.target.Lookup(nextBytes, valueOut) - if xerrors.Is(mi.err, ErrKeyNotExist) { + if errors.Is(mi.err, ErrKeyNotExist) { // Even though the key should be valid, we couldn't look up // its value. If we're iterating a hash map this is probably // because a concurrent delete removed the value before we @@ -745,7 +749,7 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { return mi.err == nil } - mi.err = xerrors.Errorf("%w", ErrIterationAborted) + mi.err = fmt.Errorf("%w", ErrIterationAborted) return false } @@ -762,7 +766,7 @@ func (mi *MapIterator) Err() error { // // Returns ErrNotExist, if there is no next eBPF map. func MapGetNextID(startID MapID) (MapID, error) { - id, err := objGetNextID(_MapGetNextID, uint32(startID)) + id, err := objGetNextID(internal.BPF_MAP_GET_NEXT_ID, uint32(startID)) return MapID(id), err } @@ -770,7 +774,7 @@ func MapGetNextID(startID MapID) (MapID, error) { // // Returns ErrNotExist, if there is no eBPF map with the given id. func NewMapFromID(id MapID) (*Map, error) { - fd, err := bpfObjGetFDByID(_MapGetFDByID, uint32(id)) + fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go index 89fdc86028c..c0db2f6b0b1 100644 --- a/vendor/github.com/cilium/ebpf/marshalers.go +++ b/vendor/github.com/cilium/ebpf/marshalers.go @@ -4,13 +4,13 @@ import ( "bytes" "encoding" "encoding/binary" + "errors" + "fmt" "reflect" "runtime" "unsafe" "github.com/cilium/ebpf/internal" - - "golang.org/x/xerrors" ) func marshalPtr(data interface{}, length int) (internal.Pointer, error) { @@ -18,7 +18,7 @@ func marshalPtr(data interface{}, length int) (internal.Pointer, error) { if length == 0 { return internal.NewPointer(nil), nil } - return internal.Pointer{}, xerrors.New("can't use nil as key of map") + return internal.Pointer{}, errors.New("can't use nil as key of map") } if ptr, ok := data.(unsafe.Pointer); ok { @@ -42,12 +42,12 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) { case []byte: buf = value case unsafe.Pointer: - err = xerrors.New("can't marshal from unsafe.Pointer") + err = errors.New("can't marshal from unsafe.Pointer") default: var wr bytes.Buffer err = binary.Write(&wr, internal.NativeEndian, value) if err != nil { - err = xerrors.Errorf("encoding %T: %v", value, err) + err = fmt.Errorf("encoding %T: %v", value, err) } buf = wr.Bytes() } @@ -56,7 +56,7 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) { } if len(buf) != length { - return nil, xerrors.Errorf("%T doesn't marshal to %d bytes", data, length) + return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length) } return buf, nil } @@ -92,13 +92,13 @@ func unmarshalBytes(data interface{}, buf []byte) error { *value = buf return nil case string: - return xerrors.New("require pointer to string") + return errors.New("require pointer to string") case []byte: - return xerrors.New("require pointer to []byte") + return errors.New("require pointer to []byte") default: rd := bytes.NewReader(buf) if err := binary.Read(rd, internal.NativeEndian, value); err != nil { - return xerrors.Errorf("decoding %T: %v", value, err) + return fmt.Errorf("decoding %T: %v", value, err) } return nil } @@ -113,7 +113,7 @@ func unmarshalBytes(data interface{}, buf []byte) error { func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { - return internal.Pointer{}, xerrors.New("per-CPU value requires slice") + return internal.Pointer{}, errors.New("per-CPU value requires slice") } possibleCPUs, err := internal.PossibleCPUs() @@ -124,7 +124,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er sliceValue := reflect.ValueOf(slice) sliceLen := sliceValue.Len() if sliceLen > possibleCPUs { - return internal.Pointer{}, xerrors.Errorf("per-CPU value exceeds number of CPUs") + return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs") } alignedElemLength := align(elemLength, 8) @@ -151,7 +151,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error { slicePtrType := reflect.TypeOf(slicePtr) if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { - return xerrors.Errorf("per-cpu value requires pointer to slice") + return fmt.Errorf("per-cpu value requires pointer to slice") } possibleCPUs, err := internal.PossibleCPUs() @@ -170,7 +170,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro step := len(buf) / possibleCPUs if step < elemLength { - return xerrors.Errorf("per-cpu element length is larger than available data") + return fmt.Errorf("per-cpu element length is larger than available data") } for i := 0; i < possibleCPUs; i++ { var elem interface{} @@ -188,7 +188,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro err := unmarshalBytes(elem, elemBytes) if err != nil { - return xerrors.Errorf("cpu %d: %w", i, err) + return fmt.Errorf("cpu %d: %w", i, err) } buf = buf[step:] diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index 60860a2bb24..429203f07e8 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -2,18 +2,17 @@ package ebpf import ( "bytes" + "encoding/binary" + "errors" "fmt" "math" "strings" "time" - "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) // ErrNotSupported is returned whenever the kernel doesn't support a feature. @@ -47,17 +46,33 @@ type ProgramOptions struct { type ProgramSpec struct { // Name is passed to the kernel as a debug aid. Must only contain // alpha numeric and '_' characters. - Name string - Type ProgramType - AttachType AttachType - Instructions asm.Instructions - License string + Name string + // Type determines at which hook in the kernel a program will run. + Type ProgramType + AttachType AttachType + // Name of a kernel data structure to attach to. It's interpretation + // depends on Type and AttachType. + AttachTo string + Instructions asm.Instructions + + // License of the program. Some helpers are only available if + // the license is deemed compatible with the GPL. + // + // See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1 + License string + + // Version used by tracing programs. + // + // Deprecated: superseded by BTF. KernelVersion uint32 // The BTF associated with this program. Changing Instructions // will most likely invalidate the contained data, and may // result in errors when attempting to load it into the kernel. BTF *btf.Program + + // The byte order this program was compiled for, may be nil. + ByteOrder binary.ByteOrder } // Copy returns a copy of the spec. @@ -80,9 +95,10 @@ type Program struct { // otherwise it is empty. VerifierLog string - fd *internal.FD - name string - abi ProgramABI + fd *internal.FD + name string + abi ProgramABI + attachType AttachType } // NewProgram creates a new Program. @@ -103,8 +119,8 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er } handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF)) - if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { - return nil, xerrors.Errorf("can't load BTF: %w", err) + if err != nil && !errors.Is(err, btf.ErrNotSupported) { + return nil, fmt.Errorf("can't load BTF: %w", err) } return newProgramWithBTF(spec, handle, opts) @@ -148,7 +164,7 @@ func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions) } err = internal.ErrorWithLog(err, logBuf, logErr) - return nil, xerrors.Errorf("can't load program: %w", err) + return nil, fmt.Errorf("can't load program: %w", err) } // NewProgramFromFD creates a program from a raw fd. @@ -158,7 +174,7 @@ func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions) // Requires at least Linux 4.11. func NewProgramFromFD(fd int) (*Program, error) { if fd < 0 { - return nil, xerrors.New("invalid fd") + return nil, errors.New("invalid fd") } bpfFd := internal.NewFD(uint32(fd)) @@ -181,11 +197,15 @@ func newProgram(fd *internal.FD, name string, abi *ProgramABI) *Program { func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) { if len(spec.Instructions) == 0 { - return nil, xerrors.New("Instructions cannot be empty") + return nil, errors.New("Instructions cannot be empty") } if len(spec.License) == 0 { - return nil, xerrors.New("License cannot be empty") + return nil, errors.New("License cannot be empty") + } + + if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { + return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian) } buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize)) @@ -214,7 +234,7 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) if err != nil { - return nil, xerrors.Errorf("can't get BTF line infos: %w", err) + return nil, fmt.Errorf("can't get BTF line infos: %w", err) } attr.lineInfoRecSize = recSize attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) @@ -222,13 +242,23 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) if err != nil { - return nil, xerrors.Errorf("can't get BTF function infos: %w", err) + return nil, fmt.Errorf("can't get BTF function infos: %w", err) } attr.funcInfoRecSize = recSize attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.funcInfo = internal.NewSlicePointer(bytes) } + if spec.AttachTo != "" { + target, err := resolveBTFType(spec.AttachTo, spec.Type, spec.AttachType) + if err != nil { + return nil, err + } + if target != nil { + attr.attachBTFID = target.ID() + } + } + return attr, nil } @@ -270,7 +300,7 @@ func (p *Program) Clone() (*Program, error) { dup, err := p.fd.Dup() if err != nil { - return nil, xerrors.Errorf("can't clone program: %w", err) + return nil, fmt.Errorf("can't clone program: %w", err) } return newProgram(dup, p.name, &p.abi), nil @@ -280,8 +310,8 @@ func (p *Program) Clone() (*Program, error) { // // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional func (p *Program) Pin(fileName string) error { - if err := bpfPinObject(fileName, p.fd); err != nil { - return xerrors.Errorf("can't pin program: %w", err) + if err := internal.BPFObjPin(fileName, p.fd); err != nil { + return fmt.Errorf("can't pin program: %w", err) } return nil } @@ -305,7 +335,7 @@ func (p *Program) Close() error { func (p *Program) Test(in []byte) (uint32, []byte, error) { ret, out, _, err := p.testRun(in, 1, nil) if err != nil { - return ret, nil, xerrors.Errorf("can't test program: %w", err) + return ret, nil, fmt.Errorf("can't test program: %w", err) } return ret, out, nil } @@ -324,12 +354,12 @@ func (p *Program) Test(in []byte) (uint32, []byte, error) { func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { ret, _, total, err := p.testRun(in, repeat, reset) if err != nil { - return ret, total, xerrors.Errorf("can't benchmark program: %w", err) + return ret, total, fmt.Errorf("can't benchmark program: %w", err) } return ret, total, nil } -var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool { +var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() (bool, error) { prog, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ @@ -340,28 +370,23 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() b }) if err != nil { // This may be because we lack sufficient permissions, etc. - return false + return false, err } defer prog.Close() - fd, err := prog.fd.Value() - if err != nil { - return false - } - // Programs require at least 14 bytes input in := make([]byte, 14) attr := bpfProgTestRunAttr{ - fd: fd, + fd: uint32(prog.FD()), dataSizeIn: uint32(len(in)), dataIn: internal.NewSlicePointer(in), } - _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + err = bpfProgTestRun(&attr) // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. - return !xerrors.Is(err, unix.EINVAL) + return !errors.Is(err, unix.EINVAL), nil }) func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) { @@ -403,19 +428,19 @@ func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, } for { - _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + err = bpfProgTestRun(&attr) if err == nil { break } - if xerrors.Is(err, unix.EINTR) { + if errors.Is(err, unix.EINTR) { if reset != nil { reset() } continue } - return 0, nil, 0, xerrors.Errorf("can't run test: %w", err) + return 0, nil, 0, fmt.Errorf("can't run test: %w", err) } if int(attr.dataSizeOut) > cap(out) { @@ -431,7 +456,7 @@ func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, func unmarshalProgram(buf []byte) (*Program, error) { if len(buf) != 4 { - return nil, xerrors.New("program id requires 4 byte value") + return nil, errors.New("program id requires 4 byte value") } // Looking up an entry in a nested map or prog array returns an id, @@ -452,10 +477,12 @@ func (p *Program) MarshalBinary() ([]byte, error) { return buf, nil } -// Attach a Program to a container object fd +// Attach a Program. +// +// Deprecated: use link.RawAttachProgram instead. func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { if fd < 0 { - return xerrors.New("invalid fd") + return errors.New("invalid fd") } pfd, err := p.fd.Value() @@ -463,20 +490,26 @@ func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { return err } - attr := bpfProgAlterAttr{ - targetFd: uint32(fd), - attachBpfFd: pfd, - attachType: uint32(typ), - attachFlags: uint32(flags), + attr := internal.BPFProgAttachAttr{ + TargetFd: uint32(fd), + AttachBpfFd: pfd, + AttachType: uint32(typ), + AttachFlags: uint32(flags), } - return bpfProgAlter(_ProgAttach, &attr) + return internal.BPFProgAttach(&attr) } -// Detach a Program from a container object fd +// Detach a Program. +// +// Deprecated: use link.RawDetachProgram instead. func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { if fd < 0 { - return xerrors.New("invalid fd") + return errors.New("invalid fd") + } + + if flags != 0 { + return errors.New("flags must be zero") } pfd, err := p.fd.Value() @@ -484,21 +517,20 @@ func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { return err } - attr := bpfProgAlterAttr{ - targetFd: uint32(fd), - attachBpfFd: pfd, - attachType: uint32(typ), - attachFlags: uint32(flags), + attr := internal.BPFProgDetachAttr{ + TargetFd: uint32(fd), + AttachBpfFd: pfd, + AttachType: uint32(typ), } - return bpfProgAlter(_ProgDetach, &attr) + return internal.BPFProgDetach(&attr) } // LoadPinnedProgram loads a Program from a BPF file. // // Requires at least Linux 4.11. func LoadPinnedProgram(fileName string) (*Program, error) { - fd, err := bpfGetObject(fileName) + fd, err := internal.BPFObjGet(fileName) if err != nil { return nil, err } @@ -506,7 +538,7 @@ func LoadPinnedProgram(fileName string) (*Program, error) { name, abi, err := newProgramABIFromFd(fd) if err != nil { _ = fd.Close() - return nil, xerrors.Errorf("can't get ABI for %s: %w", fileName, err) + return nil, fmt.Errorf("can't get ABI for %s: %w", fileName, err) } return newProgram(fd, name, abi), nil @@ -532,7 +564,7 @@ func SanitizeName(name string, replacement rune) string { // // Returns ErrNotExist, if there is no next eBPF program. func ProgramGetNextID(startID ProgramID) (ProgramID, error) { - id, err := objGetNextID(_ProgGetNextID, uint32(startID)) + id, err := objGetNextID(internal.BPF_PROG_GET_NEXT_ID, uint32(startID)) return ProgramID(id), err } @@ -540,7 +572,7 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) { // // Returns ErrNotExist, if there is no eBPF program with the given id. func NewProgramFromID(id ProgramID) (*Program, error) { - fd, err := bpfObjGetFDByID(_ProgGetFDByID, uint32(id)) + fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, err } @@ -562,3 +594,29 @@ func (p *Program) ID() (ProgramID, error) { } return ProgramID(info.id), nil } + +func resolveBTFType(name string, progType ProgramType, attachType AttachType) (btf.Type, error) { + kernel, err := btf.LoadKernelSpec() + if err != nil { + return nil, fmt.Errorf("can't resolve BTF type %s: %w", name, err) + } + + type match struct { + p ProgramType + a AttachType + } + + target := match{progType, attachType} + switch target { + case match{Tracing, AttachTraceIter}: + var target btf.Func + if err := kernel.FindType("bpf_iter_"+name, &target); err != nil { + return nil, fmt.Errorf("can't resolve BTF for iterator %s: %w", name, err) + } + + return &target, nil + + default: + return nil, nil + } +} diff --git a/vendor/github.com/cilium/ebpf/run-tests.sh b/vendor/github.com/cilium/ebpf/run-tests.sh index e60c35e2531..c43a90ddd0d 100644 --- a/vendor/github.com/cilium/ebpf/run-tests.sh +++ b/vendor/github.com/cilium/ebpf/run-tests.sh @@ -15,8 +15,13 @@ if [[ "${1:-}" = "--in-vm" ]]; then export GOPROXY=file:///run/go-root/pkg/mod/cache/download export GOCACHE=/run/go-cache + elfs="" + if [[ -d "/run/input/bpf" ]]; then + elfs="/run/input/bpf" + fi + echo Running tests... - /usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v ./... + /usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v -elfs "$elfs" ./... touch "$1/success" exit 0 fi @@ -39,20 +44,34 @@ if [[ -z "${kernel_version}" ]]; then fi readonly kernel="linux-${kernel_version}.bz" +readonly selftests="linux-${kernel_version}-selftests-bpf.bz" +readonly input="$(mktemp -d)" readonly output="$(mktemp -d)" -readonly tmp_dir="${TMPDIR:-$(mktemp -d)}" +readonly tmp_dir="${TMPDIR:-/tmp}" +readonly branch="${BRANCH:-master}" -test -e "${tmp_dir}/${kernel}" || { - echo Fetching "${kernel}" - curl --fail -L "https://github.com/cilium/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}" +fetch() { + echo Fetching "${1}" + wget -nv -N -P "${tmp_dir}" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}" } +fetch "${kernel}" + +if fetch "${selftests}"; then + mkdir "${input}/bpf" + tar --strip-components=4 -xjf "${tmp_dir}/${selftests}" -C "${input}/bpf" +else + echo "No selftests found, disabling" +fi + echo Testing on "${kernel_version}" $sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \ + --rwdir=/run/input="${input}" \ --rwdir=/run/output="${output}" \ --rodir=/run/go-path="$(go env GOPATH)" \ --rwdir=/run/go-cache="$(go env GOCACHE)" \ - --script-sh "$(realpath "$0") --in-vm /run/output" + --script-sh "$(realpath "$0") --in-vm /run/output" \ + --qemu-opts -smp 2 # need at least two CPUs for some tests if [[ ! -e "${output}/success" ]]; then echo "Test failed on ${kernel_version}" @@ -66,4 +85,5 @@ else fi fi +$sudo rm -r "${input}" $sudo rm -r "${output}" diff --git a/vendor/github.com/cilium/ebpf/syscalls.go b/vendor/github.com/cilium/ebpf/syscalls.go index 20de356df27..2b713d06a0c 100644 --- a/vendor/github.com/cilium/ebpf/syscalls.go +++ b/vendor/github.com/cilium/ebpf/syscalls.go @@ -1,19 +1,19 @@ package ebpf import ( - "path/filepath" + "errors" + "fmt" + "os" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" - - "golang.org/x/xerrors" ) // Generic errors returned by BPF syscalls. var ( - ErrNotExist = xerrors.New("requested object does not exit") + ErrNotExist = errors.New("requested object does not exist") ) // bpfObjName is a null-terminated string made up of @@ -79,12 +79,6 @@ type bpfMapInfo struct { mapName bpfObjName // since 4.15 ad5b177bd73f } -type bpfPinObjAttr struct { - fileName internal.Pointer - fd uint32 - padding uint32 -} - type bpfProgLoadAttr struct { progType ProgramType insCount uint32 @@ -105,6 +99,8 @@ type bpfProgLoadAttr struct { lineInfoRecSize uint32 lineInfo internal.Pointer lineInfoCnt uint32 + attachBTFID btf.TypeID + attachProgFd uint32 } type bpfProgInfo struct { @@ -133,13 +129,6 @@ type bpfProgTestRunAttr struct { duration uint32 } -type bpfProgAlterAttr struct { - targetFd uint32 - attachBpfFd uint32 - attachType uint32 - attachFlags uint32 -} - type bpfObjGetInfoByFDAttr struct { fd uint32 infoLen uint32 @@ -163,7 +152,7 @@ type bpfObjGetNextIDAttr struct { func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { for { - fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) // As of ~4.20 the verifier can be interrupted by a signal, // and returns EAGAIN in that case. if err == unix.EAGAIN { @@ -178,13 +167,17 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { } } -func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error { - _, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) +func bpfProgTestRun(attr *bpfProgTestRunAttr) error { + _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) { - fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + fd, err := internal.BPF(internal.BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if errors.Is(err, os.ErrPermission) { + return nil, errors.New("permission denied or insufficient rlimit to lock memory for map") + } + if err != nil { return nil, err } @@ -192,7 +185,7 @@ func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) { return internal.NewFD(uint32(fd)), nil } -var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { +var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() (bool, error) { inner, err := bpfMapCreate(&bpfMapCreateAttr{ mapType: Array, keySize: 4, @@ -200,7 +193,7 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { maxEntries: 1, }) if err != nil { - return false + return false, err } defer inner.Close() @@ -213,14 +206,14 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { innerMapFd: innerFd, }) if err != nil { - return false + return false, nil } _ = nested.Close() - return true + return true, nil }) -var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() bool { +var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() (bool, error) { // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. m, err := bpfMapCreate(&bpfMapCreateAttr{ @@ -231,10 +224,10 @@ var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps flags: unix.BPF_F_RDONLY_PROG, }) if err != nil { - return false + return false, nil } _ = m.Close() - return true + return true, nil }) func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { @@ -248,7 +241,7 @@ func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { key: key, value: valueOut, } - _, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } @@ -263,7 +256,7 @@ func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error key: key, value: valueOut, } - _, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } @@ -279,7 +272,7 @@ func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint value: valueOut, flags: flags, } - _, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } @@ -293,7 +286,7 @@ func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error { mapFd: fd, key: key, } - _, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } @@ -308,11 +301,11 @@ func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error { key: key, value: nextKeyOut, } - _, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } -func objGetNextID(cmd int, start uint32) (uint32, error) { +func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) { attr := bpfObjGetNextIDAttr{ startID: start, } @@ -324,11 +317,11 @@ func wrapObjError(err error) error { if err == nil { return nil } - if xerrors.Is(err, unix.ENOENT) { - return xerrors.Errorf("%w", ErrNotExist) + if errors.Is(err, unix.ENOENT) { + return fmt.Errorf("%w", ErrNotExist) } - return xerrors.New(err.Error()) + return errors.New(err.Error()) } func wrapMapError(err error) error { @@ -336,11 +329,15 @@ func wrapMapError(err error) error { return nil } - if xerrors.Is(err, unix.ENOENT) { + if errors.Is(err, unix.ENOENT) { return ErrKeyNotExist } - return xerrors.New(err.Error()) + if errors.Is(err, unix.EEXIST) { + return ErrKeyExist + } + + return errors.New(err.Error()) } func bpfMapFreeze(m *internal.FD) error { @@ -352,47 +349,10 @@ func bpfMapFreeze(m *internal.FD) error { attr := bpfMapFreezeAttr{ mapFd: fd, } - _, err = internal.BPF(_MapFreeze, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } -const bpfFSType = 0xcafe4a11 - -func bpfPinObject(fileName string, fd *internal.FD) error { - dirName := filepath.Dir(fileName) - var statfs unix.Statfs_t - if err := unix.Statfs(dirName, &statfs); err != nil { - return err - } - if uint64(statfs.Type) != bpfFSType { - return xerrors.Errorf("%s is not on a bpf filesystem", fileName) - } - - value, err := fd.Value() - if err != nil { - return err - } - - _, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{ - fileName: internal.NewStringPointer(fileName), - fd: value, - }), 16) - if err != nil { - return xerrors.Errorf("pin object %s: %w", fileName, err) - } - return nil -} - -func bpfGetObject(fileName string) (*internal.FD, error) { - ptr, err := internal.BPF(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{ - fileName: internal.NewStringPointer(fileName), - }), 16) - if err != nil { - return nil, xerrors.Errorf("get object %s: %w", fileName, err) - } - return internal.NewFD(uint32(ptr)), nil -} - func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error { value, err := fd.Value() if err != nil { @@ -405,9 +365,9 @@ func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) er infoLen: uint32(size), info: internal.NewPointer(info), } - _, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(internal.BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { - return xerrors.Errorf("fd %d: %w", fd, err) + return fmt.Errorf("fd %d: %w", fd, err) } return nil } @@ -415,7 +375,7 @@ func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) er func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { var info bpfProgInfo if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { - return nil, xerrors.Errorf("can't get program info: %w", err) + return nil, fmt.Errorf("can't get program info: %w", err) } return &info, nil } @@ -424,12 +384,12 @@ func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) { var info bpfMapInfo err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) if err != nil { - return nil, xerrors.Errorf("can't get map info: %w", err) + return nil, fmt.Errorf("can't get map info: %w", err) } return &info, nil } -var haveObjName = internal.FeatureTest("object names", "4.15", func() bool { +var haveObjName = internal.FeatureTest("object names", "4.15", func() (bool, error) { attr := bpfMapCreateAttr{ mapType: Array, keySize: 4, @@ -440,16 +400,16 @@ var haveObjName = internal.FeatureTest("object names", "4.15", func() bool { fd, err := bpfMapCreate(&attr) if err != nil { - return false + return false, nil } _ = fd.Close() - return true + return true, nil }) -var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool { +var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() (bool, error) { if err := haveObjName(); err != nil { - return false + return false, err } attr := bpfMapCreateAttr{ @@ -462,14 +422,14 @@ var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() fd, err := bpfMapCreate(&attr) if err != nil { - return false + return false, nil } _ = fd.Close() - return true + return true, nil }) -func bpfObjGetFDByID(cmd int, id uint32) (*internal.FD, error) { +func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) { attr := bpfGetFDByIDAttr{ id: id, } diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 94cdb30814b..1ffc62123be 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -1,6 +1,6 @@ package ebpf -//go:generate stringer -output types_string.go -type=MapType,ProgramType +//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType // MapType indicates the type map structure // that will be initialized in the kernel. @@ -85,39 +85,12 @@ const ( // hasPerCPUValue returns true if the Map stores a value per CPU. func (mt MapType) hasPerCPUValue() bool { - if mt == PerCPUHash || mt == PerCPUArray { + if mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash { return true } return false } -const ( - _MapCreate = iota - _MapLookupElem - _MapUpdateElem - _MapDeleteElem - _MapGetNextKey - _ProgLoad - _ObjPin - _ObjGet - _ProgAttach - _ProgDetach - _ProgTestRun - _ProgGetNextID - _MapGetNextID - _ProgGetFDByID - _MapGetFDByID - _ObjGetInfoByFD - _ProgQuery - _RawTracepointOpen - _BTFLoad - _BTFGetFDByID - _TaskFDQuery - _MapLookupAndDeleteElem - _MapFreeze - _BTFGetNextID -) - // ProgramType of the eBPF program type ProgramType uint32 @@ -214,6 +187,9 @@ const ( AttachTraceRawTp AttachTraceFEntry AttachTraceFExit + AttachModifyReturn + AttachLSMMac + AttachTraceIter ) // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go index f41ba77df90..c7139578ec6 100644 --- a/vendor/github.com/cilium/ebpf/types_string.go +++ b/vendor/github.com/cilium/ebpf/types_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -output types_string.go -type=MapType,ProgramType"; DO NOT EDIT. +// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType"; DO NOT EDIT. package ebpf @@ -89,3 +89,49 @@ func (i ProgramType) String() string { } return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] } +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[AttachNone-0] + _ = x[AttachCGroupInetIngress-0] + _ = x[AttachCGroupInetEgress-1] + _ = x[AttachCGroupInetSockCreate-2] + _ = x[AttachCGroupSockOps-3] + _ = x[AttachSkSKBStreamParser-4] + _ = x[AttachSkSKBStreamVerdict-5] + _ = x[AttachCGroupDevice-6] + _ = x[AttachSkMsgVerdict-7] + _ = x[AttachCGroupInet4Bind-8] + _ = x[AttachCGroupInet6Bind-9] + _ = x[AttachCGroupInet4Connect-10] + _ = x[AttachCGroupInet6Connect-11] + _ = x[AttachCGroupInet4PostBind-12] + _ = x[AttachCGroupInet6PostBind-13] + _ = x[AttachCGroupUDP4Sendmsg-14] + _ = x[AttachCGroupUDP6Sendmsg-15] + _ = x[AttachLircMode2-16] + _ = x[AttachFlowDissector-17] + _ = x[AttachCGroupSysctl-18] + _ = x[AttachCGroupUDP4Recvmsg-19] + _ = x[AttachCGroupUDP6Recvmsg-20] + _ = x[AttachCGroupGetsockopt-21] + _ = x[AttachCGroupSetsockopt-22] + _ = x[AttachTraceRawTp-23] + _ = x[AttachTraceFEntry-24] + _ = x[AttachTraceFExit-25] + _ = x[AttachModifyReturn-26] + _ = x[AttachLSMMac-27] + _ = x[AttachTraceIter-28] +} + +const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIter" + +var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582} + +func (i AttachType) String() string { + if i >= AttachType(len(_AttachType_index)-1) { + return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]] +} diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE deleted file mode 100644 index e4a47e17f14..00000000000 --- a/vendor/golang.org/x/xerrors/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2019 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS deleted file mode 100644 index 733099041f8..00000000000 --- a/vendor/golang.org/x/xerrors/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README deleted file mode 100644 index aac7867a560..00000000000 --- a/vendor/golang.org/x/xerrors/README +++ /dev/null @@ -1,2 +0,0 @@ -This repository holds the transition packages for the new Go 1.13 error values. -See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go deleted file mode 100644 index 4317f248331..00000000000 --- a/vendor/golang.org/x/xerrors/adaptor.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "bytes" - "fmt" - "io" - "reflect" - "strconv" -) - -// FormatError calls the FormatError method of f with an errors.Printer -// configured according to s and verb, and writes the result to s. -func FormatError(f Formatter, s fmt.State, verb rune) { - // Assuming this function is only called from the Format method, and given - // that FormatError takes precedence over Format, it cannot be called from - // any package that supports errors.Formatter. It is therefore safe to - // disregard that State may be a specific printer implementation and use one - // of our choice instead. - - // limitations: does not support printing error as Go struct. - - var ( - sep = " " // separator before next error - p = &state{State: s} - direct = true - ) - - var err error = f - - switch verb { - // Note that this switch must match the preference order - // for ordinary string printing (%#v before %+v, and so on). - - case 'v': - if s.Flag('#') { - if stringer, ok := err.(fmt.GoStringer); ok { - io.WriteString(&p.buf, stringer.GoString()) - goto exit - } - // proceed as if it were %v - } else if s.Flag('+') { - p.printDetail = true - sep = "\n - " - } - case 's': - case 'q', 'x', 'X': - // Use an intermediate buffer in the rare cases that precision, - // truncation, or one of the alternative verbs (q, x, and X) are - // specified. - direct = false - - default: - p.buf.WriteString("%!") - p.buf.WriteRune(verb) - p.buf.WriteByte('(') - switch { - case err != nil: - p.buf.WriteString(reflect.TypeOf(f).String()) - default: - p.buf.WriteString("") - } - p.buf.WriteByte(')') - io.Copy(s, &p.buf) - return - } - -loop: - for { - switch v := err.(type) { - case Formatter: - err = v.FormatError((*printer)(p)) - case fmt.Formatter: - v.Format(p, 'v') - break loop - default: - io.WriteString(&p.buf, v.Error()) - break loop - } - if err == nil { - break - } - if p.needColon || !p.printDetail { - p.buf.WriteByte(':') - p.needColon = false - } - p.buf.WriteString(sep) - p.inDetail = false - p.needNewline = false - } - -exit: - width, okW := s.Width() - prec, okP := s.Precision() - - if !direct || (okW && width > 0) || okP { - // Construct format string from State s. - format := []byte{'%'} - if s.Flag('-') { - format = append(format, '-') - } - if s.Flag('+') { - format = append(format, '+') - } - if s.Flag(' ') { - format = append(format, ' ') - } - if okW { - format = strconv.AppendInt(format, int64(width), 10) - } - if okP { - format = append(format, '.') - format = strconv.AppendInt(format, int64(prec), 10) - } - format = append(format, string(verb)...) - fmt.Fprintf(s, string(format), p.buf.String()) - } else { - io.Copy(s, &p.buf) - } -} - -var detailSep = []byte("\n ") - -// state tracks error printing state. It implements fmt.State. -type state struct { - fmt.State - buf bytes.Buffer - - printDetail bool - inDetail bool - needColon bool - needNewline bool -} - -func (s *state) Write(b []byte) (n int, err error) { - if s.printDetail { - if len(b) == 0 { - return 0, nil - } - if s.inDetail && s.needColon { - s.needNewline = true - if b[0] == '\n' { - b = b[1:] - } - } - k := 0 - for i, c := range b { - if s.needNewline { - if s.inDetail && s.needColon { - s.buf.WriteByte(':') - s.needColon = false - } - s.buf.Write(detailSep) - s.needNewline = false - } - if c == '\n' { - s.buf.Write(b[k:i]) - k = i + 1 - s.needNewline = true - } - } - s.buf.Write(b[k:]) - if !s.inDetail { - s.needColon = true - } - } else if !s.inDetail { - s.buf.Write(b) - } - return len(b), nil -} - -// printer wraps a state to implement an xerrors.Printer. -type printer state - -func (s *printer) Print(args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprint((*state)(s), args...) - } -} - -func (s *printer) Printf(format string, args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprintf((*state)(s), format, args...) - } -} - -func (s *printer) Detail() bool { - s.inDetail = true - return s.printDetail -} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg deleted file mode 100644 index 3f8b14b64e8..00000000000 --- a/vendor/golang.org/x/xerrors/codereview.cfg +++ /dev/null @@ -1 +0,0 @@ -issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go deleted file mode 100644 index eef99d9d54d..00000000000 --- a/vendor/golang.org/x/xerrors/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package xerrors implements functions to manipulate errors. -// -// This package is based on the Go 2 proposal for error values: -// https://golang.org/design/29934-error-values -// -// These functions were incorporated into the standard library's errors package -// in Go 1.13: -// - Is -// - As -// - Unwrap -// -// Also, Errorf's %w verb was incorporated into fmt.Errorf. -// -// Use this package to get equivalent behavior in all supported Go versions. -// -// No other features of this package were included in Go 1.13, and at present -// there are no plans to include any of them. -package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go deleted file mode 100644 index e88d3772d86..00000000000 --- a/vendor/golang.org/x/xerrors/errors.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import "fmt" - -// errorString is a trivial implementation of error. -type errorString struct { - s string - frame Frame -} - -// New returns an error that formats as the given text. -// -// The returned error contains a Frame set to the caller's location and -// implements Formatter to show this information when printed with details. -func New(text string) error { - return &errorString{text, Caller(1)} -} - -func (e *errorString) Error() string { - return e.s -} - -func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *errorString) FormatError(p Printer) (next error) { - p.Print(e.s) - e.frame.Format(p) - return nil -} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go deleted file mode 100644 index 829862ddf6a..00000000000 --- a/vendor/golang.org/x/xerrors/fmt.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "fmt" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/xerrors/internal" -) - -const percentBangString = "%!" - -// Errorf formats according to a format specifier and returns the string as a -// value that satisfies error. -// -// The returned error includes the file and line number of the caller when -// formatted with additional detail enabled. If the last argument is an error -// the returned error's Format method will return it if the format string ends -// with ": %s", ": %v", or ": %w". If the last argument is an error and the -// format string ends with ": %w", the returned error implements an Unwrap -// method returning it. -// -// If the format specifier includes a %w verb with an error operand in a -// position other than at the end, the returned error will still implement an -// Unwrap method returning the operand, but the error's Format method will not -// return the wrapped error. -// -// It is invalid to include more than one %w verb or to supply it with an -// operand that does not implement the error interface. The %w verb is otherwise -// a synonym for %v. -func Errorf(format string, a ...interface{}) error { - format = formatPlusW(format) - // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. - wrap := strings.HasSuffix(format, ": %w") - idx, format2, ok := parsePercentW(format) - percentWElsewhere := !wrap && idx >= 0 - if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { - err := errorAt(a, len(a)-1) - if err == nil { - return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} - } - // TODO: this is not entirely correct. The error value could be - // printed elsewhere in format if it mixes numbered with unnumbered - // substitutions. With relatively small changes to doPrintf we can - // have it optionally ignore extra arguments and pass the argument - // list in its entirety. - msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - if wrap { - return &wrapError{msg, err, frame} - } - return &noWrapError{msg, err, frame} - } - // Support %w anywhere. - // TODO: don't repeat the wrapped error's message when %w occurs in the middle. - msg := fmt.Sprintf(format2, a...) - if idx < 0 { - return &noWrapError{msg, nil, Caller(1)} - } - err := errorAt(a, idx) - if !ok || err == nil { - // Too many %ws or argument of %w is not an error. Approximate the Go - // 1.13 fmt.Errorf message. - return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} - } - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - return &wrapError{msg, err, frame} -} - -func errorAt(args []interface{}, i int) error { - if i < 0 || i >= len(args) { - return nil - } - err, ok := args[i].(error) - if !ok { - return nil - } - return err -} - -// formatPlusW is used to avoid the vet check that will barf at %w. -func formatPlusW(s string) string { - return s -} - -// Return the index of the only %w in format, or -1 if none. -// Also return a rewritten format string with %w replaced by %v, and -// false if there is more than one %w. -// TODO: handle "%[N]w". -func parsePercentW(format string) (idx int, newFormat string, ok bool) { - // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. - idx = -1 - ok = true - n := 0 - sz := 0 - var isW bool - for i := 0; i < len(format); i += sz { - if format[i] != '%' { - sz = 1 - continue - } - // "%%" is not a format directive. - if i+1 < len(format) && format[i+1] == '%' { - sz = 2 - continue - } - sz, isW = parsePrintfVerb(format[i:]) - if isW { - if idx >= 0 { - ok = false - } else { - idx = n - } - // "Replace" the last character, the 'w', with a 'v'. - p := i + sz - 1 - format = format[:p] + "v" + format[p+1:] - } - n++ - } - return idx, format, ok -} - -// Parse the printf verb starting with a % at s[0]. -// Return how many bytes it occupies and whether the verb is 'w'. -func parsePrintfVerb(s string) (int, bool) { - // Assume only that the directive is a sequence of non-letters followed by a single letter. - sz := 0 - var r rune - for i := 1; i < len(s); i += sz { - r, sz = utf8.DecodeRuneInString(s[i:]) - if unicode.IsLetter(r) { - return i + sz, r == 'w' - } - } - return len(s), false -} - -type noWrapError struct { - msg string - err error - frame Frame -} - -func (e *noWrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *noWrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -type wrapError struct { - msg string - err error - frame Frame -} - -func (e *wrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *wrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -func (e *wrapError) Unwrap() error { - return e.err -} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go deleted file mode 100644 index 1bc9c26b97f..00000000000 --- a/vendor/golang.org/x/xerrors/format.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -// A Formatter formats error messages. -type Formatter interface { - error - - // FormatError prints the receiver's first error and returns the next error in - // the error chain, if any. - FormatError(p Printer) (next error) -} - -// A Printer formats error messages. -// -// The most common implementation of Printer is the one provided by package fmt -// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message -// typically provide their own implementations. -type Printer interface { - // Print appends args to the message output. - Print(args ...interface{}) - - // Printf writes a formatted string. - Printf(format string, args ...interface{}) - - // Detail reports whether error detail is requested. - // After the first call to Detail, all text written to the Printer - // is formatted as additional detail, or ignored when - // detail has not been requested. - // If Detail returns false, the caller can avoid printing the detail at all. - Detail() bool -} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go deleted file mode 100644 index 0de628ec501..00000000000 --- a/vendor/golang.org/x/xerrors/frame.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "runtime" -) - -// A Frame contains part of a call stack. -type Frame struct { - // Make room for three PCs: the one we were asked for, what it called, - // and possibly a PC for skipPleaseUseCallersFrames. See: - // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 - frames [3]uintptr -} - -// Caller returns a Frame that describes a frame on the caller's stack. -// The argument skip is the number of frames to skip over. -// Caller(0) returns the frame for the caller of Caller. -func Caller(skip int) Frame { - var s Frame - runtime.Callers(skip+1, s.frames[:]) - return s -} - -// location reports the file, line, and function of a frame. -// -// The returned function may be "" even if file and line are not. -func (f Frame) location() (function, file string, line int) { - frames := runtime.CallersFrames(f.frames[:]) - if _, ok := frames.Next(); !ok { - return "", "", 0 - } - fr, ok := frames.Next() - if !ok { - return "", "", 0 - } - return fr.Function, fr.File, fr.Line -} - -// Format prints the stack as error detail. -// It should be called from an error's Format implementation -// after printing any other error detail. -func (f Frame) Format(p Printer) { - if p.Detail() { - function, file, line := f.location() - if function != "" { - p.Printf("%s\n ", function) - } - if file != "" { - p.Printf("%s:%d\n", file, line) - } - } -} diff --git a/vendor/golang.org/x/xerrors/go.mod b/vendor/golang.org/x/xerrors/go.mod deleted file mode 100644 index 870d4f612db..00000000000 --- a/vendor/golang.org/x/xerrors/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module golang.org/x/xerrors - -go 1.11 diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go deleted file mode 100644 index 89f4eca5df7..00000000000 --- a/vendor/golang.org/x/xerrors/internal/internal.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package internal - -// EnableTrace indicates whether stack information should be recorded in errors. -var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go deleted file mode 100644 index 9a3b510374e..00000000000 --- a/vendor/golang.org/x/xerrors/wrap.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "reflect" -) - -// A Wrapper provides context around another error. -type Wrapper interface { - // Unwrap returns the next error in the error chain. - // If there is no next error, Unwrap returns nil. - Unwrap() error -} - -// Opaque returns an error with the same error formatting as err -// but that does not match err and cannot be unwrapped. -func Opaque(err error) error { - return noWrapper{err} -} - -type noWrapper struct { - error -} - -func (e noWrapper) FormatError(p Printer) (next error) { - if f, ok := e.error.(Formatter); ok { - return f.FormatError(p) - } - p.Print(e.error) - return nil -} - -// Unwrap returns the result of calling the Unwrap method on err, if err implements -// Unwrap. Otherwise, Unwrap returns nil. -func Unwrap(err error) error { - u, ok := err.(Wrapper) - if !ok { - return nil - } - return u.Unwrap() -} - -// Is reports whether any error in err's chain matches target. -// -// An error is considered to match a target if it is equal to that target or if -// it implements a method Is(error) bool such that Is(target) returns true. -func Is(err, target error) bool { - if target == nil { - return err == target - } - - isComparable := reflect.TypeOf(target).Comparable() - for { - if isComparable && err == target { - return true - } - if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { - return true - } - // TODO: consider supporing target.Is(err). This would allow - // user-definable predicates, but also may allow for coping with sloppy - // APIs, thereby making it easier to get away with them. - if err = Unwrap(err); err == nil { - return false - } - } -} - -// As finds the first error in err's chain that matches the type to which target -// points, and if so, sets the target to its value and returns true. An error -// matches a type if it is assignable to the target type, or if it has a method -// As(interface{}) bool such that As(target) returns true. As will panic if target -// is not a non-nil pointer to a type which implements error or is of interface type. -// -// The As method should set the target to its value and return true if err -// matches the type to which target points. -func As(err error, target interface{}) bool { - if target == nil { - panic("errors: target cannot be nil") - } - val := reflect.ValueOf(target) - typ := val.Type() - if typ.Kind() != reflect.Ptr || val.IsNil() { - panic("errors: target must be a non-nil pointer") - } - if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { - panic("errors: *target must be interface or implement error") - } - targetType := typ.Elem() - for err != nil { - if reflect.TypeOf(err).AssignableTo(targetType) { - val.Elem().Set(reflect.ValueOf(err)) - return true - } - if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { - return true - } - err = Unwrap(err) - } - return false -} - -var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/modules.txt b/vendor/modules.txt index 229a980bee9..9b13e66ac9e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2,7 +2,7 @@ ## explicit github.com/checkpoint-restore/go-criu/v4 github.com/checkpoint-restore/go-criu/v4/rpc -# github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 +# github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 ## explicit github.com/cilium/ebpf github.com/cilium/ebpf/asm @@ -75,6 +75,3 @@ github.com/vishvananda/netns ## explicit golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 -golang.org/x/xerrors -golang.org/x/xerrors/internal From 475c88fb896a9d8f67a6b889e06ce0e506e51a99 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:48:44 -0400 Subject: [PATCH 06/16] TEMP: specs: Extend specs with vTPM support Signed-off-by: Stefan Berger --- .../runtime-spec/specs-go/config.go | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index 7b60f8bb3b2..278385a9f4d 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -352,6 +352,24 @@ type LinuxRdma struct { HcaObjects *uint32 `json:"hcaObjects,omitempty"` } +// LinuxVTPM for vTPM definition +type LinuxVTPM struct { + // Path on host where vTPM writes state to + StatePath string `json:"statePath,omitempty"` + // Whether runc is allowed to delete the 'Statepath' once the TPM is destroyed + StatePathIsManaged bool `json:"statePathIsManaged,omitempty"` + // Version of the TPM that is emulated + TPMVersion string `json:"vtpmVersion,omitempty"` + // Whether to create certificates upon first start of vTPM + CreateCertificates bool `json:"createCerts,omitempty"` + // The PCR banks to enable + PcrBanks string `json:"pcrBanks,omitempty"` + // Under what user to run the vTPM process + RunAs string `json:"runAs,omitempty"` + // The password to derive the encryption key from + EncryptionPassword string `json:"encryptionPassword,omitempty"` +} + // LinuxResources has container runtime resource constraints type LinuxResources struct { // Devices configures the device whitelist. @@ -372,12 +390,16 @@ type LinuxResources struct { // Limits are a set of key value pairs that define RDMA resource limits, // where the key is device name and value is resource limits. Rdma map[string]LinuxRdma `json:"rdma,omitempty"` + // VTPM configuration + VTPMs []LinuxVTPM `json:"vtpms,omitempty"` } // LinuxDevice represents the mknod information for a Linux special device file type LinuxDevice struct { // Path to the device. Path string `json:"path"` + // Path of passed-through device on host + Devpath string `json:"devpath"` // Device type, block, char, etc. Type string `json:"type"` // Major is the device's major number. From d752dca0a25865fdd9211207ce857554c03814e3 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:48:48 -0400 Subject: [PATCH 07/16] Add vTPM support for Linux This patch adds vTPM support for Linux to libcontainer. The functionality is based on a recently added vtpm_proxy driver, which is becoming available in Linux 4.8. The driver provides /dev/vtpmx, on which an ioctl is called that spawns a TPM device in the host's /dev directory and returns an anonymous file-descriptor on which a TPM emulator can listen for TPM commands. If we for example created /dev/tpm12 on the host we make this device available as /dev/tpm0 inside the container. We also add its major and minor numbers to the device cgroup. We implement a VTPM class that allows us to create the device and starts a TPM emulator 'swtpm', to which it passes the anonymous file descriptor. Besides that, the user can choose to have the vTPM create certificates in a step that simulates TPM manufacturing. We do this by calling the external swtpm_setup program, which is part of the swtpm project. VTPM support is added inside the JSON configuration as follows: [...] "linux": { "resources": { "devices": [ { "allow": false, "access": "rwm" } ] , "vtpms": [ { "statePath": "/tmp/tpm-1", "createCerts": true }, ] }, [...] This JSON markup makes a single TPM available inside the created container. o The statPath parameter indicates the directory where the TPM emulator 'swtpm' writes the state of the TPM device to. o The createCerts parameter indicates that certificates for the TPM are to be created. The current implementation does not support checkpointing, so checkpointing of a container with an attached vTPM is prevented. The swtpm project is available here : https://github.com/stefanberger/swtpm The libtpms project is available here: https://github.com/stefanberger/libtpms Signed-off-by: Stefan Berger --- libcontainer/configs/config.go | 4 + libcontainer/configs/device.go | 4 + libcontainer/container_linux.go | 3 + libcontainer/rootfs_linux.go | 7 +- libcontainer/specconv/spec_linux.go | 4 + libcontainer/state_linux.go | 2 + libcontainer/vtpm/vtpm-helper/vtpm_helper.go | 116 ++++ libcontainer/vtpm/vtpm.go | 613 +++++++++++++++++++ utils_linux.go | 63 +- 9 files changed, 813 insertions(+), 3 deletions(-) create mode 100644 libcontainer/vtpm/vtpm-helper/vtpm_helper.go create mode 100644 libcontainer/vtpm/vtpm.go diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index ac523b41760..50a8a180764 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -7,6 +7,7 @@ import ( "os/exec" "time" + "github.com/opencontainers/runc/libcontainer/vtpm" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -201,6 +202,9 @@ type Config struct { // RootlessCgroups is set when unlikely to have the full access to cgroups. // When RootlessCgroups is set, cgroups errors are ignored. RootlessCgroups bool `json:"rootless_cgroups,omitempty"` + + // VTPM configuration + VTPMs []*vtpm.VTPM `json:"vtpms"` } type HookName string diff --git a/libcontainer/configs/device.go b/libcontainer/configs/device.go index 24c5bbfa6ae..e396ffa6ad3 100644 --- a/libcontainer/configs/device.go +++ b/libcontainer/configs/device.go @@ -21,6 +21,10 @@ type Device struct { // Path to the device. Path string `json:"path"` + // the name of the device inside the container (optional) + // allows a host device to appear under different name inside container + Devpath string `json:"devpath"` + // FileMode permission bits for the device. FileMode os.FileMode `json:"file_mode"` diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index b4d722b03ac..05cf32e9c9f 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -843,6 +843,9 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error { // support for doing unprivileged dumps, but the setup of // rootless containers might make this complicated. + if len(c.config.VTPMs) > 0 { + return fmt.Errorf("Checkpointing with attached vTPM is not supported") + } // We are relying on the CRIU version RPC which was introduced with CRIU 3.0.0 if err := c.checkCriuVersion(30000); err != nil { return err diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index b8e792fbb32..41024e7f5ad 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -617,7 +617,12 @@ func createDeviceNode(rootfs string, node *configs.Device, bind bool) error { // The node only exists for cgroup reasons, ignore it here. return nil } - dest := filepath.Join(rootfs, node.Path) + var dest string + if node.Devpath != "" { + dest = filepath.Join(rootfs, node.Devpath) + } else { + dest = filepath.Join(rootfs, node.Path) + } if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { return err } diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 6fd98f02753..0020e2b7e55 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -19,6 +19,7 @@ import ( "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/seccomp" libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" + "github.com/opencontainers/runc/libcontainer/vtpm" "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" @@ -200,6 +201,7 @@ type CreateOpts struct { Spec *specs.Spec RootlessEUID bool RootlessCgroups bool + VTPMs []*vtpm.VTPM } // CreateLibcontainerConfig creates a new libcontainer configuration from a @@ -235,6 +237,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) { NoNewKeyring: opts.NoNewKeyring, RootlessEUID: opts.RootlessEUID, RootlessCgroups: opts.RootlessCgroups, + VTPMs: opts.VTPMs, } exists := false @@ -683,6 +686,7 @@ func createDevices(spec *specs.Spec, config *configs.Config) error { Minor: d.Minor, }, Path: d.Path, + Devpath: d.Devpath, FileMode: filemode, Uid: uid, Gid: gid, diff --git a/libcontainer/state_linux.go b/libcontainer/state_linux.go index aa800c36008..211ffc9bae9 100644 --- a/libcontainer/state_linux.go +++ b/libcontainer/state_linux.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -38,6 +39,7 @@ type containerState interface { } func destroy(c *linuxContainer) error { + vtpmhelper.DestroyVTPMs(c.config.VTPMs) if !c.config.Namespaces.Contains(configs.NEWPID) { if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil { logrus.Warn(err) diff --git a/libcontainer/vtpm/vtpm-helper/vtpm_helper.go b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go new file mode 100644 index 00000000000..ebbbc620bcb --- /dev/null +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go @@ -0,0 +1,116 @@ +// + build linux + +package vtpmhelper + +import ( + "fmt" + "os" + "syscall" + + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/vtpm" + + "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" +) + +// addVTPMDevice adds a device and cgroup entry to the spec +func addVTPMDevice(spec *specs.Spec, hostpath, devpath string, major, minor uint32) { + var filemode os.FileMode = 0600 + + device := specs.LinuxDevice{ + Path: hostpath, + Devpath: devpath, + Type: "c", + Major: int64(major), + Minor: int64(minor), + FileMode: &filemode, + } + spec.Linux.Devices = append(spec.Linux.Devices, device) + + major_p := new(int64) + *major_p = int64(major) + minor_p := new(int64) + *minor_p = int64(minor) + + ld := &specs.LinuxDeviceCgroup{ + Allow: true, + Type: "c", + Major: major_p, + Minor: minor_p, + Access: "rwm", + } + spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld) +} + +// CreateVTPM create a VTPM proxy device and starts the TPM emulator with it +func CreateVTPM(spec *specs.Spec, vtpmdev *specs.LinuxVTPM, devnum int) (*vtpm.VTPM, error) { + + vtpm, err := vtpm.NewVTPM(vtpmdev.StatePath, vtpmdev.StatePathIsManaged, vtpmdev.TPMVersion, vtpmdev.CreateCertificates, vtpmdev.RunAs, vtpmdev.PcrBanks) + if err != nil { + return nil, err + } + + // Start the vTPM process; once stopped, the device pair will also disappear + vtpm.CreatedStatepath, err = vtpm.Start() + if err != nil { + return nil, err + } + + hostdev := vtpm.GetTPMDevname() + major, minor := vtpm.GetMajorMinor() + + devpath := fmt.Sprintf("/dev/tpm%d", devnum) + addVTPMDevice(spec, hostdev, devpath, major, minor) + + // for TPM 2: check if /dev/vtpmrm%d is available + host_tpmrm := fmt.Sprintf("/dev/tpmrm%d", vtpm.GetTPMDevNum()) + if fileInfo, err := os.Lstat(host_tpmrm); err == nil { + if stat_t, ok := fileInfo.Sys().(*syscall.Stat_t); ok { + devNumber := stat_t.Rdev + devpath = fmt.Sprintf("/dev/tpmrm%d", devnum) + addVTPMDevice(spec, host_tpmrm, devpath, unix.Major(devNumber), unix.Minor(devNumber)) + } + } + + return vtpm, nil +} + +func setVTPMHostDevOwner(vtpm *vtpm.VTPM, uid, gid int) error { + hostdev := vtpm.GetTPMDevname() + // adapt ownership of the device since only root can access it + if err := os.Chown(hostdev, uid, gid); err != nil { + return err + } + + host_tpmrm := fmt.Sprintf("/dev/tpmrm%d", vtpm.GetTPMDevNum()) + if _, err := os.Lstat(host_tpmrm); err == nil { + // adapt ownership of the device since only root can access it + if err := os.Chown(host_tpmrm, uid, gid); err != nil { + return err + } + } + + return nil +} + +// SetVTPMHostDevsOwner sets the owner of the host devices to the +// container root's mapped user id; if root inside the container is +// uid 1000 on the host, the devices will be owned by uid 1000. +func SetVTPMHostDevsOwner(config *configs.Config, uid, gid int) error { + if uid != 0 { + for _, vtpm := range config.VTPMs { + if err := setVTPMHostDevOwner(vtpm, uid, gid); err != nil { + return err + } + } + } + return nil +} + +// DestroyVTPMs stops all VTPMs and cleans up the state directory if necessary +func DestroyVTPMs(vtpms []*vtpm.VTPM) { + for _, vtpm := range vtpms { + vtpm.Stop(vtpm.CreatedStatepath) + } +} diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go new file mode 100644 index 00000000000..548da13108b --- /dev/null +++ b/libcontainer/vtpm/vtpm.go @@ -0,0 +1,613 @@ +// + build linux + +package vtpm + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path" + "path/filepath" + "strconv" + "syscall" + "time" + "unsafe" + + "github.com/sirupsen/logrus" +) + +// object +type VTPM struct { + // The path where the TPM emulator writes the TPM state to + StatePath string `json:"statePath"` + + // Whether we are allowed to delete the TPM's state path upon + // destroying the TPM or an outside mgmt. stack will do that + StatePathIsManaged bool `json:"statePathIsManaged"` + + // whether the device state path was created or already existed + CreatedStatepath bool + + // Whether to create a certificate for the VTPM + CreateCerts bool `json:"createCerts"` + + // Version of the TPM + Vtpmversion string `json:"vtpmversion"` + + // Set of active PCR banks + PcrBanks string `json:"pcrbanks"` + + // The user under which to run the TPM emulator + user string + + // The TPM device number as returned from /dev/vtpmx ioctl + Tpm_dev_num uint32 `json:"tpm_dev_num"` + + // The backend file descriptor + fd int32 + + // The major number of the created device + major uint32 + + // The minor number of the created device + minor uint32 + + // process id of this vtpm + Pid int +} + +// ioctl +type vtpm_proxy_new_dev struct { + flags uint32 + tpm_dev_num uint32 + fd int32 + major uint32 + minor uint32 +} + +const ( + ILLEGAL_FD = -1 + VTPM_DEV_NUM_INVALID = 0xffffffff + + VTPM_PROXY_IOC_NEW_DEV = 0xc014a100 + + VTPM_VERSION_1_2 = "1.2" + VTPM_VERSION_2 = "2" + + VTPM_FLAG_TPM2 = 1 +) + +func translateUser(username string) (*user.User, error) { + usr, err := user.Lookup(username) + if err != nil { + usr, err = user.LookupId(username) + } + if err != nil { + return nil, fmt.Errorf("User '%s' not available: %v", username, err) + } + return usr, nil +} + +func ioctl(fd, cmd, msg uintptr) error { + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, msg) + if errno != 0 { + err := errno + return err + } + + return nil +} + +func vtpmx_ioctl(cmd, msg uintptr) error { + vtpmx, err := os.Open("/dev/vtpmx") + if err != nil { + logrus.Warnf("Could not open /dev/vtpmx: %v", err) + return err + } + defer vtpmx.Close() + + if err := ioctl(uintptr(vtpmx.Fd()), cmd, msg); err != nil { + return fmt.Errorf("VTPM: vtpmx ioctl failed: %v", err) + } + + return nil +} + +// Create a new VTPM object +// +// @statepath: directory where the vTPM's state will be written into +// @statepathismanaged: whether we are allowed to delete the TPM's state +// path upon destroying the vTPM +// @vtpmversion: The TPM version +// @createcerts: whether to create certificates for the vTPM (on first start) +// @runas: the account under which to run the swtpm; TPM 1.2 should be run +// with account tss; TPM 2 has more flexibility +// +// After successful creation of the object the Start() method can be called +func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, createcerts bool, runas string, pcrbanks string) (*VTPM, error) { + if len(statepath) == 0 { + return nil, fmt.Errorf("Missing required statpath for vTPM.") + } + + if len(vtpmversion) == 0 { + vtpmversion = VTPM_VERSION_2 + } + if vtpmversion != VTPM_VERSION_1_2 && vtpmversion != VTPM_VERSION_2 { + return nil, fmt.Errorf("Unsupported VTPM version '%s'.", vtpmversion) + } + + if _, err := os.Stat("/dev/vtpmx"); err != nil { + exec.Command("modprobe", "tpm_vtpm_proxy").Run() + if _, err := os.Stat("/dev/vtpmx"); err != nil { + return nil, fmt.Errorf("VTPM device driver not available.") + } + } + + if runas == "" { + runas = "root" + } + // TPM 1.2 choices are only 'root' and 'tss' users due to tcsd + if vtpmversion == VTPM_VERSION_1_2 && runas != "root" && runas != "tss" { + runas = "root" + } + + usr, err := translateUser(runas) + if err != nil { + return nil, err + } + runas = usr.Uid + + return &VTPM{ + user: runas, + StatePath: statepath, + StatePathIsManaged: statepathismanaged, + Vtpmversion: vtpmversion, + CreateCerts: createcerts, + PcrBanks: pcrbanks, + Tpm_dev_num: VTPM_DEV_NUM_INVALID, + fd: ILLEGAL_FD, + }, nil +} + +// createDev creates the vTPM proxy device using an ioctl on /dev/vtpmx. +// The ioctl returns the major and minor number of the /dev/tpm%d device +// that was created and the device number to indicate which /dev/tpm%d +// is the device. A file descriptor is also returned that must be passed +// to the TPM emulator for it to read the TPM commands from and write +// TPM response to. +func (vtpm *VTPM) createDev() error { + var ( + vtpm_proxy_new_dev vtpm_proxy_new_dev + ) + + if vtpm.Tpm_dev_num != VTPM_DEV_NUM_INVALID { + logrus.Info("Device already exists") + return nil + } + + if vtpm.Vtpmversion == VTPM_VERSION_2 { + vtpm_proxy_new_dev.flags = VTPM_FLAG_TPM2 + } + + err := vtpmx_ioctl(VTPM_PROXY_IOC_NEW_DEV, uintptr(unsafe.Pointer(&vtpm_proxy_new_dev))) + if err != nil { + return err + } + + vtpm.Tpm_dev_num = vtpm_proxy_new_dev.tpm_dev_num + vtpm.fd = vtpm_proxy_new_dev.fd + vtpm.major = vtpm_proxy_new_dev.major + vtpm.minor = vtpm_proxy_new_dev.minor + + return nil +} + +// getPidFile creates the full path of the TPM emulator PID file +func (vtpm *VTPM) getPidFile() string { + return path.Join(vtpm.StatePath, "swtpm.pid") +} + +// getLogFile creates the full path of the TPM emulator log file +func (vtpm *VTPM) getLogFile() string { + return path.Join(vtpm.StatePath, "swtpm.log") +} + +// getPidFromFile: Get the PID from the PID file +func (vtpm *VTPM) getPidFromFile() (int, error) { + d, err := ioutil.ReadFile(vtpm.getPidFile()) + if err != nil { + return -1, err + } + if len(d) == 0 { + return -1, fmt.Errorf("Empty pid file") + } + + pid, err := strconv.Atoi(string(d)) + if err != nil { + return -1, fmt.Errorf("Could not parse pid from file: %s", string(d)) + } + return pid, nil +} + +// waitForPidFile: wait for the PID file to appear and read the PID from it +func (vtpm *VTPM) waitForPidFile(loops int) (int, error) { + for loops >= 0 { + pid, err := vtpm.getPidFromFile() + if pid > 0 && err == nil { + return pid, nil + } + time.Sleep(time.Millisecond * 100) + loops -= 1 + } + logrus.Error("PID file did not appear") + return -1, fmt.Errorf("swtpm's pid file did not appear") +} + +// sendShutdown sends the TPM2_Shutdown command to a TPM 2; no command is +// sent in case of a TPM 1.2 +func (vtpm *VTPM) sendShutdown() error { + var err error = nil + + if vtpm.Tpm_dev_num != VTPM_DEV_NUM_INVALID && vtpm.Vtpmversion == VTPM_VERSION_2 { + devname := vtpm.GetTPMDevname() + dev, err := os.OpenFile(devname, os.O_RDWR, 0666) + if err != nil { + return err + } + defer dev.Close() + + sd := []byte{0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x01, 0x45, 0x00, 0x00} + n, err := dev.Write(sd) + if err != nil || n != len(sd) { + logrus.Errorf("Could not write shutdown to %s: %v", devname, err) + } + } + return err +} + +// stopByPidFile: Stop the vTPM by its PID file +func (vtpm *VTPM) stopByPidFile() error { + + vtpm.sendShutdown() + + pid, err := vtpm.getPidFromFile() + if err != nil { + return err + } + + p, err := os.FindProcess(pid) + if err != nil { + return err + } + + err = p.Signal(syscall.SIGTERM) + + return err +} + +func (vtpm *VTPM) modifyModePath(dirPath string, mask, set os.FileMode) error { + for { + fileInfo, err := os.Stat(dirPath) + if err != nil { + return err + } + if !fileInfo.IsDir() { + continue + } + + mode := (fileInfo.Mode() & mask) | set + if err := os.Chmod(dirPath, mode); err != nil { + return err + } + + dirPath = filepath.Dir(dirPath) + if dirPath == "/" { + break + } + } + return nil +} + +// DeleteStatePath deletes the directory where the TPM emulator writes its state +// into unless the state path is managed by a higher layer application, in which +// case the state path is not removed +func (vtpm *VTPM) DeleteStatePath() error { + if !vtpm.StatePathIsManaged { + return os.RemoveAll(vtpm.StatePath) + } + return nil +} + +// createStatePath creates the TPM directory where the TPM writes its state +// into; it also makes the directory accessible to the 'runas' user +// +// This method returns true in case the path was created, false in case the +// path already existed +func (vtpm *VTPM) createStatePath() (bool, error) { + created := false + if _, err := os.Stat(vtpm.StatePath); err != nil { + if err := os.MkdirAll(vtpm.StatePath, 0770); err != nil { + return false, fmt.Errorf("Could not create directory %s: %v", vtpm.StatePath, err) + } + created = true + } + + err := vtpm.chownStatePath() + if err != nil { + if created { + vtpm.DeleteStatePath() + } + return false, err + } + return created, nil +} + +func (vtpm *VTPM) chownStatePath() error { + usr, err := translateUser(vtpm.user) + if err != nil { + return err + } + + uid, err := strconv.Atoi(usr.Uid) + if err != nil { + return fmt.Errorf("Error parsing Uid %s: %v", usr.Uid, err) + } + + gid, err := strconv.Atoi(usr.Gid) + if err != nil { + return fmt.Errorf("Error parsing Gid %s: %v", usr.Gid, err) + } + + err = filepath.Walk(vtpm.StatePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() && path != vtpm.StatePath { + return filepath.SkipDir + } + if err := os.Chown(path, uid, gid); err != nil { + return fmt.Errorf("Could not change ownership of file %s: %v", path, err) + } + return nil + }) + if err != nil { + return err + } + + if uid != 0 { + if err := vtpm.modifyModePath(vtpm.StatePath, 0777, 0010); err != nil { + return fmt.Errorf("Could not chmod path to %s: %v", vtpm.StatePath, err) + } + } + + return nil +} + +// runSwtpmSetup runs swtpm_setup to simulate TPM manufacturing by creating +// EK and platform certificates and enabling TPM 2 PCR banks +func (vtpm *VTPM) runSwtpmSetup() error { + // if state already exists, --not-overwrite will not overwrite it + cmd := exec.Command("swtpm_setup", "--tpm-state", vtpm.StatePath, "--createek", + "--logfile", vtpm.getLogFile(), "--not-overwrite") + if vtpm.Vtpmversion == VTPM_VERSION_1_2 { + cmd.Args = append(cmd.Args, "--runas", vtpm.user) + } else if vtpm.Vtpmversion == VTPM_VERSION_2 { + // when creating certs we need root access to create lock files + if !vtpm.CreateCerts { + cmd.Args = append(cmd.Args, "--runas", vtpm.user) + } + } + if vtpm.CreateCerts { + cmd.Args = append(cmd.Args, "--create-ek-cert", "--create-platform-cert", "--lock-nvram") + } + + if vtpm.Vtpmversion == VTPM_VERSION_2 { + cmd.Args = append(cmd.Args, "--tpm2") + if len(vtpm.PcrBanks) > 0 { + cmd.Args = append(cmd.Args, "--pcr-banks", vtpm.PcrBanks) + } + } + + // need to explicitly set TMPDIR + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "TMPDIR=/tmp") + + output, err := cmd.CombinedOutput() + if err != nil { + logrus.Errorf("swtpm_setup failed: %s", string(output)) + return fmt.Errorf("swtpm_setup failed: %s\nlog: %s", string(output), vtpm.ReadLog()) + } + + return nil +} + +// waitForTPMDevice: Wait for /dev/tpm%d to appear and while waiting +// check whether the swtpm is still alive by checking its PID file +func (vtpm *VTPM) waitForTPMDevice(loops int) error { + devname := vtpm.GetTPMDevname() + pidfile := vtpm.getPidFile() + + for loops >= 0 { + if _, err := os.Stat(pidfile); err != nil { + logrus.Errorf("swtpm process has terminated") + return err + } + + if _, err := os.Stat(devname); err == nil { + return nil + } + time.Sleep(time.Millisecond * 100) + loops -= 1 + } + return fmt.Errorf("TPM device %s did not appear", devname) +} + +// startSwtpm creates the VTPM proxy device and start the swtpm process +func (vtpm *VTPM) startSwtpm() error { + err := vtpm.createDev() + if err != nil { + return err + } + + tpmstate := fmt.Sprintf("dir=%s", vtpm.StatePath) + pidfile := fmt.Sprintf("file=%s", vtpm.getPidFile()) + logfile := fmt.Sprintf("file=%s", vtpm.getLogFile()) + + // child will get first passed fd as '3' + cmd := exec.Command("swtpm", "chardev", "--tpmstate", tpmstate, + "--daemon", "--fd", "3", "--pid", pidfile, "--log", logfile, + "--runas", vtpm.user, "--flags", "not-need-init", + "--locality", "reject-locality-4,allow-set-locality") + if vtpm.Vtpmversion == VTPM_VERSION_2 { + cmd.Args = append(cmd.Args, "--tpm2") + } + file := os.NewFile(uintptr(vtpm.fd), "[vtpm]") + cmd.ExtraFiles = append(cmd.ExtraFiles, file) + + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("swtpm failed on fd %d: %s\nlog: %s", vtpm.fd, string(output), vtpm.ReadLog()) + } + + vtpm.Pid, err = vtpm.waitForPidFile(10) + if err != nil { + return err + } + + err = vtpm.waitForTPMDevice(50) + if err != nil { + return err + } + return nil +} + +// runSwtpmBios runs swtpm_bios to initialize the TPM +func (vtpm *VTPM) runSwtpmBios() error { + tpmname := vtpm.GetTPMDevname() + + cmd := exec.Command("swtpm_bios", "-n", "-cs", "-u", "--tpm-device", tpmname) + if vtpm.Vtpmversion == VTPM_VERSION_2 { + cmd.Args = append(cmd.Args, "--tpm2") + } else { + // make sure the TPM 1.2 is activated + cmd.Args = append(cmd.Args, "-ea") + } + + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("swtpm_bios failed: %s", output) + } + return nil +} + +// Start starts the vTPM (swtpm) +// +// - ensure any still running vTPM, which wrote its PID into a file in its state path, is terminated +// the swtpm will, upon normal termination, remove its PID file +// - setup the state path +// - if the state path was created ( = swtpm runs for the first time) also create the certificates +// - create the device pair +// - start the swtpm process +// - run swtpm_bios on it to initialize the vTPM as firmware would +// - if return code is 129, restart the vTPM to activate it and run swtpm_bios again +// +// After this method ran successfully, the TPM device (/dev/tpm%d) is available for use +func (vtpm *VTPM) Start() (bool, error) { + + vtpm.Stop(false) + + createdStatePath, err := vtpm.createStatePath() + if err != nil { + return false, err + } + defer func() { + if err != nil { + vtpm.Stop(createdStatePath) + } + }() + + err = vtpm.runSwtpmSetup() + if err != nil { + return false, err + } + // set the directory accesses for vtpm.user after swtpm_setup may have needed higher + // privileges + err = vtpm.chownStatePath() + if err != nil { + return false, err + } + + err = vtpm.startSwtpm() + if err != nil { + return false, err + } + + err = vtpm.runSwtpmBios() + if err != nil { + return false, err + } + + return createdStatePath, nil +} + +// Stop stops a running vTPM; this method can be called at any time also +// to do partial cleanups; After this method ran, Start() can be called again +func (vtpm *VTPM) Stop(deleteStatePath bool) error { + + err := vtpm.stopByPidFile() + + vtpm.CloseServer() + + vtpm.Tpm_dev_num = VTPM_DEV_NUM_INVALID + + if deleteStatePath { + vtpm.DeleteStatePath() + } + + return err +} + +// Get the TPM device name; this method can be called after successful Start() +func (vtpm *VTPM) GetTPMDevname() string { + return fmt.Sprintf("/dev/tpm%d", vtpm.Tpm_dev_num) +} + +// GetTPMDevNum returns the TPM device number; this would return 10 in case +// /dev/tpm10 was created on the host; this method can be called after +// sucessful Start() +func (vtpm *VTPM) GetTPMDevNum() uint32 { + return vtpm.Tpm_dev_num +} + +// Get the major and minor numbers of the created TPM device; +// This method can be called after successful Start() +func (vtpm *VTPM) GetMajorMinor() (uint32, uint32) { + return vtpm.major, vtpm.minor +} + +// ReadLog reads the vTPM's log file and returns the contents as a string +// This method can be called after Start() +func (vtpm *VTPM) ReadLog() string { + output, err := ioutil.ReadFile(vtpm.getLogFile()) + if err != nil { + return "" + } + return string(output) +} + +// CloseServer closes the server side file descriptor; this will remove the +// /dev/tpm%d and /dev/tpmrm%d (in case of TPM 2) on the host if the file +// descriptor is the last one holding the device open; also use this function +// after passing the file +// This method can be called after Start() +func (vtpm *VTPM) CloseServer() error { + + if vtpm.fd != ILLEGAL_FD { + os.NewFile(uintptr(vtpm.fd), "[vtpm]").Close() + vtpm.fd = ILLEGAL_FD + } + return nil +} diff --git a/utils_linux.go b/utils_linux.go index a9587f7d3bc..d178c86ad4b 100644 --- a/utils_linux.go +++ b/utils_linux.go @@ -16,6 +16,8 @@ import ( "github.com/opencontainers/runc/libcontainer/intelrdt" "github.com/opencontainers/runc/libcontainer/specconv" "github.com/opencontainers/runc/libcontainer/utils" + "github.com/opencontainers/runc/libcontainer/vtpm" + "github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper" "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" @@ -226,7 +228,7 @@ func createPidFile(path string, process *libcontainer.Process) error { return os.Rename(tmpName, path) } -func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { +func createContainer(context *cli.Context, id string, spec *specs.Spec, vtpms []*vtpm.VTPM) (libcontainer.Container, error) { rootlessCg, err := shouldUseRootlessCgroupManager(context) if err != nil { return nil, err @@ -239,11 +241,17 @@ func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcont Spec: spec, RootlessEUID: os.Geteuid() != 0, RootlessCgroups: rootlessCg, + VTPMs: vtpms, }) if err != nil { return nil, err } + err = setHostDevsOwner(config) + if err != nil { + return nil, err + } + factory, err := loadFactory(context) if err != nil { return nil, err @@ -417,7 +425,17 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp } } - container, err := createContainer(context, id, spec) + vtpms, err := createVTPMs(spec) + if err != nil { + return -1, err + } + defer func() { + if err != nil { + destroyVTPMs(vtpms) + } + }() + + container, err := createContainer(context, id, spec, vtpms) if err != nil { return -1, err } @@ -463,3 +481,44 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp } return r.run(spec.Process) } + +func createVTPMs(spec *specs.Spec) ([]*vtpm.VTPM, error) { + var vtpms []*vtpm.VTPM + + r := spec.Linux.Resources + if r == nil { + return vtpms, nil + } + + devnum := 0 + for _, vtpm := range r.VTPMs { + v, err := vtpmhelper.CreateVTPM(spec, &vtpm, devnum) + if err != nil { + destroyVTPMs(vtpms) + return vtpms, err + } + vtpms = append(vtpms, v) + devnum++ + } + return vtpms, nil +} + +func destroyVTPMs(vtpms []*vtpm.VTPM) { + vtpmhelper.DestroyVTPMs(vtpms) +} + +func setVTPMHostDevsOwner(config *configs.Config) error { + rootUID, err := config.HostRootUID() + if err != nil { + return err + } + rootGID, err := config.HostRootGID() + if err != nil { + return err + } + return vtpmhelper.SetVTPMHostDevsOwner(config, rootUID, rootGID) +} + +func setHostDevsOwner(config *configs.Config) error { + return setVTPMHostDevsOwner(config) +} From 7aff18cdbec8fe8f9755f19cb3af765009a98e77 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:48:57 -0400 Subject: [PATCH 08/16] apparmor: Implement ApplyProfileThread() We need to implement ApplyProfileThread() to apply a profile via /proc/self-thread/attr/exec rather than /proc/self/attr/exec otherwise we get (~50%) failures trying to write the profile to /proc/self/attr/exec. When using self-thread we get 100% success. Signed-off-by: Stefan Berger --- libcontainer/apparmor/apparmor.go | 24 +++++++++++++++++----- libcontainer/apparmor/apparmor_disabled.go | 7 +++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libcontainer/apparmor/apparmor.go b/libcontainer/apparmor/apparmor.go index debfc1e489e..4febc70bc67 100644 --- a/libcontainer/apparmor/apparmor.go +++ b/libcontainer/apparmor/apparmor.go @@ -21,10 +21,15 @@ func IsEnabled() bool { return false } -func setProcAttr(attr, value string) error { +func setProcAttr(attr, value string, useThread bool) error { // Under AppArmor you can only change your own attr, so use /proc/self/ // instead of /proc// like libapparmor does - path := fmt.Sprintf("/proc/self/attr/%s", attr) + var path string + if useThread { + path = fmt.Sprintf("/proc/thread-self/attr/%s", attr) + } else { + path = fmt.Sprintf("/proc/self/attr/%s", attr) + } f, err := os.OpenFile(path, os.O_WRONLY, 0) if err != nil { @@ -41,14 +46,23 @@ func setProcAttr(attr, value string) error { } // changeOnExec reimplements aa_change_onexec from libapparmor in Go -func changeOnExec(name string) error { +func changeOnExec(name string, useThread bool) error { value := "exec " + name - if err := setProcAttr("exec", value); err != nil { + if err := setProcAttr("exec", value, useThread); err != nil { return fmt.Errorf("apparmor failed to apply profile: %s", err) } return nil } +// ApplyProfileThread will apply the profile with the specified name to the process +// after the next exec using /proc/self-thread rather than /proc/self +func ApplyProfileThread(name string) error { + if name == "" { + return nil + } + return changeOnExec(name, true) +} + // ApplyProfile will apply the profile with the specified name to the process after // the next exec. func ApplyProfile(name string) error { @@ -56,5 +70,5 @@ func ApplyProfile(name string) error { return nil } - return changeOnExec(name) + return changeOnExec(name, false) } diff --git a/libcontainer/apparmor/apparmor_disabled.go b/libcontainer/apparmor/apparmor_disabled.go index d4110cf0bc6..ce18b4a5211 100644 --- a/libcontainer/apparmor/apparmor_disabled.go +++ b/libcontainer/apparmor/apparmor_disabled.go @@ -12,6 +12,13 @@ func IsEnabled() bool { return false } +func ApplyProfileThread(name string) error { + if name != "" { + return ErrApparmorNotEnabled + } + return nil +} + func ApplyProfile(name string) error { if name != "" { return ErrApparmorNotEnabled From 463e2f5a83e924f28c353ff8f3cf4b8bc18c35bf Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:08 -0400 Subject: [PATCH 09/16] vtpm: Run swtpm with an AppArmor profile Create an AppArmor profile and apply it so that swtpm runs with an AppArmor profile. Signed-off-by: Stefan Berger --- libcontainer/vtpm/vtpm.go | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go index 548da13108b..a8de0d6b698 100644 --- a/libcontainer/vtpm/vtpm.go +++ b/libcontainer/vtpm/vtpm.go @@ -15,6 +15,8 @@ import ( "time" "unsafe" + "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/sirupsen/logrus" ) @@ -56,6 +58,9 @@ type VTPM struct { // process id of this vtpm Pid int + + // The AppArmor profile's full path + aaprofile string } // ioctl @@ -452,6 +457,11 @@ func (vtpm *VTPM) startSwtpm() error { return err } + err = vtpm.setupAppArmor() + if err != nil { + return err + } + tpmstate := fmt.Sprintf("dir=%s", vtpm.StatePath) pidfile := fmt.Sprintf("file=%s", vtpm.getPidFile()) logfile := fmt.Sprintf("file=%s", vtpm.getLogFile()) @@ -481,6 +491,9 @@ func (vtpm *VTPM) startSwtpm() error { if err != nil { return err } + + vtpm.resetAppArmor() + return nil } @@ -561,6 +574,8 @@ func (vtpm *VTPM) Stop(deleteStatePath bool) error { vtpm.CloseServer() + vtpm.teardownAppArmor() + vtpm.Tpm_dev_num = VTPM_DEV_NUM_INVALID if deleteStatePath { @@ -609,5 +624,85 @@ func (vtpm *VTPM) CloseServer() error { os.NewFile(uintptr(vtpm.fd), "[vtpm]").Close() vtpm.fd = ILLEGAL_FD } + + return nil +} + +// setupAppArmor creates an apparmor profile for swtpm if AppArmor is enabled and +// compiles it using apparmor_parser -r and activates it for the next +// exec. +func (vtpm *VTPM) setupAppArmor() error { + var statefilepattern string + + if !apparmor.IsEnabled() { + return nil + } + + profilename := fmt.Sprintf("runc_%d_swtpm_tpm%d", os.Getpid(), vtpm.GetTPMDevNum()) + if vtpm.Vtpmversion == VTPM_VERSION_1_2 { + statefilepattern = path.Join(vtpm.StatePath, "tpm-00.*") + } else { + statefilepattern = path.Join(vtpm.StatePath, "tpm2-00.*") + } + + profile := fmt.Sprintf("\n#include \n"+ + "profile %s {\n"+ + " #include \n"+ + " capability setgid,\n"+ + " capability setuid,\n"+ + " /dev/tpm[0-9]* rw,\n"+ + " owner /etc/group r,\n"+ + " owner /etc/nsswitch.conf r,\n"+ + " owner /etc/passwd r,\n"+ + " %s/.lock wk,\n"+ + " %s w,\n"+ + " %s rw,\n"+ + " %s rw,\n"+ + "}\n", + profilename, + vtpm.StatePath, + vtpm.getLogFile(), + vtpm.getPidFile(), + statefilepattern) + + vtpm.aaprofile = path.Join(vtpm.StatePath, "swtpm.apparmor") + + err := ioutil.WriteFile(vtpm.aaprofile, []byte(profile), 0600) + if err != nil { + return err + } + defer func() { + if err != nil { + vtpm.teardownAppArmor() + } + }() + + cmd := exec.Command("/sbin/apparmor_parser", "-r", vtpm.aaprofile) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("apparmor_parser -r failed: %s", string(output)) + } + + err = apparmor.ApplyProfileThread(profilename) + if err != nil { + return err + } + return nil } + +func (vtpm *VTPM) resetAppArmor() { + apparmor.ApplyProfileThread("unconfined") +} + +// teardownAppArmor removes the AppArmor profile from the system and ensures +// that the next time the process exec's no swtpm related profile is applied +func (vtpm *VTPM) teardownAppArmor() { + vtpm.resetAppArmor() + if len(vtpm.aaprofile) > 0 { + cmd := exec.Command("/sbin/apparmor_parser", "-R", vtpm.aaprofile) + cmd.Run() + os.Remove(vtpm.aaprofile) + vtpm.aaprofile = "" + } +} From 03af7ccddd0cccda417860da0024d37dfcd3137c Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:15 -0400 Subject: [PATCH 10/16] vtpm: Run swtpm with an SELinux label On systems supporting SELinux run swtpm with an SELinux label applied. Also label the required files in the state directory. Signed-off-by: Stefan Berger --- libcontainer/vtpm/vtpm.go | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go index a8de0d6b698..96423d9346c 100644 --- a/libcontainer/vtpm/vtpm.go +++ b/libcontainer/vtpm/vtpm.go @@ -16,6 +16,7 @@ import ( "unsafe" "github.com/opencontainers/runc/libcontainer/apparmor" + selinux "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" ) @@ -461,6 +462,10 @@ func (vtpm *VTPM) startSwtpm() error { if err != nil { return err } + err = vtpm.setupSELinux() + if err != nil { + return err + } tpmstate := fmt.Sprintf("dir=%s", vtpm.StatePath) pidfile := fmt.Sprintf("file=%s", vtpm.getPidFile()) @@ -492,6 +497,7 @@ func (vtpm *VTPM) startSwtpm() error { return err } + vtpm.resetSELinux() vtpm.resetAppArmor() return nil @@ -574,6 +580,7 @@ func (vtpm *VTPM) Stop(deleteStatePath bool) error { vtpm.CloseServer() + vtpm.teardownSELinux() vtpm.teardownAppArmor() vtpm.Tpm_dev_num = VTPM_DEV_NUM_INVALID @@ -706,3 +713,52 @@ func (vtpm *VTPM) teardownAppArmor() { vtpm.aaprofile = "" } } + +// setupSELinux labels the swtpm files with SELinux labels if SELinux is enabled +func (vtpm *VTPM) setupSELinux() error { + if !selinux.GetEnabled() { + return nil + } + + processLabel, fileLabel := selinux.ContainerLabels() + if len(processLabel) == 0 || len(fileLabel) == 0 { + return nil + } + + err := filepath.Walk(vtpm.StatePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() && path != vtpm.StatePath { + return filepath.SkipDir + } + return selinux.SetFileLabel(path, fileLabel) + }) + + err = selinux.SetFSCreateLabel(fileLabel) + if err != nil { + return err + } + err = ioutil.WriteFile("/sys/fs/selinux/context", []byte(processLabel), 0000) + if err != nil { + return err + } + err = selinux.SetExecLabel(processLabel) + if err != nil { + return err + } + + return nil +} + +// resetSELinux resets the prepared SELinux labels +func (vtpm *VTPM) resetSELinux() { + selinux.SetExecLabel("") + selinux.SetFSCreateLabel("") + ioutil.WriteFile("/sys/fs/selinux/context", []byte(""), 0000) +} + +// teardownSELinux cleans up SELinux for next spawned process +func (vtpm *VTPM) teardownSELinux() { + vtpm.resetSELinux() +} From ffbcce6dff2e29e8829f23a8492bb7222f6f9471 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:22 -0400 Subject: [PATCH 11/16] vtpm: Add test cases Add test cases for testing TPM 1.2 and TPM 2 by creating, stopping, restarting, and destroying it. Signed-off-by: Stefan Berger --- .../vtpm/vtpm-helper/vtpm_helper_test.go | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go diff --git a/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go b/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go new file mode 100644 index 00000000000..6b43dcc2191 --- /dev/null +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go @@ -0,0 +1,112 @@ +// + build linux + +package vtpmhelper + +import ( + "io/ioutil" + "os" + "os/exec" + "path" + "strings" + "testing" + + "github.com/opencontainers/runc/libcontainer/vtpm" + "github.com/opencontainers/runtime-spec/specs-go" +) + +func TestCreateVTPMFail(t *testing.T) { + vtpmdev := specs.LinuxVTPM{} + + _, err := CreateVTPM(&specs.Spec{}, &vtpmdev, 0) + if err == nil { + t.Fatalf("Could create vTPM without statepath %v", err) + } +} + +// check prerequisites for starting a vTPM +func checkPrerequisites(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("Need to be root to run this test") + } + + for _, executable := range []string{"swtpm_setup", "swtpm"} { + if err := exec.Command(executable, "--help").Run(); err != nil { + t.Skipf("Could not run %s --help: %v", executable, err) + } + } +} + +func createVTPM(t *testing.T, tpmversion string, createCertificates bool, runas string) *vtpm.VTPM { + + checkPrerequisites(t) + + workdir, err := ioutil.TempDir("", "runctest") + if err != nil { + t.Fatalf("Could not create tmp dir: %s", err) + } + defer os.Remove(workdir) + + tpmdirname := path.Join(workdir, "myvtpm") + + spec := &specs.Spec{ + Linux: &specs.Linux{ + Devices: []specs.LinuxDevice{}, + Resources: &specs.LinuxResources{}, + }, + } + vtpmdev := &specs.LinuxVTPM{ + StatePath: tpmdirname, + TPMVersion: tpmversion, + CreateCertificates: createCertificates, + RunAs: runas, + } + + myvtpm, err := CreateVTPM(spec, vtpmdev, 0) + if err != nil { + if strings.Contains(err.Error(), "VTPM device driver not available") { + t.Skipf("%v", err) + } else { + t.Fatalf("Could not create VTPM device: %v", err) + } + } + return myvtpm +} + +func destroyVTPM(t *testing.T, myvtpm *vtpm.VTPM) { + tpmdirname := myvtpm.StatePath + + DestroyVTPMs([]*vtpm.VTPM{myvtpm}) + + if _, err := os.Stat(tpmdirname); !os.IsNotExist(err) { + t.Fatalf("State directory should have been removed since it was created by vtpm-helpers") + } +} + +func createRestartDestroyVTPM(t *testing.T, tpmversion string, createCertificates bool, runas string) { + myvtpm := createVTPM(t, tpmversion, createCertificates, runas) + + err := myvtpm.Stop(false) + if err != nil { + t.Fatalf("VTPM could not be stopped cleanly: %v", err) + } + + createdStatePath, err := myvtpm.Start() + if err != nil { + t.Fatalf("VTPM could not be started: %v", err) + } + if createdStatePath { + t.Fatalf("VTPM Start() should not have created the state path at this time") + } + + destroyVTPM(t, myvtpm) +} + +func TestCreateVTPM2(t *testing.T) { + createRestartDestroyVTPM(t, "", true, "root") + createRestartDestroyVTPM(t, "", false, "0") + createRestartDestroyVTPM(t, "2", true, "0") +} + +func TestCreateVTPM12(t *testing.T) { + createRestartDestroyVTPM(t, "1.2", true, "root") +} From fc8d5aa1eb57efba72880eb4e7037992be2a78aa Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:37 -0400 Subject: [PATCH 12/16] vtpm: Get the supported capabilities of swtpm and swtpm_setup Call 'swtpm chardev --print-capabilities' to get the supported capabilites from swtpm. An JSON object is printed by swtpm that we unmarshal and we pick the 'features' part from it that is an array of strings indicating what this version of swtpm supports. This option was added in v0.2. For older versions of swtpm we return an empty array. Signed-off-by: Stefan Berger --- libcontainer/vtpm/vtpm.go | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go index 96423d9346c..de8b1ba54e7 100644 --- a/libcontainer/vtpm/vtpm.go +++ b/libcontainer/vtpm/vtpm.go @@ -3,6 +3,7 @@ package vtpm import ( + "encoding/json" "fmt" "io/ioutil" "os" @@ -62,6 +63,12 @@ type VTPM struct { // The AppArmor profile's full path aaprofile string + + // swtpm_setup capabilities + swtpmSetupCaps []string + + // swtpm capabilities + swtpmCaps []string } // ioctl @@ -121,6 +128,49 @@ func vtpmx_ioctl(cmd, msg uintptr) error { return nil } +// getCapabilities gets the capabilities map of an executable by invoking it with +// --print-capabilities. It returns the array of feature strings. +// This function returns an empty array if the executable does not support --print-capabilities. +// Expected output looks like this: +// { "type": "swtpm_setup", "features": [ "cmdarg-keyfile-fd", "cmdarg-pwdfile-fd" ] } +func getCapabilities(cmd *exec.Cmd) ([]string, error) { + caps := make(map[string]interface{}) + + output, err := cmd.Output() + if err != nil { + return nil, nil + } + + err = json.Unmarshal([]byte(output), &caps) + if err != nil { + return nil, fmt.Errorf("Could not unmarshal output: %s: %v\n", output, err) + } + + features, _ := caps["features"].([]interface{}) + res := make([]string, 0) + for _, f := range features { + res = append(res, f.(string)) + } + return res, nil +} + +func getSwtpmSetupCapabilities() ([]string, error) { + return getCapabilities(exec.Command("swtpm_setup", "--print-capabilities")) +} + +func getSwtpmCapabilities() ([]string, error) { + return getCapabilities(exec.Command("swtpm", "chardev", "--print-capabilities")) +} + +func hasCapability(capabilities []string, capability string) bool { + for _, c := range capabilities { + if capability == c { + return true + } + } + return false +} + // Create a new VTPM object // // @statepath: directory where the vTPM's state will be written into @@ -165,6 +215,15 @@ func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, crea } runas = usr.Uid + swtpmSetupCaps, err := getSwtpmSetupCapabilities() + if err != nil { + return nil, err + } + swtpmCaps, err := getSwtpmCapabilities() + if err != nil { + return nil, err + } + return &VTPM{ user: runas, StatePath: statepath, @@ -174,6 +233,8 @@ func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, crea PcrBanks: pcrbanks, Tpm_dev_num: VTPM_DEV_NUM_INVALID, fd: ILLEGAL_FD, + swtpmSetupCaps: swtpmSetupCaps, + swtpmCaps: swtpmCaps, }, nil } From 5b7bb1f9ec49e4e30618b0ada4b1ddd051726616 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:44 -0400 Subject: [PATCH 13/16] vtpm: Pass startup-clear as part of flags to avoid kernel logging We need to startup the TPM as part of starting swtpm so that the Linux driver can successfully send its initial command to the vTPM and does not log a failure and then do the startup itself. Signed-off-by: Stefan Berger --- libcontainer/vtpm/vtpm.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go index de8b1ba54e7..e7ffd5cd54b 100644 --- a/libcontainer/vtpm/vtpm.go +++ b/libcontainer/vtpm/vtpm.go @@ -532,10 +532,15 @@ func (vtpm *VTPM) startSwtpm() error { pidfile := fmt.Sprintf("file=%s", vtpm.getPidFile()) logfile := fmt.Sprintf("file=%s", vtpm.getLogFile()) + flags := "not-need-init" + if hasCapability(vtpm.swtpmCaps, "flags-opt-startup") { + flags += ",startup-clear" + } + // child will get first passed fd as '3' cmd := exec.Command("swtpm", "chardev", "--tpmstate", tpmstate, "--daemon", "--fd", "3", "--pid", pidfile, "--log", logfile, - "--runas", vtpm.user, "--flags", "not-need-init", + "--runas", vtpm.user, "--flags", flags, "--locality", "reject-locality-4,allow-set-locality") if vtpm.Vtpmversion == VTPM_VERSION_2 { cmd.Args = append(cmd.Args, "--tpm2") From 0a352ff3a094469972c96c0309b4dea1d66f8e3b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:49:58 -0400 Subject: [PATCH 14/16] vtpm: Add support for encrypted vTPM state This patch adds support for encrypting the vTPM state by allowing a user to pass a password to swtpm_setup and swtpm. Signed-off-by: Stefan Berger --- libcontainer/vtpm/vtpm-helper/vtpm_helper.go | 41 +++++++++++- .../vtpm/vtpm-helper/vtpm_helper_test.go | 60 +++++++++++++++-- libcontainer/vtpm/vtpm.go | 65 ++++++++++++++++++- 3 files changed, 157 insertions(+), 9 deletions(-) diff --git a/libcontainer/vtpm/vtpm-helper/vtpm_helper.go b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go index ebbbc620bcb..dcd4cf4ca22 100644 --- a/libcontainer/vtpm/vtpm-helper/vtpm_helper.go +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go @@ -4,7 +4,10 @@ package vtpmhelper import ( "fmt" + "io/ioutil" "os" + "strconv" + "strings" "syscall" "github.com/opencontainers/runc/libcontainer/configs" @@ -43,10 +46,46 @@ func addVTPMDevice(spec *specs.Spec, hostpath, devpath string, major, minor uint spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *ld) } +// getEncryptionPassword gets the plain password from the caller +// valid formats passed to this function are: +// - +// - pass= +// - fd= +// - file= +func getEncryptionPassword(pwdString string) ([]byte, error) { + if strings.HasPrefix(pwdString, "file=") { + return ioutil.ReadFile(pwdString[5:]) + } else if strings.HasPrefix(pwdString, "pass=") { + return []byte(pwdString[5:]), nil + } else if strings.HasPrefix(pwdString, "fd=") { + fdStr := pwdString[3:] + fd, err := strconv.Atoi(fdStr) + if err != nil { + return nil, fmt.Errorf("could not parse file descriptor %s", fdStr) + } + f := os.NewFile(uintptr(fd), "pwdfile") + if f == nil { + return nil, fmt.Errorf("%s is not a valid file descriptor", fdStr) + } + defer f.Close() + pwd := make([]byte, 1024) + n, err := f.Read(pwd) + if err != nil { + return nil, fmt.Errorf("could not read from file descriptor: %v", err) + } + return pwd[:n], nil + } + return []byte(pwdString), nil +} + // CreateVTPM create a VTPM proxy device and starts the TPM emulator with it func CreateVTPM(spec *specs.Spec, vtpmdev *specs.LinuxVTPM, devnum int) (*vtpm.VTPM, error) { + encryptionPassword, err := getEncryptionPassword(vtpmdev.EncryptionPassword) + if err != nil { + return nil, err + } - vtpm, err := vtpm.NewVTPM(vtpmdev.StatePath, vtpmdev.StatePathIsManaged, vtpmdev.TPMVersion, vtpmdev.CreateCertificates, vtpmdev.RunAs, vtpmdev.PcrBanks) + vtpm, err := vtpm.NewVTPM(vtpmdev.StatePath, vtpmdev.StatePathIsManaged, vtpmdev.TPMVersion, vtpmdev.CreateCertificates, vtpmdev.RunAs, vtpmdev.PcrBanks, encryptionPassword) if err != nil { return nil, err } diff --git a/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go b/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go index 6b43dcc2191..cbee06fa5d3 100644 --- a/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go @@ -3,6 +3,7 @@ package vtpmhelper import ( + "fmt" "io/ioutil" "os" "os/exec" @@ -36,7 +37,7 @@ func checkPrerequisites(t *testing.T) { } } -func createVTPM(t *testing.T, tpmversion string, createCertificates bool, runas string) *vtpm.VTPM { +func createVTPM(t *testing.T, tpmversion string, createCertificates bool, runas, encryptionPassword string) *vtpm.VTPM { checkPrerequisites(t) @@ -59,6 +60,7 @@ func createVTPM(t *testing.T, tpmversion string, createCertificates bool, runas TPMVersion: tpmversion, CreateCertificates: createCertificates, RunAs: runas, + EncryptionPassword: encryptionPassword, } myvtpm, err := CreateVTPM(spec, vtpmdev, 0) @@ -82,8 +84,8 @@ func destroyVTPM(t *testing.T, myvtpm *vtpm.VTPM) { } } -func createRestartDestroyVTPM(t *testing.T, tpmversion string, createCertificates bool, runas string) { - myvtpm := createVTPM(t, tpmversion, createCertificates, runas) +func createRestartDestroyVTPM(t *testing.T, tpmversion string, createCertificates bool, runas, encryptionPassword string) { + myvtpm := createVTPM(t, tpmversion, createCertificates, runas, encryptionPassword) err := myvtpm.Stop(false) if err != nil { @@ -102,11 +104,55 @@ func createRestartDestroyVTPM(t *testing.T, tpmversion string, createCertificate } func TestCreateVTPM2(t *testing.T) { - createRestartDestroyVTPM(t, "", true, "root") - createRestartDestroyVTPM(t, "", false, "0") - createRestartDestroyVTPM(t, "2", true, "0") + createRestartDestroyVTPM(t, "", true, "root", "") + createRestartDestroyVTPM(t, "", false, "0", "") + createRestartDestroyVTPM(t, "2", true, "0", "") } func TestCreateVTPM12(t *testing.T) { - createRestartDestroyVTPM(t, "1.2", true, "root") + createRestartDestroyVTPM(t, "1.2", true, "root", "") +} + +func TestCreateEncryptedVTPM_Pipe(t *testing.T) { + checkPrerequisites(t) + + piper, pipew, err := os.Pipe() + if err != nil { + t.Fatalf("Could not create pipe") + } + defer piper.Close() + + password := "123456" + + // pass password via write to pipe + go func() { + n, err := pipew.Write([]byte(password)) + if err != nil { + t.Fatalf("Could not write to pipe: %v", err) + } + if n != len(password) { + t.Fatalf("Could not write all data to pipe") + } + pipew.Close() + }() + createRestartDestroyVTPM(t, "", true, "root", fmt.Sprintf("fd=%d", piper.Fd())) +} + +func TestCreateEncryptedVTPM_File(t *testing.T) { + fil, err := ioutil.TempFile("", "passwordfile") + if err != nil { + t.Fatalf("Could not create temporary file: %v", err) + } + defer os.Remove(fil.Name()) + + _, err = fil.WriteString("123456") + if err != nil { + t.Fatalf("Could not write to temporary file: %v", err) + } + createRestartDestroyVTPM(t, "", true, "root", fmt.Sprintf("file=%s", fil.Name())) +} + +func TestCreateEncryptedVTPM_Direct(t *testing.T) { + createRestartDestroyVTPM(t, "", true, "root", "pass=123456") + createRestartDestroyVTPM(t, "", true, "root", "123456") } diff --git a/libcontainer/vtpm/vtpm.go b/libcontainer/vtpm/vtpm.go index e7ffd5cd54b..a4811f01ecf 100644 --- a/libcontainer/vtpm/vtpm.go +++ b/libcontainer/vtpm/vtpm.go @@ -43,6 +43,12 @@ type VTPM struct { // Set of active PCR banks PcrBanks string `json:"pcrbanks"` + // plain text encryption password used by vTPM + encryptionPassword []byte + + // whether an error occurred writing the password to the pipe + passwordPipeError error + // The user under which to run the TPM emulator user string @@ -182,7 +188,7 @@ func hasCapability(capabilities []string, capability string) bool { // with account tss; TPM 2 has more flexibility // // After successful creation of the object the Start() method can be called -func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, createcerts bool, runas string, pcrbanks string) (*VTPM, error) { +func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, createcerts bool, runas string, pcrbanks string, encryptionpassword []byte) (*VTPM, error) { if len(statepath) == 0 { return nil, fmt.Errorf("Missing required statpath for vTPM.") } @@ -231,6 +237,7 @@ func NewVTPM(statepath string, statepathismanaged bool, vtpmversion string, crea Vtpmversion: vtpmversion, CreateCerts: createcerts, PcrBanks: pcrbanks, + encryptionPassword: encryptionpassword, Tpm_dev_num: VTPM_DEV_NUM_INVALID, fd: ILLEGAL_FD, swtpmSetupCaps: swtpmSetupCaps, @@ -453,6 +460,34 @@ func (vtpm *VTPM) chownStatePath() error { return nil } +// setup the password pipe so that we can transfer the TPM state encryption via +// a pipe where the read-end is passed to swtpm / swtpm_setup as a file descriptor +func (vtpm *VTPM) setupPasswordPipe(password []byte) (*os.File, error) { + if !hasCapability(vtpm.swtpmSetupCaps, "cmdarg-pwdfile-fd") { + return nil, fmt.Errorf("Requiring newer version of swtpm for state encryption; needs cmdarg-pwd-fd feature") + } + + piper, pipew, err := os.Pipe() + if err != nil { + return nil, fmt.Errorf("Could not create pipe") + } + vtpm.passwordPipeError = nil + + go func() { + tot := 0 + for tot < len(password) { + var n int + n, vtpm.passwordPipeError = pipew.Write(password) + if vtpm.passwordPipeError != nil { + break + } + tot = tot + n + } + pipew.Close() + }() + return piper, nil +} + // runSwtpmSetup runs swtpm_setup to simulate TPM manufacturing by creating // EK and platform certificates and enabling TPM 2 PCR banks func (vtpm *VTPM) runSwtpmSetup() error { @@ -470,6 +505,16 @@ func (vtpm *VTPM) runSwtpmSetup() error { if vtpm.CreateCerts { cmd.Args = append(cmd.Args, "--create-ek-cert", "--create-platform-cert", "--lock-nvram") } + if len(vtpm.encryptionPassword) > 0 { + piper, err := vtpm.setupPasswordPipe(vtpm.encryptionPassword) + if err != nil { + return err + } + cmd.ExtraFiles = append(cmd.ExtraFiles, piper) + pwdfile_fd := fmt.Sprintf("%d", 3+len(cmd.ExtraFiles)-1) + cmd.Args = append(cmd.Args, "--cipher", "aes-256-cbc", "--pwdfile-fd", pwdfile_fd) + defer piper.Close() + } if vtpm.Vtpmversion == VTPM_VERSION_2 { cmd.Args = append(cmd.Args, "--tpm2") @@ -488,6 +533,10 @@ func (vtpm *VTPM) runSwtpmSetup() error { return fmt.Errorf("swtpm_setup failed: %s\nlog: %s", string(output), vtpm.ReadLog()) } + if vtpm.passwordPipeError != nil { + return fmt.Errorf("Error transferring password using pipe: %v", vtpm.passwordPipeError) + } + return nil } @@ -548,10 +597,24 @@ func (vtpm *VTPM) startSwtpm() error { file := os.NewFile(uintptr(vtpm.fd), "[vtpm]") cmd.ExtraFiles = append(cmd.ExtraFiles, file) + if len(vtpm.encryptionPassword) > 0 { + piper, err := vtpm.setupPasswordPipe(vtpm.encryptionPassword) + if err != nil { + return err + } + cmd.ExtraFiles = append(cmd.ExtraFiles, piper) + cmd.Args = append(cmd.Args, "--key", + fmt.Sprintf("pwdfd=%d,mode=aes-256-cbc,kdf=pbkdf2", 3+len(cmd.ExtraFiles)-1)) + defer piper.Close() + } + output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("swtpm failed on fd %d: %s\nlog: %s", vtpm.fd, string(output), vtpm.ReadLog()) } + if vtpm.passwordPipeError != nil { + return fmt.Errorf("Error transferring password using pipe: %v", vtpm.passwordPipeError) + } vtpm.Pid, err = vtpm.waitForPidFile(10) if err != nil { From d65b66a60a1e1f9e715381f2a490fcfa2ae41d5f Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:50:01 -0400 Subject: [PATCH 15/16] maskedPaths: Add /sys/devices/virtual/tpm Add /sys/devices/virtual/tpm to the mask paths to avoid isolation issues via sysfs for TPM 1.2 Signed-off-by: Stefan Berger --- libcontainer/specconv/example.go | 1 + 1 file changed, 1 insertion(+) diff --git a/libcontainer/specconv/example.go b/libcontainer/specconv/example.go index 8a201bc78dd..782ac4047ab 100644 --- a/libcontainer/specconv/example.go +++ b/libcontainer/specconv/example.go @@ -121,6 +121,7 @@ func Example() *specs.Spec { "/proc/sched_debug", "/sys/firmware", "/proc/scsi", + "/sys/devices/virtual/tpm", }, ReadonlyPaths: []string{ "/proc/bus", From 83ac63c5b290ffd8fec5deb813a5afa2cd669e34 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Jul 2020 11:50:21 -0400 Subject: [PATCH 16/16] vtpm: Put vTPMs into container's cgroup Put vTPMs into a container's cgroup to limit their CPU usage. Signed-off-by: Stefan Berger --- libcontainer/container_linux.go | 6 ++++++ libcontainer/vtpm/vtpm-helper/vtpm_helper.go | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 05cf32e9c9f..b71f65a3553 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -24,6 +24,7 @@ import ( "github.com/opencontainers/runc/libcontainer/intelrdt" "github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/utils" + "github.com/opencontainers/runc/libcontainer/vtpm/vtpm-helper" "github.com/opencontainers/runtime-spec/specs-go" "github.com/checkpoint-restore/go-criu/v4" @@ -391,6 +392,11 @@ func (c *linuxContainer) start(process *Process) error { return err } } + if len(c.config.VTPMs) > 0 { + if err := vtpmhelper.ApplyCGroupVTPMs(c.config.VTPMs, c.cgroupManager); err != nil { + return err + } + } } return nil } diff --git a/libcontainer/vtpm/vtpm-helper/vtpm_helper.go b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go index dcd4cf4ca22..fdd272906b1 100644 --- a/libcontainer/vtpm/vtpm-helper/vtpm_helper.go +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go @@ -10,6 +10,7 @@ import ( "strings" "syscall" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/vtpm" @@ -153,3 +154,13 @@ func DestroyVTPMs(vtpms []*vtpm.VTPM) { vtpm.Stop(vtpm.CreatedStatepath) } } + +// ApplyCGroupVTPMs puts all VTPMs into the given Cgroup manager's cgroup +func ApplyCGroupVTPMs(vtpms []*vtpm.VTPM, cgroupManager cgroups.Manager) error { + for _, vtpm := range vtpms { + if err := cgroupManager.Apply(vtpm.Pid); err != nil { + return fmt.Errorf("cGroupManager failed to apply vtpm with pid %d: %v", vtpm.Pid, err) + } + } + return nil +}