diff --git a/VERSION b/VERSION index aeb3a87c243..3bde53ab14b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-rc10+dev +1.0.0-rc91+dev 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/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 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..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 } @@ -843,6 +849,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/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", 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..fdd272906b1 --- /dev/null +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper.go @@ -0,0 +1,166 @@ +// + build linux + +package vtpmhelper + +import ( + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + "syscall" + + "github.com/opencontainers/runc/libcontainer/cgroups" + "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) +} + +// 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, encryptionPassword) + 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) + } +} + +// 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 +} 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..cbee06fa5d3 --- /dev/null +++ b/libcontainer/vtpm/vtpm-helper/vtpm_helper_test.go @@ -0,0 +1,158 @@ +// + build linux + +package vtpmhelper + +import ( + "fmt" + "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, encryptionPassword 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, + EncryptionPassword: encryptionPassword, + } + + 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, encryptionPassword string) { + myvtpm := createVTPM(t, tpmversion, createCertificates, runas, encryptionPassword) + + 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", "") +} + +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 new file mode 100644 index 00000000000..a4811f01ecf --- /dev/null +++ b/libcontainer/vtpm/vtpm.go @@ -0,0 +1,893 @@ +// + build linux + +package vtpm + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path" + "path/filepath" + "strconv" + "syscall" + "time" + "unsafe" + + "github.com/opencontainers/runc/libcontainer/apparmor" + selinux "github.com/opencontainers/selinux/go-selinux" + + "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"` + + // 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 + + // 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 + + // The AppArmor profile's full path + aaprofile string + + // swtpm_setup capabilities + swtpmSetupCaps []string + + // swtpm capabilities + swtpmCaps []string +} + +// 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 +} + +// 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 +// @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, encryptionpassword []byte) (*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 + + swtpmSetupCaps, err := getSwtpmSetupCapabilities() + if err != nil { + return nil, err + } + swtpmCaps, err := getSwtpmCapabilities() + if err != nil { + return nil, err + } + + return &VTPM{ + user: runas, + StatePath: statepath, + StatePathIsManaged: statepathismanaged, + Vtpmversion: vtpmversion, + CreateCerts: createcerts, + PcrBanks: pcrbanks, + encryptionPassword: encryptionpassword, + Tpm_dev_num: VTPM_DEV_NUM_INVALID, + fd: ILLEGAL_FD, + swtpmSetupCaps: swtpmSetupCaps, + swtpmCaps: swtpmCaps, + }, 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 +} + +// 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 { + // 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 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") + 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()) + } + + if vtpm.passwordPipeError != nil { + return fmt.Errorf("Error transferring password using pipe: %v", vtpm.passwordPipeError) + } + + 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 + } + + err = vtpm.setupAppArmor() + 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()) + 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", flags, + "--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) + + 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 { + return err + } + + err = vtpm.waitForTPMDevice(50) + if err != nil { + return err + } + + vtpm.resetSELinux() + vtpm.resetAppArmor() + + 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.teardownSELinux() + vtpm.teardownAppArmor() + + 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 +} + +// 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 = "" + } +} + +// 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() +} diff --git a/tests/integration/checkpoint.bats b/tests/integration/checkpoint.bats index 3e5cdf10224..00947427cd2 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 @@ -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 @@ -76,7 +75,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 +133,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 @@ -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 @@ -268,7 +269,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 @@ -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 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) +} 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/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. 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