From fbdffb9e9a87d92ab7335a6b1aadeb3c09c7260f Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 30 May 2019 22:16:45 +0530 Subject: [PATCH 01/27] Generate seccomp profile by tracing the syscalls made by the container This commit adds the flag -genseccomp to podman which can trace the syscalls made by a container and create a container profile from all the syscalls collected Signed-off-by: Divyansh Kamboj --- Makefile | 18 +- cmd/oci-trace-hook/oci-trace-hook-run.json | 14 + cmd/oci-trace-hook/oci-trace-hook-stop.json | 18 + cmd/oci-trace-hook/trace.go | 272 +++++++++ cmd/podman/common.go | 3 + cmd/podman/shared/create.go | 6 + cmd/podman/shared/intermediate.go | 1 + go.mod | 3 +- go.sum | 2 + pkg/adapter/containers.go | 11 +- vendor/github.com/iovisor/gobpf/COPYRIGHT.txt | 14 + .../github.com/iovisor/gobpf/LICENSE-bpf.txt | 374 ++++++++++++ vendor/github.com/iovisor/gobpf/LICENSE.txt | 201 +++++++ vendor/github.com/iovisor/gobpf/bcc/module.go | 538 ++++++++++++++++++ vendor/github.com/iovisor/gobpf/bcc/perf.go | 200 +++++++ vendor/github.com/iovisor/gobpf/bcc/symbol.go | 171 ++++++ vendor/github.com/iovisor/gobpf/bcc/table.go | 334 +++++++++++ .../iovisor/gobpf/pkg/cpuonline/cpu_range.go | 43 ++ vendor/modules.txt | 23 +- 19 files changed, 2232 insertions(+), 14 deletions(-) create mode 100644 cmd/oci-trace-hook/oci-trace-hook-run.json create mode 100644 cmd/oci-trace-hook/oci-trace-hook-stop.json create mode 100644 cmd/oci-trace-hook/trace.go create mode 100644 vendor/github.com/iovisor/gobpf/COPYRIGHT.txt create mode 100644 vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt create mode 100644 vendor/github.com/iovisor/gobpf/LICENSE.txt create mode 100644 vendor/github.com/iovisor/gobpf/bcc/module.go create mode 100644 vendor/github.com/iovisor/gobpf/bcc/perf.go create mode 100644 vendor/github.com/iovisor/gobpf/bcc/symbol.go create mode 100644 vendor/github.com/iovisor/gobpf/bcc/table.go create mode 100644 vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go diff --git a/Makefile b/Makefile index 6734ef1b0e..ee1df36f6e 100644 --- a/Makefile +++ b/Makefile @@ -306,7 +306,7 @@ run-perftest: perftest ## Build and run perf tests vagrant-check: BOX=$(BOX) sh ./vagrant.sh -binaries: varlink_generate podman podman-remote ## Build podman +binaries: varlink_generate podman podman-remote oci-trace-hook ## Build podman install.catatonit: ./hack/install_catatonit.sh @@ -394,7 +394,7 @@ changelog: ## Generate changelog $(shell cat $(TMPFILE) >> changelog.txt) $(shell rm $(TMPFILE)) -install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations +install: .gopathok install.bin install.remote install.man install.cni install.systemd install.oci-trace-hook ## Install binaries to system locations install.remote: podman-remote install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR) @@ -498,6 +498,20 @@ endef make all install; \ fi + +HOOK_BIN_DIR ?= /usr/libexec/oci/hooks.d/ +HOOK_DIR ?= /usr/share/containers/oci/hooks.d + +install.oci-trace-hook: + install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR) + install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) + install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook + cp cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) + cp cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) + +oci-trace-hook: + $(GO) build -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook + varlink_generate: .gopathok cmd/podman/varlink/iopodman.go ## Generate varlink varlink_api_generate: .gopathok API.md diff --git a/cmd/oci-trace-hook/oci-trace-hook-run.json b/cmd/oci-trace-hook/oci-trace-hook-run.json new file mode 100644 index 0000000000..ca9a47d551 --- /dev/null +++ b/cmd/oci-trace-hook/oci-trace-hook-run.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0", + "hook": { + "path": "/usr/libexec/oci/hooks.d/oci-trace-hook" + }, + "when": { + "annotations": { + "io.podman.trace-syscall": ".*" + } + }, + "stages": [ + "prestart" + ] +} \ No newline at end of file diff --git a/cmd/oci-trace-hook/oci-trace-hook-stop.json b/cmd/oci-trace-hook/oci-trace-hook-stop.json new file mode 100644 index 0000000000..56e3a10cb0 --- /dev/null +++ b/cmd/oci-trace-hook/oci-trace-hook-stop.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0", + "hook": { + "path": "/usr/libexec/oci/hooks.d/oci-trace-hook", + "args": [ + "oci-trace-hook", + "-t" + ] + }, + "when": { + "annotations": { + "io.podman.trace-syscall": ".*" + } + }, + "stages": [ + "poststop" + ] +} \ No newline at end of file diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go new file mode 100644 index 0000000000..a98ef90670 --- /dev/null +++ b/cmd/oci-trace-hook/trace.go @@ -0,0 +1,272 @@ +package main + +import ( + "bufio" + "bytes" + "encoding/binary" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "os/signal" + "strconv" + "strings" + "time" + + "github.com/docker/docker/api/types" + bpf "github.com/iovisor/gobpf/bcc" + spec "github.com/opencontainers/runtime-spec/specs-go" + seccomp "github.com/seccomp/libseccomp-golang" + "github.com/sirupsen/logrus" +) + +// event +type event struct { + Pid uint32 + ID uint32 + // Inum uint + Command [16]byte +} + +type calls map[string]int + +const source string = ` +#include +#include +#include +#include +#include +#include + +BPF_HASH(parent_namespace, u64, unsigned int); +BPF_PERF_OUTPUT(events); + +struct data_t +{ + u32 pid; + u32 id; + char comm[16]; +}; + +int enter_trace(struct tracepoint__raw_syscalls__sys_enter *args) +{ + struct data_t data = {}; + u64 key = 0; + unsigned int zero = 0; + struct task_struct *task; + + data.pid = bpf_get_current_pid_tgid(); + data.id = (int)args->id; + bpf_get_current_comm(&data.comm, sizeof(data.comm)); + + task = (struct task_struct *)bpf_get_current_task(); + struct nsproxy *ns = task->nsproxy; + unsigned int inum = ns->pid_ns_for_children->ns.inum; + + if (data.pid == $PARENT_PID) + { + parent_namespace.update(&key, &inum); + } + unsigned int *parent_inum = parent_namespace.lookup_or_init(&key, &zero); + + if (*parent_inum != inum) + { + return 0; + } + + events.perf_submit(args, &data, sizeof(data)); + return 0; +} +` + +func main() { + + terminate := flag.Bool("t", false, "send SIGINT to floating process") + runBPF := flag.Int("r", 0, "-r [PID] run the BPF function and attach to the pid") + fileName := flag.String("f", "profile.json", "path of the file to save the seccomp profile") + + flag.Parse() + + logfile, err := os.OpenFile("logfile", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + logrus.Errorf("error opening file: %v", err) + } + defer logfile.Close() + formatter := new(logrus.TextFormatter) + formatter.FullTimestamp = true + logrus.SetFormatter(formatter) + logrus.SetOutput(logfile) + if *runBPF > 0 { + if err := runBPFSource(*runBPF, *fileName); err != nil { + logrus.Error(err) + } + } else if *terminate { + if err := sendSIGINT(); err != nil { + logrus.Error(err) + } + } else { + if err := startFloatingProcess(); err != nil { + logrus.Error(err) + } + } + +} + +// Start a process which runs the BPF source and detach the process +func startFloatingProcess() error { + logrus.Println("Starting the floating process") + var s spec.State + reader := bufio.NewReader(os.Stdin) + decoder := json.NewDecoder(reader) + err := decoder.Decode(&s) + if err != nil { + return err + } + pid := s.Pid + fileName := s.Annotations["io.podman.trace-syscall"] + //sysproc := &syscall.SysProcAttr{Noctty: true} + attr := &os.ProcAttr{ + Dir: ".", + Env: os.Environ(), + Files: []*os.File{ + os.Stdin, + nil, + nil, + }, + // Sys: sysproc, + } + if pid > 0 { + process, err := os.StartProcess("/usr/libexec/oci/hooks.d/oci-trace-hook", []string{"/usr/libexec/oci/hooks.d/trace", "-r", strconv.Itoa(pid), "-f", fileName}, attr) + if err != nil { + return fmt.Errorf("cannot launch process err: %q", err.Error()) + } + time.Sleep(2 * time.Second) // Waits 2 seconds to compile using clang and llvm + processPID := process.Pid + f, err := os.Create("pid") + if err != nil { + return fmt.Errorf("cannot write pid to file err:%q", err.Error()) + } + defer f.Close() + f.WriteString(strconv.Itoa(processPID)) + err = process.Release() + if err != nil { + return fmt.Errorf("cannot detach process err:%q", err.Error()) + } + } else { + return fmt.Errorf("container not running") + } + return nil +} + +// run the BPF source and attach it to raw_syscalls:sys_enter tracepoint +func runBPFSource(pid int, fileName string) error { + logrus.Println("Running floating process PID to attach:", pid) + syscalls := make(calls, 303) + src := strings.Replace(source, "$PARENT_PID", strconv.Itoa(pid), -1) + m := bpf.NewModule(src, []string{}) + defer m.Close() + + tracepoint, err := m.LoadTracepoint("enter_trace") + if err != nil { + return err + } + + logrus.Println("Loaded tracepoint") + + if err := m.AttachTracepoint("raw_syscalls:sys_enter", tracepoint); err != nil { + return fmt.Errorf("unable to load tracepoint err:%q", err.Error()) + } + + table := bpf.NewTable(m.TableId("events"), m) + channel := make(chan []byte) + perfMap, err := bpf.InitPerfMap(table, channel) + if err != nil { + return fmt.Errorf("unable to init perf map err:%q", err.Error()) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt) + rsc := false // Reached Seccomp syscall + go func() { + var e event + for { + data := <-channel + err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e) + if err != nil { + logrus.Errorf("failed to decode received data '%s': %s\n", data, err) + continue + } + name, err := getName(e.ID) + if err != nil { + logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) + } + if name == "seccomp" { + rsc = true + continue + } + if rsc { + syscalls[name]++ + } + } + }() + logrus.Println("PerfMap Start") + perfMap.Start() + <-sig + logrus.Println("PerfMap Stop") + perfMap.Stop() + if err := generateProfile(syscalls, fileName); err != nil { + return err + } + return nil +} + +// send SIGINT to the floating process reading the perfbuffer +func sendSIGINT() error { + f, err := ioutil.ReadFile("pid") + + if err != nil { + return err + } + + Spid := string(f) + + pid, _ := strconv.Atoi(Spid) + p, _ := os.FindProcess(pid) + p.Signal(os.Interrupt) + return nil +} + +// generate the seccomp profile from the syscalls provided +func generateProfile(c calls, fileName string) error { + s := types.Seccomp{} + var names []string + for s, t := range c { + if t > 0 { + names = append(names, s) + } + } + s.DefaultAction = types.ActErrno + + s.Syscalls = []*types.Syscall{ + &types.Syscall{ + Action: types.ActAllow, + Names: names, + Args: []*types.Arg{}, + }, + } + sJSON, err := json.Marshal(s) + if err != nil { + return err + } + if err := ioutil.WriteFile(fileName, sJSON, 0644); err != nil { + return err + } + return nil +} + +// get the name of the syscall from it's ID +func getName(id uint32) (string, error) { + name, err := seccomp.ScmpSyscall(id).GetName() + return name, err +} diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 0115e6ef18..790379fe22 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -502,6 +502,9 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "workdir", "w", "", "Working directory inside the container", ) + createFlags.String( + "genseccomp", "", "Generate a seccomp profile for the container", + ) } func getFormat(c *cliconfig.PodmanCommand) (string, error) { diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index fc8197721d..8fa976781b 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -12,6 +12,8 @@ import ( "syscall" "time" + "github.com/containers/libpod/pkg/errorhandling" + "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/libpod" @@ -586,6 +588,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. annotations[splitAnnotation[0]] = splitAnnotation[1] } + if c.IsSet("genseccomp") { + annotations["io.podman.trace-syscall"] = c.String("genseccomp") + } + // WORKING DIRECTORY workDir := "/" if c.IsSet("workdir") { diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index cccdd1bea3..c737073f19 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -461,6 +461,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["volume"] = newCRStringArray(c, "volume") m["volumes-from"] = newCRStringSlice(c, "volumes-from") m["workdir"] = newCRString(c, "workdir") + m["genseccomp"] = newCRString(c, "genseccomp") // global flag if !remote { m["authfile"] = newCRString(c, "authfile") diff --git a/go.mod b/go.mod index 117f85b3cb..b45dc34c7b 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/hpcloud/tail v1.0.0 github.com/imdario/mergo v0.3.7 // indirect github.com/json-iterator/go v1.1.7 + github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/magiconair/properties v1.8.1 // indirect github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 // indirect @@ -78,7 +79,7 @@ require ( github.com/rogpeppe/fastuuid v1.1.0 // indirect github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 // indirect github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f // indirect - github.com/seccomp/libseccomp-golang v0.9.1 // indirect + github.com/seccomp/libseccomp-golang v0.9.1 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 63c2ddd309..00a0bbe9fd 100644 --- a/go.sum +++ b/go.sum @@ -303,6 +303,8 @@ github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 h1:y3708HMZuw+OpIdFPPzwSMC12f/LncanSCpVAaR8jCY= +github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368/go.mod h1:+5U5qu5UOu8YJ5oHVLvWKH7/Dr5QNHU7mZ2RfPEeXg8= github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8= github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 47db5c0dc5..fc453d687d 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -426,7 +426,16 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } } - + // if c.IsSet("genseccomp") { + // profile, err := ioutil.ReadFile(ctr.StaticDir() + "/profile.json") + // if err != nil { + // logrus.Errorln(err) + // } + // destFile := c.String("genseccomp") + // if err = ioutil.WriteFile(destFile, profile, 0644); err != nil { + // logrus.Errorf("Cannot write file %s to the disk", destFile) + // } + // } return exitCode, nil } diff --git a/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt b/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt new file mode 100644 index 0000000000..1eae73fb26 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt @@ -0,0 +1,14 @@ +Copyright 2016 PLUMgrid +Copyright 2016 Kinvolk + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt b/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt new file mode 100644 index 0000000000..9d7d80e613 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt @@ -0,0 +1,374 @@ +The file at /elf/include/bpf.h is a copy of the Linux kernel file +/include/uapi/linux/bpf.h, retrieved from version 4.17, Git commit +36f9814, available at +https://raw.githubusercontent.com/torvalds/linux/36f9814a494a874d5a0f44843544b4b2539022db/include/uapi/linux/bpf.h. +It is provided here in unmodified source code form. + +As indicated in the file, it is licensed under the GNU General Public License, +version 2.0, with the Linux-syscall-note. Copies of the Linux-syscall-note and +GPL-2.0 license text are included below. gobpf has included this header file +in this repository with the intention of using it solely in the manner +described in the Linux-syscall-note. + += = = = = + +Linux-syscall-note: + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + +Linus Torvalds + += = = = = + +GPL-2.0: + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/vendor/github.com/iovisor/gobpf/LICENSE.txt b/vendor/github.com/iovisor/gobpf/LICENSE.txt new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor 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, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/iovisor/gobpf/bcc/module.go b/vendor/github.com/iovisor/gobpf/bcc/module.go new file mode 100644 index 0000000000..34a40e4936 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/module.go @@ -0,0 +1,538 @@ +// Copyright 2016 PLUMgrid +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "fmt" + "regexp" + "runtime" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/iovisor/gobpf/pkg/cpuonline" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +*/ +import "C" + +// Module type +type Module struct { + p unsafe.Pointer + funcs map[string]int + kprobes map[string]int + uprobes map[string]int + tracepoints map[string]int + rawTracepoints map[string]int + perfEvents map[string][]int +} + +type compileRequest struct { + code string + cflags []string + rspCh chan *Module +} + +const ( + BPF_PROBE_ENTRY = iota + BPF_PROBE_RETURN +) + +const ( + XDP_FLAGS_UPDATE_IF_NOEXIST = uint32(1) << iota + XDP_FLAGS_SKB_MODE + XDP_FLAGS_DRV_MODE + XDP_FLAGS_HW_MODE + XDP_FLAGS_MODES = XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE + XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_MODES +) + +var ( + defaultCflags []string + compileCh chan compileRequest + bpfInitOnce sync.Once +) + +func bpfInit() { + defaultCflags = []string{ + fmt.Sprintf("-DNUMCPUS=%d", runtime.NumCPU()), + } + compileCh = make(chan compileRequest) + go compile() +} + +// NewModule constructor +func newModule(code string, cflags []string) *Module { + cflagsC := make([]*C.char, len(defaultCflags)+len(cflags)) + defer func() { + for _, cflag := range cflagsC { + C.free(unsafe.Pointer(cflag)) + } + }() + for i, cflag := range cflags { + cflagsC[i] = C.CString(cflag) + } + for i, cflag := range defaultCflags { + cflagsC[len(cflags)+i] = C.CString(cflag) + } + cs := C.CString(code) + defer C.free(unsafe.Pointer(cs)) + c := C.bpf_module_create_c_from_string(cs, 2, (**C.char)(&cflagsC[0]), C.int(len(cflagsC)), (C.bool)(true)) + if c == nil { + return nil + } + return &Module{ + p: c, + funcs: make(map[string]int), + kprobes: make(map[string]int), + uprobes: make(map[string]int), + tracepoints: make(map[string]int), + rawTracepoints: make(map[string]int), + perfEvents: make(map[string][]int), + } +} + +// NewModule asynchronously compiles the code, generates a new BPF +// module and returns it. +func NewModule(code string, cflags []string) *Module { + bpfInitOnce.Do(bpfInit) + ch := make(chan *Module) + compileCh <- compileRequest{code, cflags, ch} + return <-ch +} + +func compile() { + for { + req := <-compileCh + req.rspCh <- newModule(req.code, req.cflags) + } +} + +// Close takes care of closing all kprobes opened by this modules and +// destroys the underlying libbpf module. +func (bpf *Module) Close() { + C.bpf_module_destroy(bpf.p) + for k, v := range bpf.kprobes { + C.bpf_close_perf_event_fd((C.int)(v)) + evNameCS := C.CString(k) + C.bpf_detach_kprobe(evNameCS) + C.free(unsafe.Pointer(evNameCS)) + } + for k, v := range bpf.uprobes { + C.bpf_close_perf_event_fd((C.int)(v)) + evNameCS := C.CString(k) + C.bpf_detach_uprobe(evNameCS) + C.free(unsafe.Pointer(evNameCS)) + } + for k, v := range bpf.tracepoints { + C.bpf_close_perf_event_fd((C.int)(v)) + parts := strings.SplitN(k, ":", 2) + tpCategoryCS := C.CString(parts[0]) + tpNameCS := C.CString(parts[1]) + C.bpf_detach_tracepoint(tpCategoryCS, tpNameCS) + C.free(unsafe.Pointer(tpCategoryCS)) + C.free(unsafe.Pointer(tpNameCS)) + } + for _, vs := range bpf.perfEvents { + for _, v := range vs { + C.bpf_close_perf_event_fd((C.int)(v)) + } + } + for _, fd := range bpf.funcs { + syscall.Close(fd) + } +} + +// GetProgramTag returns a tag for ebpf program under passed fd +func (bpf *Module) GetProgramTag(fd int) (tag uint64, err error) { + _, err = C.bpf_prog_get_tag(C.int(fd), (*C.ulonglong)(unsafe.Pointer(&tag))) + return tag, err +} + +// LoadNet loads a program of type BPF_PROG_TYPE_SCHED_ACT. +func (bpf *Module) LoadNet(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_SCHED_ACT, 0, 0) +} + +// LoadKprobe loads a program of type BPF_PROG_TYPE_KPROBE. +func (bpf *Module) LoadKprobe(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) +} + +// LoadTracepoint loads a program of type BPF_PROG_TYPE_TRACEPOINT +func (bpf *Module) LoadTracepoint(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_TRACEPOINT, 0, 0) +} + +// LoadRawTracepoint loads a program of type BPF_PROG_TYPE_RAW_TRACEPOINT +func (bpf *Module) LoadRawTracepoint(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, 0) +} + +// LoadPerfEvent loads a program of type BPF_PROG_TYPE_PERF_EVENT +func (bpf *Module) LoadPerfEvent(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_PERF_EVENT, 0, 0) +} + +// LoadUprobe loads a program of type BPF_PROG_TYPE_KPROBE. +func (bpf *Module) LoadUprobe(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) +} + +// Load a program. +func (bpf *Module) Load(name string, progType int, logLevel, logSize uint) (int, error) { + fd, ok := bpf.funcs[name] + if ok { + return fd, nil + } + fd, err := bpf.load(name, progType, logLevel, logSize) + if err != nil { + return -1, err + } + bpf.funcs[name] = fd + return fd, nil +} + +func (bpf *Module) load(name string, progType int, logLevel, logSize uint) (int, error) { + nameCS := C.CString(name) + defer C.free(unsafe.Pointer(nameCS)) + start := (*C.struct_bpf_insn)(C.bpf_function_start(bpf.p, nameCS)) + size := C.int(C.bpf_function_size(bpf.p, nameCS)) + license := C.bpf_module_license(bpf.p) + version := C.bpf_module_kern_version(bpf.p) + if start == nil { + return -1, fmt.Errorf("Module: unable to find %s", name) + } + var logBuf []byte + var logBufP *C.char + if logSize > 0 { + logBuf = make([]byte, logSize) + logBufP = (*C.char)(unsafe.Pointer(&logBuf[0])) + } + fd, err := C.bcc_prog_load(uint32(progType), nameCS, start, size, license, version, C.int(logLevel), logBufP, C.uint(len(logBuf))) + if fd < 0 { + return -1, fmt.Errorf("error loading BPF program: %v", err) + } + return int(fd), nil +} + +var kprobeRegexp = regexp.MustCompile("[+.]") +var uprobeRegexp = regexp.MustCompile("[^a-zA-Z0-9_]") + +func (bpf *Module) attachProbe(evName string, attachType uint32, fnName string, fd int, maxActive int) error { + if _, ok := bpf.kprobes[evName]; ok { + return nil + } + + evNameCS := C.CString(evName) + fnNameCS := C.CString(fnName) + res, err := C.bpf_attach_kprobe(C.int(fd), attachType, evNameCS, fnNameCS, (C.uint64_t)(0), C.int(maxActive)) + C.free(unsafe.Pointer(evNameCS)) + C.free(unsafe.Pointer(fnNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF kprobe: %v", err) + } + bpf.kprobes[evName] = int(res) + return nil +} + +func (bpf *Module) attachUProbe(evName string, attachType uint32, path string, addr uint64, fd, pid int) error { + evNameCS := C.CString(evName) + binaryPathCS := C.CString(path) + res, err := C.bpf_attach_uprobe(C.int(fd), attachType, evNameCS, binaryPathCS, (C.uint64_t)(addr), (C.pid_t)(pid)) + C.free(unsafe.Pointer(evNameCS)) + C.free(unsafe.Pointer(binaryPathCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF uprobe: %v", err) + } + bpf.uprobes[evName] = int(res) + return nil +} + +// AttachKprobe attaches a kprobe fd to a function. +func (bpf *Module) AttachKprobe(fnName string, fd int, maxActive int) error { + evName := "p_" + kprobeRegexp.ReplaceAllString(fnName, "_") + + return bpf.attachProbe(evName, BPF_PROBE_ENTRY, fnName, fd, maxActive) +} + +// AttachKretprobe attaches a kretprobe fd to a function. +func (bpf *Module) AttachKretprobe(fnName string, fd int, maxActive int) error { + evName := "r_" + kprobeRegexp.ReplaceAllString(fnName, "_") + + return bpf.attachProbe(evName, BPF_PROBE_RETURN, fnName, fd, maxActive) +} + +// AttachTracepoint attaches a tracepoint fd to a function +// The 'name' argument is in the format 'category:name' +func (bpf *Module) AttachTracepoint(name string, fd int) error { + if _, ok := bpf.tracepoints[name]; ok { + return nil + } + + parts := strings.SplitN(name, ":", 2) + if len(parts) < 2 { + return fmt.Errorf("failed to parse tracepoint name, expected %q, got %q", "category:name", name) + } + + tpCategoryCS := C.CString(parts[0]) + tpNameCS := C.CString(parts[1]) + + res, err := C.bpf_attach_tracepoint(C.int(fd), tpCategoryCS, tpNameCS) + + C.free(unsafe.Pointer(tpCategoryCS)) + C.free(unsafe.Pointer(tpNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF tracepoint: %v", err) + } + bpf.tracepoints[name] = int(res) + return nil +} + +// AttachRawTracepoint attaches a raw tracepoint fd to a function +// The 'name' argument is in the format 'name', there is no category +func (bpf *Module) AttachRawTracepoint(name string, fd int) error { + if _, ok := bpf.rawTracepoints[name]; ok { + return nil + } + + tpNameCS := C.CString(name) + + res, err := C.bpf_attach_raw_tracepoint(C.int(fd), tpNameCS) + + C.free(unsafe.Pointer(tpNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF tracepoint: %v", err) + } + bpf.rawTracepoints[name] = int(res) + return nil +} + +// AttachPerfEvent attaches a perf event fd to a function +// Argument 'evType' is a member of 'perf_type_id' enum in the kernel +// header 'include/uapi/linux/perf_event.h'. Argument 'evConfig' +// is one of PERF_COUNT_* constants in the same file. +func (bpf *Module) AttachPerfEvent(evType, evConfig int, samplePeriod int, sampleFreq int, pid, cpu, groupFd, fd int) error { + key := fmt.Sprintf("%d:%d", evType, evConfig) + if _, ok := bpf.perfEvents[key]; ok { + return nil + } + + res := []int{} + + if cpu > 0 { + r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(cpu), C.int(groupFd)) + if r < 0 { + return fmt.Errorf("failed to attach BPF perf event: %v", err) + } + + res = append(res, int(r)) + } else { + cpus, err := cpuonline.Get() + if err != nil { + return fmt.Errorf("failed to determine online cpus: %v", err) + } + + for _, i := range cpus { + r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(i), C.int(groupFd)) + if r < 0 { + return fmt.Errorf("failed to attach BPF perf event: %v", err) + } + + res = append(res, int(r)) + } + } + + bpf.perfEvents[key] = res + + return nil +} + +// AttachUprobe attaches a uprobe fd to the symbol in the library or binary 'name' +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given to attach to, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachUprobe(name, symbol string, fd, pid int) error { + path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) + if err != nil { + return err + } + evName := fmt.Sprintf("p_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) + return bpf.attachUProbe(evName, BPF_PROBE_ENTRY, path, addr, fd, pid) +} + +// AttachMatchingUprobes attaches a uprobe fd to all symbols in the library or binary +// 'name' that match a given pattern. +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachMatchingUprobes(name, match string, fd, pid int) error { + symbols, err := matchUserSymbols(name, match) + if err != nil { + return fmt.Errorf("unable to match symbols: %s", err) + } + if len(symbols) == 0 { + return fmt.Errorf("no symbols matching %s for %s found", match, name) + } + for _, symbol := range symbols { + if err := bpf.AttachUprobe(name, symbol.name, fd, pid); err != nil { + return err + } + } + return nil +} + +// AttachUretprobe attaches a uretprobe fd to the symbol in the library or binary 'name' +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given to attach to, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachUretprobe(name, symbol string, fd, pid int) error { + path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) + if err != nil { + return err + } + evName := fmt.Sprintf("r_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) + return bpf.attachUProbe(evName, BPF_PROBE_RETURN, path, addr, fd, pid) +} + +// AttachMatchingUretprobes attaches a uretprobe fd to all symbols in the library or binary +// 'name' that match a given pattern. +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachMatchingUretprobes(name, match string, fd, pid int) error { + symbols, err := matchUserSymbols(name, match) + if err != nil { + return fmt.Errorf("unable to match symbols: %s", err) + } + if len(symbols) == 0 { + return fmt.Errorf("no symbols matching %s for %s found", match, name) + } + for _, symbol := range symbols { + if err := bpf.AttachUretprobe(name, symbol.name, fd, pid); err != nil { + return err + } + } + return nil +} + +// TableSize returns the number of tables in the module. +func (bpf *Module) TableSize() uint64 { + size := C.bpf_num_tables(bpf.p) + return uint64(size) +} + +// TableId returns the id of a table. +func (bpf *Module) TableId(name string) C.size_t { + cs := C.CString(name) + defer C.free(unsafe.Pointer(cs)) + return C.bpf_table_id(bpf.p, cs) +} + +// TableDesc returns a map with table properties (name, fd, ...). +func (bpf *Module) TableDesc(id uint64) map[string]interface{} { + i := C.size_t(id) + return map[string]interface{}{ + "name": C.GoString(C.bpf_table_name(bpf.p, i)), + "fd": int(C.bpf_table_fd_id(bpf.p, i)), + "key_size": uint64(C.bpf_table_key_size_id(bpf.p, i)), + "leaf_size": uint64(C.bpf_table_leaf_size_id(bpf.p, i)), + "key_desc": C.GoString(C.bpf_table_key_desc_id(bpf.p, i)), + "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(bpf.p, i)), + } +} + +// TableIter returns a receveier channel to iterate over entries. +func (bpf *Module) TableIter() <-chan map[string]interface{} { + ch := make(chan map[string]interface{}) + go func() { + size := C.bpf_num_tables(bpf.p) + for i := C.size_t(0); i < size; i++ { + ch <- bpf.TableDesc(uint64(i)) + } + close(ch) + }() + return ch +} + +func (bpf *Module) attachXDP(devName string, fd int, flags uint32) error { + devNameCS := C.CString(devName) + res, err := C.bpf_attach_xdp(devNameCS, C.int(fd), C.uint32_t(flags)) + defer C.free(unsafe.Pointer(devNameCS)) + + if res != 0 || err != nil { + return fmt.Errorf("failed to attach BPF xdp to device %v: %v", devName, err) + } + return nil +} + +// AttachXDP attaches a xdp fd to a device. +func (bpf *Module) AttachXDP(devName string, fd int) error { + return bpf.attachXDP(devName, fd, 0) +} + +// AttachXDPWithFlags attaches a xdp fd to a device with flags. +func (bpf *Module) AttachXDPWithFlags(devName string, fd int, flags uint32) error { + return bpf.attachXDP(devName, fd, flags) +} + +// RemoveXDP removes any xdp from this device. +func (bpf *Module) RemoveXDP(devName string) error { + return bpf.attachXDP(devName, -1, 0) +} + +func GetSyscallFnName(name string) string { + return GetSyscallPrefix() + name +} + +func GetSyscallPrefix() string { + _, err := bccResolveName("", "sys_bpf", -1) + if err == nil { + return "sys_" + } + + _, err = bccResolveName("", "__x64_sys_bpf", -1) + if err == nil { + return "__x64_sys_" + } + + return "sys_" +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/perf.go b/vendor/github.com/iovisor/gobpf/bcc/perf.go new file mode 100644 index 0000000000..a831715032 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/perf.go @@ -0,0 +1,200 @@ +// Copyright 2016 Kinvolk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "encoding/binary" + "fmt" + "sync" + "unsafe" + + "github.com/iovisor/gobpf/pkg/cpuonline" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +#include + +// perf_reader_raw_cb as defined in bcc libbpf.h +// typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size); +extern void callback_to_go(void*, void*, int); +*/ +import "C" + +type PerfMap struct { + table *Table + readers []*C.struct_perf_reader + stop chan bool +} + +type callbackData struct { + receiverChan chan []byte +} + +const BPF_PERF_READER_PAGE_CNT = 8 + +var byteOrder binary.ByteOrder +var callbackRegister = make(map[uint64]*callbackData) +var callbackIndex uint64 +var mu sync.Mutex + +// In lack of binary.HostEndian ... +func init() { + byteOrder = determineHostByteOrder() +} + +func registerCallback(data *callbackData) uint64 { + mu.Lock() + defer mu.Unlock() + callbackIndex++ + for callbackRegister[callbackIndex] != nil { + callbackIndex++ + } + callbackRegister[callbackIndex] = data + return callbackIndex +} + +func unregisterCallback(i uint64) { + mu.Lock() + defer mu.Unlock() + delete(callbackRegister, i) +} + +func lookupCallback(i uint64) *callbackData { + return callbackRegister[i] +} + +// Gateway function as required with CGO Go >= 1.6 +// "If a C-program wants a function pointer, a gateway function has to +// be written. This is because we can't take the address of a Go +// function and give that to C-code since the cgo tool will generate a +// stub in C that should be called." +//export callback_to_go +func callback_to_go(cbCookie unsafe.Pointer, raw unsafe.Pointer, rawSize C.int) { + callbackData := lookupCallback(uint64(uintptr(cbCookie))) + callbackData.receiverChan <- C.GoBytes(raw, rawSize) +} + +// GetHostByteOrder returns the current byte-order. +func GetHostByteOrder() binary.ByteOrder { + return byteOrder +} + +func determineHostByteOrder() binary.ByteOrder { + var i int32 = 0x01020304 + u := unsafe.Pointer(&i) + pb := (*byte)(u) + b := *pb + if b == 0x04 { + return binary.LittleEndian + } + + return binary.BigEndian +} + +// InitPerfMap initializes a perf map with a receiver channel. +func InitPerfMap(table *Table, receiverChan chan []byte) (*PerfMap, error) { + fd := table.Config()["fd"].(int) + keySize := table.Config()["key_size"].(uint64) + leafSize := table.Config()["leaf_size"].(uint64) + + if keySize != 4 || leafSize != 4 { + return nil, fmt.Errorf("passed table has wrong size") + } + + callbackDataIndex := registerCallback(&callbackData{ + receiverChan, + }) + + key := make([]byte, keySize) + leaf := make([]byte, leafSize) + keyP := unsafe.Pointer(&key[0]) + leafP := unsafe.Pointer(&leaf[0]) + + readers := []*C.struct_perf_reader{} + + cpus, err := cpuonline.Get() + if err != nil { + return nil, fmt.Errorf("failed to determine online cpus: %v", err) + } + + for _, cpu := range cpus { + reader, err := bpfOpenPerfBuffer(cpu, callbackDataIndex) + if err != nil { + return nil, fmt.Errorf("failed to open perf buffer: %v", err) + } + + perfFd := C.perf_reader_fd((*C.struct_perf_reader)(reader)) + + readers = append(readers, (*C.struct_perf_reader)(reader)) + + byteOrder.PutUint32(leaf, uint32(perfFd)) + + r, err := C.bpf_update_elem(C.int(fd), keyP, leafP, 0) + if r != 0 { + return nil, fmt.Errorf("unable to initialize perf map: %v", err) + } + r = C.bpf_get_next_key(C.int(fd), keyP, keyP) + if r != 0 { + break + } + } + return &PerfMap{ + table, + readers, + make(chan bool), + }, nil +} + +// Start to poll the perf map reader and send back event data +// over the connected channel. +func (pm *PerfMap) Start() { + go pm.poll(500) +} + +// Stop to poll the perf map readers after a maximum of 500ms +// (the timeout we use for perf_reader_poll). Ideally we would +// have a way to cancel the poll, but perf_reader_poll doesn't +// support that yet. +func (pm *PerfMap) Stop() { + pm.stop <- true +} + +func (pm *PerfMap) poll(timeout int) { + for { + select { + case <-pm.stop: + return + default: + C.perf_reader_poll(C.int(len(pm.readers)), &pm.readers[0], C.int(timeout)) + } + } +} + +func bpfOpenPerfBuffer(cpu uint, callbackDataIndex uint64) (unsafe.Pointer, error) { + cpuC := C.int(cpu) + reader, err := C.bpf_open_perf_buffer( + (C.perf_reader_raw_cb)(unsafe.Pointer(C.callback_to_go)), + nil, + unsafe.Pointer(uintptr(callbackDataIndex)), + -1, cpuC, BPF_PERF_READER_PAGE_CNT) + if reader == nil { + return nil, fmt.Errorf("failed to open perf buffer: %v", err) + } + return reader, nil +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/symbol.go b/vendor/github.com/iovisor/gobpf/bcc/symbol.go new file mode 100644 index 0000000000..e1fe176141 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/symbol.go @@ -0,0 +1,171 @@ +// Copyright 2017 Louis McCormack +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "fmt" + "regexp" + "sync" + "unsafe" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +#include +extern void foreach_symbol_callback(char*, uint64_t); +*/ +import "C" + +type symbolAddress struct { + name string + addr uint64 +} + +//symbolCache will cache module lookups +var symbolCache = struct { + cache map[string][]*symbolAddress + currentModule string + lock *sync.Mutex +}{ + cache: map[string][]*symbolAddress{}, + currentModule: "", + lock: &sync.Mutex{}, +} + +type bccSymbol struct { + name *C.char + demangleName *C.char + module *C.char + offset C.ulonglong +} + +type bccSymbolOption struct { + useDebugFile int + checkDebugFileCrc int + useSymbolType uint32 +} + +// resolveSymbolPath returns the file and offset to locate symname in module +func resolveSymbolPath(module string, symname string, addr uint64, pid int) (string, uint64, error) { + if pid == -1 { + pid = 0 + } + + modname, offset, err := bccResolveSymname(module, symname, addr, pid) + if err != nil { + return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) + } + + return modname, offset, nil +} + +func bccResolveSymname(module string, symname string, addr uint64, pid int) (string, uint64, error) { + symbol := &bccSymbol{} + symbolC := (*C.struct_bcc_symbol)(unsafe.Pointer(symbol)) + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + symnameCS := C.CString(symname) + defer C.free(unsafe.Pointer(symnameCS)) + + res, err := C.bcc_resolve_symname(moduleCS, symnameCS, (C.uint64_t)(addr), C.int(pid), nil, symbolC) + if res < 0 { + return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) + } + + return C.GoString(symbolC.module), (uint64)(symbolC.offset), nil +} + +func bccResolveName(module, symname string, pid int) (uint64, error) { + symbol := &bccSymbolOption{} + symbolC := (*C.struct_bcc_symbol_option)(unsafe.Pointer(symbol)) + + pidC := C.int(pid) + cache := C.bcc_symcache_new(pidC, symbolC) + + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + + nameCS := C.CString(symname) + defer C.free(unsafe.Pointer(nameCS)) + + var addr uint64 + addrC := C.uint64_t(addr) + res := C.bcc_symcache_resolve_name(cache, moduleCS, nameCS, &addrC) + if res < 0 { + return 0, fmt.Errorf("unable to locate symbol %s in module %s", symname, module) + } + + return addr, nil +} + +// getUserSymbolsAndAddresses finds a list of symbols associated with a module, +// along with their addresses. The results are cached in the symbolCache and +// returned +func getUserSymbolsAndAddresses(module string) ([]*symbolAddress, error) { + symbolCache.lock.Lock() + defer symbolCache.lock.Unlock() + // return previously cached list if it exists + if _, ok := symbolCache.cache[module]; ok { + return symbolCache.cache[module], nil + } + + symbolCache.cache[module] = []*symbolAddress{} + symbolCache.currentModule = module + + if err := bccForeachSymbol(module); err != nil { + return nil, err + } + + return symbolCache.cache[module], nil +} + +func matchUserSymbols(module, match string) ([]*symbolAddress, error) { + r, err := regexp.Compile(match) + if err != nil { + return nil, fmt.Errorf("invalid regex %s : %s", match, err) + } + matchedSymbols := []*symbolAddress{} + symbols, err := getUserSymbolsAndAddresses(module) + if err != nil { + return nil, err + } + for _, sym := range symbols { + if r.MatchString(sym.name) { + matchedSymbols = append(matchedSymbols, sym) + } + } + return matchedSymbols, nil +} + +// foreach_symbol_callback is a gateway function that will be exported to C +// so that it can be referenced as a function pointer +//export foreach_symbol_callback +func foreach_symbol_callback(symname *C.char, addr C.uint64_t) { + symbolCache.cache[symbolCache.currentModule] = + append(symbolCache.cache[symbolCache.currentModule], &symbolAddress{C.GoString(symname), (uint64)(addr)}) +} + +func bccForeachSymbol(module string) error { + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + res := C.bcc_foreach_function_symbol(moduleCS, (C.SYM_CB)(unsafe.Pointer(C.foreach_symbol_callback))) + if res < 0 { + return fmt.Errorf("unable to list symbols for %s", module) + } + return nil +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/table.go b/vendor/github.com/iovisor/gobpf/bcc/table.go new file mode 100644 index 0000000000..d745b80049 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/table.go @@ -0,0 +1,334 @@ +// Copyright 2016 PLUMgrid +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "bytes" + "errors" + "fmt" + "os" + "unsafe" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +*/ +import "C" + +var errIterationFailed = errors.New("table.Iter: leaf for next key not found") + +// Table references a BPF table. The zero value cannot be used. +type Table struct { + id C.size_t + module *Module +} + +// New tables returns a refernce to a BPF table. +func NewTable(id C.size_t, module *Module) *Table { + return &Table{ + id: id, + module: module, + } +} + +// ID returns the table id. +func (table *Table) ID() string { + return C.GoString(C.bpf_table_name(table.module.p, table.id)) +} + +// Name returns the table name. +func (table *Table) Name() string { + return C.GoString(C.bpf_table_name(table.module.p, table.id)) +} + +// Config returns the table properties (name, fd, ...). +func (table *Table) Config() map[string]interface{} { + mod := table.module.p + return map[string]interface{}{ + "name": C.GoString(C.bpf_table_name(mod, table.id)), + "fd": int(C.bpf_table_fd_id(mod, table.id)), + "key_size": uint64(C.bpf_table_key_size_id(mod, table.id)), + "leaf_size": uint64(C.bpf_table_leaf_size_id(mod, table.id)), + "key_desc": C.GoString(C.bpf_table_key_desc_id(mod, table.id)), + "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(mod, table.id)), + } +} + +func (table *Table) LeafStrToBytes(leafStr string) ([]byte, error) { + mod := table.module.p + + leafSize := C.bpf_table_leaf_size_id(mod, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + leafCS := C.CString(leafStr) + defer C.free(unsafe.Pointer(leafCS)) + + r := C.bpf_table_leaf_sscanf(mod, table.id, leafCS, leafP) + if r != 0 { + return nil, fmt.Errorf("error scanning leaf (%v) from string", leafStr) + } + return leaf, nil +} + +func (table *Table) KeyStrToBytes(keyStr string) ([]byte, error) { + mod := table.module.p + + keySize := C.bpf_table_key_size_id(mod, table.id) + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + + keyCS := C.CString(keyStr) + defer C.free(unsafe.Pointer(keyCS)) + + r := C.bpf_table_key_sscanf(mod, table.id, keyCS, keyP) + if r != 0 { + return nil, fmt.Errorf("error scanning key (%v) from string", keyStr) + } + return key, nil +} + +// KeyBytesToStr returns the given key value formatted using the bcc-table's key string printer. +func (table *Table) KeyBytesToStr(key []byte) (string, error) { + keySize := len(key) + keyP := unsafe.Pointer(&key[0]) + + keyStr := make([]byte, keySize*8) + keyStrP := (*C.char)(unsafe.Pointer(&keyStr[0])) + + if res := C.bpf_table_key_snprintf(table.module.p, table.id, keyStrP, C.size_t(len(keyStr)), keyP); res != 0 { + return "", fmt.Errorf("formatting table-key: %d", res) + } + + return string(keyStr[:bytes.IndexByte(keyStr, 0)]), nil +} + +// LeafBytesToStr returns the given leaf value formatted using the bcc-table's leaf string printer. +func (table *Table) LeafBytesToStr(leaf []byte) (string, error) { + leafSize := len(leaf) + leafP := unsafe.Pointer(&leaf[0]) + + leafStr := make([]byte, leafSize*8) + leafStrP := (*C.char)(unsafe.Pointer(&leafStr[0])) + + if res := C.bpf_table_leaf_snprintf(table.module.p, table.id, leafStrP, C.size_t(len(leafStr)), leafP); res != 0 { + return "", fmt.Errorf("formatting table-leaf: %d", res) + } + + return string(leafStr[:bytes.IndexByte(leafStr, 0)]), nil +} + +// Get takes a key and returns the value or nil, and an 'ok' style indicator. +func (table *Table) Get(key []byte) ([]byte, error) { + mod := table.module.p + fd := C.bpf_table_fd_id(mod, table.id) + + keyP := unsafe.Pointer(&key[0]) + + leafSize := C.bpf_table_leaf_size_id(mod, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + r, err := C.bpf_lookup_elem(fd, keyP, leafP) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + return nil, fmt.Errorf("Table.Get: key %v: %v", keyStr, err) + } + + return leaf, nil +} + +// GetP takes a key and returns the value or nil. +func (table *Table) GetP(key unsafe.Pointer) (unsafe.Pointer, error) { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + leafSize := C.bpf_table_leaf_size_id(table.module.p, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + _, err := C.bpf_lookup_elem(fd, key, leafP) + if err != nil { + return nil, err + } + return leafP, nil +} + +// Set a key to a value. +func (table *Table) Set(key, leaf []byte) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + keyP := unsafe.Pointer(&key[0]) + leafP := unsafe.Pointer(&leaf[0]) + + r, err := C.bpf_update_elem(fd, keyP, leafP, 0) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + leafStr, errL := table.LeafBytesToStr(leaf) + if errL != nil { + leafStr = fmt.Sprintf("%v", leaf) + } + + return fmt.Errorf("Table.Set: update %v to %v: %v", keyStr, leafStr, err) + } + + return nil +} + +// SetP a key to a value as unsafe.Pointer. +func (table *Table) SetP(key, leaf unsafe.Pointer) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + _, err := C.bpf_update_elem(fd, key, leaf, 0) + if err != nil { + return err + } + + return nil +} + +// Delete a key. +func (table *Table) Delete(key []byte) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + keyP := unsafe.Pointer(&key[0]) + r, err := C.bpf_delete_elem(fd, keyP) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + return fmt.Errorf("Table.Delete: key %v: %v", keyStr, err) + } + return nil +} + +// DeleteP a key. +func (table *Table) DeleteP(key unsafe.Pointer) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + _, err := C.bpf_delete_elem(fd, key) + if err != nil { + return err + } + return nil +} + +// DeleteAll deletes all entries from the table +func (table *Table) DeleteAll() error { + mod := table.module.p + fd := C.bpf_table_fd_id(mod, table.id) + + keySize := C.bpf_table_key_size_id(mod, table.id) + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + for res := C.bpf_get_first_key(fd, keyP, keySize); res == 0; res = C.bpf_get_next_key(fd, keyP, keyP) { + r, err := C.bpf_delete_elem(fd, keyP) + if r != 0 { + return fmt.Errorf("Table.DeleteAll: unable to delete element: %v", err) + } + } + return nil +} + +// TableIterator contains the current position for iteration over a *bcc.Table and provides methods for iteration. +type TableIterator struct { + table *Table + fd C.int + + err error + + key []byte + leaf []byte +} + +// Iter returns an iterator to list all table entries available as raw bytes. +func (table *Table) Iter() *TableIterator { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + return &TableIterator{ + table: table, + fd: fd, + } +} + +// Next looks up the next element and return true if one is available. +func (it *TableIterator) Next() bool { + if it.err != nil { + return false + } + + if it.key == nil { + keySize := C.bpf_table_key_size_id(it.table.module.p, it.table.id) + + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + if res, err := C.bpf_get_first_key(it.fd, keyP, keySize); res != 0 { + if !os.IsNotExist(err) { + it.err = err + } + return false + } + + leafSize := C.bpf_table_leaf_size_id(it.table.module.p, it.table.id) + leaf := make([]byte, leafSize) + + it.key = key + it.leaf = leaf + } else { + keyP := unsafe.Pointer(&it.key[0]) + if res, err := C.bpf_get_next_key(it.fd, keyP, keyP); res != 0 { + if !os.IsNotExist(err) { + it.err = err + } + return false + } + } + + keyP := unsafe.Pointer(&it.key[0]) + leafP := unsafe.Pointer(&it.leaf[0]) + if res, err := C.bpf_lookup_elem(it.fd, keyP, leafP); res != 0 { + it.err = errIterationFailed + if !os.IsNotExist(err) { + it.err = err + } + return false + } + + return true +} + +// Key returns the current key value of the iterator, if the most recent call to Next returned true. +// The slice is valid only until the next call to Next. +func (it *TableIterator) Key() []byte { + return it.key +} + +// Leaf returns the current leaf value of the iterator, if the most recent call to Next returned true. +// The slice is valid only until the next call to Next. +func (it *TableIterator) Leaf() []byte { + return it.leaf +} + +// Err returns the last error that ocurred while table.Iter oder iter.Next +func (it *TableIterator) Err() error { + return it.err +} diff --git a/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go b/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go new file mode 100644 index 0000000000..aa87830b2d --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go @@ -0,0 +1,43 @@ +package cpuonline + +import ( + "io/ioutil" + "strconv" + "strings" +) + +const cpuOnline = "/sys/devices/system/cpu/online" + +// loosely based on https://github.com/iovisor/bcc/blob/v0.3.0/src/python/bcc/utils.py#L15 +func readCPURange(cpuRangeStr string) ([]uint, error) { + var cpus []uint + cpuRangeStr = strings.Trim(cpuRangeStr, "\n ") + for _, cpuRange := range strings.Split(cpuRangeStr, ",") { + rangeOp := strings.SplitN(cpuRange, "-", 2) + first, err := strconv.ParseUint(rangeOp[0], 10, 32) + if err != nil { + return nil, err + } + if len(rangeOp) == 1 { + cpus = append(cpus, uint(first)) + continue + } + last, err := strconv.ParseUint(rangeOp[1], 10, 32) + if err != nil { + return nil, err + } + for n := first; n <= last; n++ { + cpus = append(cpus, uint(n)) + } + } + return cpus, nil +} + +// Get returns a slice with the online CPUs, for example `[0, 2, 3]` +func Get() ([]uint, error) { + buf, err := ioutil.ReadFile(cpuOnline) + if err != nil { + return nil, err + } + return readCPURange(string(buf)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3a27eea12b..187af5cb9a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -179,7 +179,7 @@ github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/metrics # github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 github.com/docker/docker/pkg/signal -github.com/docker/docker/pkg/homedir +github.com/docker/docker/api/types github.com/docker/docker/oci/caps github.com/docker/docker/pkg/namesgenerator github.com/docker/docker/pkg/term @@ -188,21 +188,22 @@ github.com/docker/docker/profiles/seccomp github.com/docker/docker/pkg/parsers github.com/docker/docker/api/types/versions github.com/docker/docker/pkg/idtools +github.com/docker/docker/api/types/container +github.com/docker/docker/api/types/filters +github.com/docker/docker/api/types/mount +github.com/docker/docker/api/types/network +github.com/docker/docker/api/types/registry +github.com/docker/docker/api/types/swarm github.com/docker/docker/errdefs github.com/docker/docker/pkg/term/windows github.com/docker/docker/pkg/longpath -github.com/docker/docker/api/types github.com/docker/docker/pkg/parsers/kernel -github.com/docker/docker/api/types/registry -github.com/docker/docker/api/types/swarm github.com/docker/docker/pkg/fileutils github.com/docker/docker/pkg/stdcopy github.com/docker/docker/pkg/system github.com/docker/docker/client -github.com/docker/docker/api/types/container -github.com/docker/docker/api/types/filters -github.com/docker/docker/api/types/mount -github.com/docker/docker/api/types/network +github.com/docker/docker/api/types/blkiodev +github.com/docker/docker/api/types/strslice github.com/docker/docker/api/types/swarm/runtime github.com/docker/docker/pkg/pools github.com/docker/docker/pkg/mount @@ -211,8 +212,6 @@ github.com/docker/docker/api/types/events github.com/docker/docker/api/types/image github.com/docker/docker/api/types/time github.com/docker/docker/api/types/volume -github.com/docker/docker/api/types/blkiodev -github.com/docker/docker/api/types/strslice # github.com/docker/docker-credential-helpers v0.6.2 github.com/docker/docker-credential-helpers/credentials github.com/docker/docker-credential-helpers/client @@ -276,6 +275,10 @@ github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.0.0 github.com/inconshreveable/mousetrap # github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 +# github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 +github.com/iovisor/gobpf/bcc +github.com/iovisor/gobpf/pkg/cpuonline +# github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.7 github.com/json-iterator/go From 1c007904b2ec35a6ef52e8e15bbf3cdc6fd2d28e Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Tue, 23 Jul 2019 21:32:37 +0530 Subject: [PATCH 02/27] Add comment to event struct Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index a98ef90670..4e31637362 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -21,11 +21,10 @@ import ( "github.com/sirupsen/logrus" ) -// event +// event struct used to read the perf_buffer type event struct { - Pid uint32 - ID uint32 - // Inum uint + Pid uint32 + ID uint32 Command [16]byte } From 38372dfb595c9adbdc1797c2a583eda4a8913a8b Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 25 Jul 2019 06:56:19 +0530 Subject: [PATCH 03/27] Refactor and comments, read syscalls after the exec call Signed-off-by: Divyansh Kamboj --- Makefile | 9 ++++----- cmd/oci-trace-hook/trace.go | 15 ++++++++++++--- cmd/podman/common.go | 2 +- cmd/podman/shared/create.go | 4 ++-- cmd/podman/shared/intermediate.go | 2 +- pkg/adapter/containers.go | 10 ---------- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index ee1df36f6e..83c6e0a5a1 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,8 @@ BINDIR ?= ${PREFIX}/bin LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers +HOOK_BIN_DIR ?= /usr/libexec/oci/hooks.d/ +HOOK_DIR ?= /usr/share/containers/oci/hooks.d ETCDIR ?= /etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system @@ -499,15 +501,12 @@ endef fi -HOOK_BIN_DIR ?= /usr/libexec/oci/hooks.d/ -HOOK_DIR ?= /usr/share/containers/oci/hooks.d - install.oci-trace-hook: install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR) install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook - cp cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) - cp cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) oci-trace-hook: $(GO) build -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 4e31637362..3cb47b1021 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "os" "os/signal" + "path/filepath" "strconv" "strings" "time" @@ -83,7 +84,11 @@ func main() { terminate := flag.Bool("t", false, "send SIGINT to floating process") runBPF := flag.Int("r", 0, "-r [PID] run the BPF function and attach to the pid") - fileName := flag.String("f", "profile.json", "path of the file to save the seccomp profile") + defaultProfilePath, err := filepath.Abs("./profile.json") + if err != nil { + logrus.Error(err) + } + fileName := flag.String("f", defaultProfilePath, "path of the file to save the seccomp profile") flag.Parse() @@ -186,7 +191,8 @@ func runBPFSource(pid int, fileName string) error { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) - rsc := false // Reached Seccomp syscall + rsc := false // Reached Seccomp syscall + rexec := false // Reached the execve from runc go func() { var e event for { @@ -203,8 +209,11 @@ func runBPFSource(pid int, fileName string) error { if name == "seccomp" { rsc = true continue + } else if name == "execve" { + rexec = true + continue } - if rsc { + if rsc && rexec { syscalls[name]++ } } diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 790379fe22..cf20beb660 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -503,7 +503,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Working directory inside the container", ) createFlags.String( - "genseccomp", "", "Generate a seccomp profile for the container", + "generate-seccomp", "", "Generate a seccomp profile for the container", ) } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 8fa976781b..7a0cb37b55 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -588,8 +588,8 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. annotations[splitAnnotation[0]] = splitAnnotation[1] } - if c.IsSet("genseccomp") { - annotations["io.podman.trace-syscall"] = c.String("genseccomp") + if c.IsSet("generate-seccomp") { + annotations["io.podman.trace-syscall"] = c.String("generate-seccomp") } // WORKING DIRECTORY diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index c737073f19..cd4007c9a2 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -461,7 +461,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["volume"] = newCRStringArray(c, "volume") m["volumes-from"] = newCRStringSlice(c, "volumes-from") m["workdir"] = newCRString(c, "workdir") - m["genseccomp"] = newCRString(c, "genseccomp") + m["generate-seccomp"] = newCRString(c, "generate-seccomp") // global flag if !remote { m["authfile"] = newCRString(c, "authfile") diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index fc453d687d..050f443db4 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -426,16 +426,6 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } } - // if c.IsSet("genseccomp") { - // profile, err := ioutil.ReadFile(ctr.StaticDir() + "/profile.json") - // if err != nil { - // logrus.Errorln(err) - // } - // destFile := c.String("genseccomp") - // if err = ioutil.WriteFile(destFile, profile, 0644); err != nil { - // logrus.Errorf("Cannot write file %s to the disk", destFile) - // } - // } return exitCode, nil } From b1144648b58194cb0ec1cd5f267e1d41ebd21044 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 25 Jul 2019 07:36:59 +0530 Subject: [PATCH 04/27] Send signal to parent process when tracepoint attaches Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 44 ++++++++++++++++++++++++++++++------- cmd/podman/shared/create.go | 6 ++++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 3cb47b1021..c2130fa62e 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -13,7 +13,7 @@ import ( "path/filepath" "strconv" "strings" - "time" + "syscall" "github.com/docker/docker/api/types" bpf "github.com/iovisor/gobpf/bcc" @@ -22,7 +22,7 @@ import ( "github.com/sirupsen/logrus" ) -// event struct used to read the perf_buffer +// event struct used to read the perf_buffer, reference type event struct { Pid uint32 ID uint32 @@ -42,6 +42,7 @@ const source string = ` BPF_HASH(parent_namespace, u64, unsigned int); BPF_PERF_OUTPUT(events); +// data_t struct data_t { u32 pid; @@ -84,6 +85,7 @@ func main() { terminate := flag.Bool("t", false, "send SIGINT to floating process") runBPF := flag.Int("r", 0, "-r [PID] run the BPF function and attach to the pid") + defaultProfilePath, err := filepath.Abs("./profile.json") if err != nil { logrus.Error(err) @@ -92,7 +94,11 @@ func main() { flag.Parse() - logfile, err := os.OpenFile("logfile", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + logfilePath, err := filepath.Abs("./logfile") + if err != nil { + logrus.Error(err) + } + logfile, err := os.OpenFile(logfilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { logrus.Errorf("error opening file: %v", err) } @@ -141,11 +147,17 @@ func startFloatingProcess() error { // Sys: sysproc, } if pid > 0 { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGUSR1) + process, err := os.StartProcess("/usr/libexec/oci/hooks.d/oci-trace-hook", []string{"/usr/libexec/oci/hooks.d/trace", "-r", strconv.Itoa(pid), "-f", fileName}, attr) if err != nil { return fmt.Errorf("cannot launch process err: %q", err.Error()) } - time.Sleep(2 * time.Second) // Waits 2 seconds to compile using clang and llvm + + // time.Sleep(2 * time.Second) // Waits 2 seconds to compile using clang and llvm + <-sig + processPID := process.Pid f, err := os.Create("pid") if err != nil { @@ -157,6 +169,7 @@ func startFloatingProcess() error { if err != nil { return fmt.Errorf("cannot detach process err:%q", err.Error()) } + } else { return fmt.Errorf("container not running") } @@ -165,6 +178,13 @@ func startFloatingProcess() error { // run the BPF source and attach it to raw_syscalls:sys_enter tracepoint func runBPFSource(pid int, fileName string) error { + ppid := os.Getppid() + parentProcess, err := os.FindProcess(ppid) + + if err != nil { + return fmt.Errorf("cannot find the parent process pid %d : %q", ppid, err) + } + logrus.Println("Running floating process PID to attach:", pid) syscalls := make(calls, 303) src := strings.Replace(source, "$PARENT_PID", strconv.Itoa(pid), -1) @@ -182,6 +202,12 @@ func runBPFSource(pid int, fileName string) error { return fmt.Errorf("unable to load tracepoint err:%q", err.Error()) } + // send a signal to the parent process to signify the compilation has been completed + err = parentProcess.Signal(syscall.SIGUSR1) + if err != nil { + return err + } + table := bpf.NewTable(m.TableId("events"), m) channel := make(chan []byte) perfMap, err := bpf.InitPerfMap(table, channel) @@ -191,8 +217,8 @@ func runBPFSource(pid int, fileName string) error { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) - rsc := false // Reached Seccomp syscall - rexec := false // Reached the execve from runc + rsc := false // Reached Seccomp syscall + rexec := true // Reached the execve from runc go func() { var e event for { @@ -211,7 +237,6 @@ func runBPFSource(pid int, fileName string) error { continue } else if name == "execve" { rexec = true - continue } if rsc && rexec { syscalls[name]++ @@ -240,7 +265,10 @@ func sendSIGINT() error { Spid := string(f) pid, _ := strconv.Atoi(Spid) - p, _ := os.FindProcess(pid) + p, err := os.FindProcess(pid) + if err != nil { + return fmt.Errorf("cannot find process with PID %d \nerror msg: %q", pid, err.Error()) + } p.Signal(os.Interrupt) return nil } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 7a0cb37b55..34afafe24d 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -589,7 +589,11 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. } if c.IsSet("generate-seccomp") { - annotations["io.podman.trace-syscall"] = c.String("generate-seccomp") + profilePath, err := filepath.Abs(c.String("generate-seccomp")) + if err != nil { + logrus.Error(err) + } + annotations["io.podman.trace-syscall"] = profilePath } // WORKING DIRECTORY From 72c20c32bcd61831612d0bd047b8bafdcf7c54ef Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 26 Jul 2019 17:18:27 +0530 Subject: [PATCH 05/27] Refactor and comments | use cgo structs Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/oci-trace-hook-run.json | 6 +- cmd/oci-trace-hook/trace.go | 95 +++++++++++++--------- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/cmd/oci-trace-hook/oci-trace-hook-run.json b/cmd/oci-trace-hook/oci-trace-hook-run.json index ca9a47d551..53a2a7c5d1 100644 --- a/cmd/oci-trace-hook/oci-trace-hook-run.json +++ b/cmd/oci-trace-hook/oci-trace-hook-run.json @@ -1,7 +1,11 @@ { "version": "1.0.0", "hook": { - "path": "/usr/libexec/oci/hooks.d/oci-trace-hook" + "path": "/usr/libexec/oci/hooks.d/oci-trace-hook", + "args": [ + "oci-trace-hook", + "-s" + ] }, "when": { "annotations": { diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index c2130fa62e..fceeb0881e 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -22,15 +22,20 @@ import ( "github.com/sirupsen/logrus" ) -// event struct used to read the perf_buffer, reference -type event struct { - Pid uint32 - ID uint32 - Command [16]byte -} - -type calls map[string]int - +// #include +// typedef struct +// { +// __u32 Pid; +// __u32 Id; +// char Comm[16]; +// } syscall_data; +import "C" + +// the source is a bpf program compiled at runtime. Some macro's like +// BPF_HASH and BPF_PERF_OUTPUT are expanded during compilation +// by bcc. $PARENT_PID get's replaced before compilation with the PID of the container +// Complete documentation is available at +// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md const source string = ` #include #include @@ -39,33 +44,47 @@ const source string = ` #include #include +// BPF_HASH used to store the PID namespace of the parent PID +// of the processes inside the container. BPF_HASH(parent_namespace, u64, unsigned int); + +// Opens a custom BPF table to push data to user space via perf ring buffer BPF_PERF_OUTPUT(events); -// data_t -struct data_t +// data_t used to store the data received from the event +struct syscall_data { - u32 pid; - u32 id; - char comm[16]; + // PID of the process + u32 Pid; + // syscall number + u32 Id; + // command which is making the syscall + char Comm[16]; }; +// enter_trace : function is attached to the kernel tracepoint raw_syscalls:sys_enter it is +// called whenever a syscall is made. The function stores the pid_namespace (task->nsproxy->pid_ns_for_children->ns.inum) of the PID which +// starts the container in the BPF_HASH called parent_namespace. +// The data of the syscall made by the process with the same pid_namespace as the parent_namespace is pushed to +// userspace using perf ring buffer + +// specification of args from sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format int enter_trace(struct tracepoint__raw_syscalls__sys_enter *args) { - struct data_t data = {}; + struct syscall_data data = {}; u64 key = 0; unsigned int zero = 0; struct task_struct *task; - data.pid = bpf_get_current_pid_tgid(); - data.id = (int)args->id; - bpf_get_current_comm(&data.comm, sizeof(data.comm)); + data.Pid = bpf_get_current_pid_tgid(); + data.Id = (int)args->id; + bpf_get_current_comm(&data.Comm, sizeof(data.Comm)); task = (struct task_struct *)bpf_get_current_task(); struct nsproxy *ns = task->nsproxy; unsigned int inum = ns->pid_ns_for_children->ns.inum; - if (data.pid == $PARENT_PID) + if (data.Pid == $PARENT_PID) { parent_namespace.update(&key, &inum); } @@ -85,16 +104,17 @@ func main() { terminate := flag.Bool("t", false, "send SIGINT to floating process") runBPF := flag.Int("r", 0, "-r [PID] run the BPF function and attach to the pid") + fileName := flag.String("f", "", "path of the file to save the seccomp profile") + + start := flag.Bool("s", false, "Start the hook which would execute a process to trace syscalls made by the container") + flag.Parse() - defaultProfilePath, err := filepath.Abs("./profile.json") + profilePath, err := filepath.Abs(*fileName) if err != nil { logrus.Error(err) } - fileName := flag.String("f", defaultProfilePath, "path of the file to save the seccomp profile") - flag.Parse() - - logfilePath, err := filepath.Abs("./logfile") + logfilePath, err := filepath.Abs("log") if err != nil { logrus.Error(err) } @@ -102,30 +122,31 @@ func main() { if err != nil { logrus.Errorf("error opening file: %v", err) } + defer logfile.Close() formatter := new(logrus.TextFormatter) formatter.FullTimestamp = true logrus.SetFormatter(formatter) logrus.SetOutput(logfile) + if *runBPF > 0 { - if err := runBPFSource(*runBPF, *fileName); err != nil { + logrus.Println("Filepath : ", profilePath) + if err := runBPFSource(*runBPF, profilePath); err != nil { logrus.Error(err) } } else if *terminate { if err := sendSIGINT(); err != nil { logrus.Error(err) } - } else { + } else if *start { if err := startFloatingProcess(); err != nil { logrus.Error(err) } } - } // Start a process which runs the BPF source and detach the process func startFloatingProcess() error { - logrus.Println("Starting the floating process") var s spec.State reader := bufio.NewReader(os.Stdin) decoder := json.NewDecoder(reader) @@ -135,7 +156,6 @@ func startFloatingProcess() error { } pid := s.Pid fileName := s.Annotations["io.podman.trace-syscall"] - //sysproc := &syscall.SysProcAttr{Noctty: true} attr := &os.ProcAttr{ Dir: ".", Env: os.Environ(), @@ -144,7 +164,6 @@ func startFloatingProcess() error { nil, nil, }, - // Sys: sysproc, } if pid > 0 { sig := make(chan os.Signal, 1) @@ -155,7 +174,6 @@ func startFloatingProcess() error { return fmt.Errorf("cannot launch process err: %q", err.Error()) } - // time.Sleep(2 * time.Second) // Waits 2 seconds to compile using clang and llvm <-sig processPID := process.Pid @@ -177,7 +195,8 @@ func startFloatingProcess() error { } // run the BPF source and attach it to raw_syscalls:sys_enter tracepoint -func runBPFSource(pid int, fileName string) error { +func runBPFSource(pid int, profilePath string) error { + ppid := os.Getppid() parentProcess, err := os.FindProcess(ppid) @@ -186,7 +205,7 @@ func runBPFSource(pid int, fileName string) error { } logrus.Println("Running floating process PID to attach:", pid) - syscalls := make(calls, 303) + syscalls := make(map[string]int, 303) src := strings.Replace(source, "$PARENT_PID", strconv.Itoa(pid), -1) m := bpf.NewModule(src, []string{}) defer m.Close() @@ -220,7 +239,7 @@ func runBPFSource(pid int, fileName string) error { rsc := false // Reached Seccomp syscall rexec := true // Reached the execve from runc go func() { - var e event + e := C.syscall_data{} for { data := <-channel err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e) @@ -228,9 +247,9 @@ func runBPFSource(pid int, fileName string) error { logrus.Errorf("failed to decode received data '%s': %s\n", data, err) continue } - name, err := getName(e.ID) + name, err := getName(uint32(e.Id)) if err != nil { - logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) + logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.Id, name) } if name == "seccomp" { rsc = true @@ -248,7 +267,7 @@ func runBPFSource(pid int, fileName string) error { <-sig logrus.Println("PerfMap Stop") perfMap.Stop() - if err := generateProfile(syscalls, fileName); err != nil { + if err := generateProfile(syscalls, profilePath); err != nil { return err } return nil @@ -274,7 +293,7 @@ func sendSIGINT() error { } // generate the seccomp profile from the syscalls provided -func generateProfile(c calls, fileName string) error { +func generateProfile(c map[string]int, fileName string) error { s := types.Seccomp{} var names []string for s, t := range c { From 4316edf2c18082aa3ed2d6714e11abb316840634 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Mon, 29 Jul 2019 19:41:45 +0530 Subject: [PATCH 06/27] Start writing test Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 7 ++- test/e2e/run_generate_seccomp_test.go | 62 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 test/e2e/run_generate_seccomp_test.go diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index fceeb0881e..e61ab493c8 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -114,7 +114,7 @@ func main() { logrus.Error(err) } - logfilePath, err := filepath.Abs("log") + logfilePath, err := filepath.Abs("trace-log") if err != nil { logrus.Error(err) } @@ -128,7 +128,6 @@ func main() { formatter.FullTimestamp = true logrus.SetFormatter(formatter) logrus.SetOutput(logfile) - if *runBPF > 0 { logrus.Println("Filepath : ", profilePath) if err := runBPFSource(*runBPF, profilePath); err != nil { @@ -177,7 +176,7 @@ func startFloatingProcess() error { <-sig processPID := process.Pid - f, err := os.Create("pid") + f, err := os.Create("pidfile") if err != nil { return fmt.Errorf("cannot write pid to file err:%q", err.Error()) } @@ -275,7 +274,7 @@ func runBPFSource(pid int, profilePath string) error { // send SIGINT to the floating process reading the perfbuffer func sendSIGINT() error { - f, err := ioutil.ReadFile("pid") + f, err := ioutil.ReadFile("pidfile") if err != nil { return err diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go new file mode 100644 index 0000000000..9311f14179 --- /dev/null +++ b/test/e2e/run_generate_seccomp_test.go @@ -0,0 +1,62 @@ +package integration + +import ( + "io/ioutil" + "os" + "time" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman generate profile", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + SkipIfRootless() + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + }) + FIt("podman generate-seccomp generates profile that works ", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + fileName := tmpfile.Name() + tmpfile.Close() + defer os.Remove(fileName) + time.Sleep(time.Second * 2) + session := podmanTest.Podman([]string{"run", "--generate-seccomp=" + fileName, ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "true"}) + result.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + FIt("podman generate-seccomp should fail to run command barred in logfile", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + fileName := tmpfile.Name() + tmpfile.Close() + defer os.Remove(fileName) + time.Sleep(time.Second * 2) + session := podmanTest.Podman([]string{"run", "--generate-seccomp=" + fileName, ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "sync"}) + result.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) +}) From 60cac60b2f6333b03e797216f93fb66e3ed65170 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Mon, 29 Jul 2019 19:45:48 +0530 Subject: [PATCH 07/27] minor corrections in spelling Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index e61ab493c8..48955c6a63 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -220,7 +220,7 @@ func runBPFSource(pid int, profilePath string) error { return fmt.Errorf("unable to load tracepoint err:%q", err.Error()) } - // send a signal to the parent process to signify the compilation has been completed + // send a signal to the parent process to indicate the compilation has been completed err = parentProcess.Signal(syscall.SIGUSR1) if err != nil { return err @@ -235,8 +235,8 @@ func runBPFSource(pid int, profilePath string) error { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) - rsc := false // Reached Seccomp syscall - rexec := true // Reached the execve from runc + rsc := false // Reached Seccomp syscall + rexec := false // Reached the execve from runc go func() { e := C.syscall_data{} for { From 33a63ac448212eea04f94258733419ddf000106f Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Tue, 30 Jul 2019 18:55:41 +0530 Subject: [PATCH 08/27] remove cgo use default structs Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 46 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 48955c6a63..39c1fbfc03 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -22,14 +22,15 @@ import ( "github.com/sirupsen/logrus" ) -// #include -// typedef struct -// { -// __u32 Pid; -// __u32 Id; -// char Comm[16]; -// } syscall_data; -import "C" +// event : read data from the perf ring buffer from the +type event struct { + // PID of the process making the syscall + Pid uint32 + // syscall number of the syscall + ID uint32 + // Command which makes the syscall + Command [16]byte +} // the source is a bpf program compiled at runtime. Some macro's like // BPF_HASH and BPF_PERF_OUTPUT are expanded during compilation @@ -55,11 +56,11 @@ BPF_PERF_OUTPUT(events); struct syscall_data { // PID of the process - u32 Pid; + u32 pid; // syscall number - u32 Id; + u32 id; // command which is making the syscall - char Comm[16]; + char comm[16]; }; // enter_trace : function is attached to the kernel tracepoint raw_syscalls:sys_enter it is @@ -76,15 +77,15 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter *args) unsigned int zero = 0; struct task_struct *task; - data.Pid = bpf_get_current_pid_tgid(); - data.Id = (int)args->id; - bpf_get_current_comm(&data.Comm, sizeof(data.Comm)); + data.pid = bpf_get_current_pid_tgid(); + data.id = (int)args->id; + bpf_get_current_comm(&data.comm, sizeof(data.comm)); task = (struct task_struct *)bpf_get_current_task(); struct nsproxy *ns = task->nsproxy; unsigned int inum = ns->pid_ns_for_children->ns.inum; - if (data.Pid == $PARENT_PID) + if (data.pid == $PARENT_PID) { parent_namespace.update(&key, &inum); } @@ -235,10 +236,9 @@ func runBPFSource(pid int, profilePath string) error { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) - rsc := false // Reached Seccomp syscall - rexec := false // Reached the execve from runc + rsc := false // Reached Seccomp syscall go func() { - e := C.syscall_data{} + var e event for { data := <-channel err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e) @@ -246,17 +246,15 @@ func runBPFSource(pid int, profilePath string) error { logrus.Errorf("failed to decode received data '%s': %s\n", data, err) continue } - name, err := getName(uint32(e.Id)) + name, err := getName(uint32(e.ID)) if err != nil { - logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.Id, name) + logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) } if name == "seccomp" { rsc = true continue - } else if name == "execve" { - rexec = true } - if rsc && rexec { + if rsc { syscalls[name]++ } } @@ -303,7 +301,7 @@ func generateProfile(c map[string]int, fileName string) error { s.DefaultAction = types.ActErrno s.Syscalls = []*types.Syscall{ - &types.Syscall{ + { Action: types.ActAllow, Names: names, Args: []*types.Arg{}, From d2c0806af7d91bf7682e1491793e5cc759bb0b48 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 1 Aug 2019 19:07:57 +0530 Subject: [PATCH 09/27] Record syscall after prctl() has been called Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 16 ++++++++-------- pkg/adapter/containers.go | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 39c1fbfc03..12cbecfbad 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -22,11 +22,11 @@ import ( "github.com/sirupsen/logrus" ) -// event : read data from the perf ring buffer from the +// event struct used to read data from the perf ring buffer from the type event struct { // PID of the process making the syscall Pid uint32 - // syscall number of the syscall + // syscall number ID uint32 // Command which makes the syscall Command [16]byte @@ -57,7 +57,7 @@ struct syscall_data { // PID of the process u32 pid; - // syscall number + // the syscall number u32 id; // command which is making the syscall char comm[16]; @@ -236,7 +236,7 @@ func runBPFSource(pid int, profilePath string) error { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) - rsc := false // Reached Seccomp syscall + reachedPRCTL := false // Reached PRCTL syscall go func() { var e event for { @@ -250,11 +250,11 @@ func runBPFSource(pid int, profilePath string) error { if err != nil { logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) } - if name == "seccomp" { - rsc = true - continue + // syscalls are not recorded until prctl() is called + if name == "prctl" { + reachedPRCTL = true } - if rsc { + if reachedPRCTL { syscalls[name]++ } } diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 050f443db4..a468c887a8 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -322,6 +322,9 @@ func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateV // Run a libpod container func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) { + if c.IsSet("rm") && c.IsSet("generate-seccomp") { + return exitCode, errors.Errorf("cannot use rm with generate-seccomp") + } results := shared.NewIntermediateLayer(&c.PodmanCommand, false) ctr, createConfig, err := shared.CreateContainer(ctx, &results, r.Runtime) From 70f70d0643fe9b1595162d58340218ade2eb8e9e Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 2 Aug 2019 00:57:02 +0530 Subject: [PATCH 10/27] Use --annotation instead of flag for generate-seccomp Signed-off-by: Divyansh Kamboj --- cmd/podman/common.go | 3 --- cmd/podman/shared/create.go | 14 +++++++------- cmd/podman/shared/intermediate.go | 1 - pkg/adapter/containers.go | 3 --- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/cmd/podman/common.go b/cmd/podman/common.go index cf20beb660..0115e6ef18 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -502,9 +502,6 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "workdir", "w", "", "Working directory inside the container", ) - createFlags.String( - "generate-seccomp", "", "Generate a seccomp profile for the container", - ) } func getFormat(c *cliconfig.PodmanCommand) (string, error) { diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 34afafe24d..576301106a 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -12,8 +12,6 @@ import ( "syscall" "time" - "github.com/containers/libpod/pkg/errorhandling" - "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/libpod" @@ -588,12 +586,14 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. annotations[splitAnnotation[0]] = splitAnnotation[1] } - if c.IsSet("generate-seccomp") { - profilePath, err := filepath.Abs(c.String("generate-seccomp")) - if err != nil { - logrus.Error(err) + if annotations["io.podman.trace-syscall"] != "" { + if !filepath.IsAbs(annotations["io.podman.trace-syscall"]) { + return nil, errors.Errorf("Invalid Path, Please enter an absolute path") + } + + if c.IsSet("rm") { + return nil, errors.Errorf("cannot use --rm while generating a SECCOMP profile") } - annotations["io.podman.trace-syscall"] = profilePath } // WORKING DIRECTORY diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index cd4007c9a2..cccdd1bea3 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -461,7 +461,6 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["volume"] = newCRStringArray(c, "volume") m["volumes-from"] = newCRStringSlice(c, "volumes-from") m["workdir"] = newCRString(c, "workdir") - m["generate-seccomp"] = newCRString(c, "generate-seccomp") // global flag if !remote { m["authfile"] = newCRString(c, "authfile") diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index a468c887a8..050f443db4 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -322,9 +322,6 @@ func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateV // Run a libpod container func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) { - if c.IsSet("rm") && c.IsSet("generate-seccomp") { - return exitCode, errors.Errorf("cannot use rm with generate-seccomp") - } results := shared.NewIntermediateLayer(&c.PodmanCommand, false) ctr, createConfig, err := shared.CreateContainer(ctx, &results, r.Runtime) From 9151f0044302f6c2103ff555e36f3bedc1c9860c Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 2 Aug 2019 01:43:36 +0530 Subject: [PATCH 11/27] Rename package bpf to bcc Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 8 ++++---- vendor/modules.txt | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 12cbecfbad..23bfd8d0e8 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -16,7 +16,7 @@ import ( "syscall" "github.com/docker/docker/api/types" - bpf "github.com/iovisor/gobpf/bcc" + "github.com/iovisor/gobpf/bcc" spec "github.com/opencontainers/runtime-spec/specs-go" seccomp "github.com/seccomp/libseccomp-golang" "github.com/sirupsen/logrus" @@ -207,7 +207,7 @@ func runBPFSource(pid int, profilePath string) error { logrus.Println("Running floating process PID to attach:", pid) syscalls := make(map[string]int, 303) src := strings.Replace(source, "$PARENT_PID", strconv.Itoa(pid), -1) - m := bpf.NewModule(src, []string{}) + m := bcc.NewModule(src, []string{}) defer m.Close() tracepoint, err := m.LoadTracepoint("enter_trace") @@ -227,9 +227,9 @@ func runBPFSource(pid int, profilePath string) error { return err } - table := bpf.NewTable(m.TableId("events"), m) + table := bcc.NewTable(m.TableId("events"), m) channel := make(chan []byte) - perfMap, err := bpf.InitPerfMap(table, channel) + perfMap, err := bcc.InitPerfMap(table, channel) if err != nil { return fmt.Errorf("unable to init perf map err:%q", err.Error()) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 187af5cb9a..4682fc4e1d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -178,33 +178,37 @@ github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/metrics # github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 -github.com/docker/docker/pkg/signal +<<<<<<< HEAD +======= github.com/docker/docker/api/types +github.com/docker/docker/pkg/homedir +>>>>>>> Rename package bpf to bcc +github.com/docker/docker/pkg/signal github.com/docker/docker/oci/caps github.com/docker/docker/pkg/namesgenerator github.com/docker/docker/pkg/term github.com/docker/docker/pkg/ioutils github.com/docker/docker/profiles/seccomp github.com/docker/docker/pkg/parsers -github.com/docker/docker/api/types/versions -github.com/docker/docker/pkg/idtools github.com/docker/docker/api/types/container github.com/docker/docker/api/types/filters github.com/docker/docker/api/types/mount github.com/docker/docker/api/types/network github.com/docker/docker/api/types/registry github.com/docker/docker/api/types/swarm +github.com/docker/docker/api/types/versions +github.com/docker/docker/pkg/idtools github.com/docker/docker/errdefs github.com/docker/docker/pkg/term/windows github.com/docker/docker/pkg/longpath github.com/docker/docker/pkg/parsers/kernel +github.com/docker/docker/api/types/blkiodev +github.com/docker/docker/api/types/strslice +github.com/docker/docker/api/types/swarm/runtime github.com/docker/docker/pkg/fileutils github.com/docker/docker/pkg/stdcopy github.com/docker/docker/pkg/system github.com/docker/docker/client -github.com/docker/docker/api/types/blkiodev -github.com/docker/docker/api/types/strslice -github.com/docker/docker/api/types/swarm/runtime github.com/docker/docker/pkg/pools github.com/docker/docker/pkg/mount github.com/docker/docker/api @@ -274,11 +278,10 @@ github.com/ijc/Gotty github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.0.0 github.com/inconshreveable/mousetrap -# github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 # github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 github.com/iovisor/gobpf/bcc github.com/iovisor/gobpf/pkg/cpuonline -# github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed +# github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.7 github.com/json-iterator/go From da8ff6479dd2f380caede8b48cb0c944914b3fb6 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 2 Aug 2019 02:49:00 +0530 Subject: [PATCH 12/27] Integration tests for generate-seccmop Signed-off-by: Divyansh Kamboj --- test/e2e/run_generate_seccomp_test.go | 64 +++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go index 9311f14179..878d590488 100644 --- a/test/e2e/run_generate_seccomp_test.go +++ b/test/e2e/run_generate_seccomp_test.go @@ -1,10 +1,14 @@ package integration import ( + "encoding/json" "io/ioutil" "os" + "reflect" "time" + "github.com/docker/docker/api/types" + . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -36,27 +40,69 @@ var _ = Describe("Podman generate profile", func() { FIt("podman generate-seccomp generates profile that works ", func() { tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") fileName := tmpfile.Name() - tmpfile.Close() + + defer tmpfile.Close() defer os.Remove(fileName) - time.Sleep(time.Second * 2) - session := podmanTest.Podman([]string{"run", "--generate-seccomp=" + fileName, ALPINE, "true"}) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + fileName, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "true"}) + + time.Sleep(time.Second * 2) + + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) - FIt("podman generate-seccomp should fail to run command barred in logfile", func() { + FIt("podman generate-seccomp should fail to run syscalls not present in the profile", func() { tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") fileName := tmpfile.Name() - tmpfile.Close() + + defer tmpfile.Close() defer os.Remove(fileName) - time.Sleep(time.Second * 2) - session := podmanTest.Podman([]string{"run", "--generate-seccomp=" + fileName, ALPINE, "true"}) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + fileName, ALPINE, "true"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "sync"}) + + time.Sleep(time.Second * 2) + + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).ToNot(Equal(0)) + }) + FIt("podman generate-seccomp should generate the same seccomp profile for identical containers", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + profilePath1 := tmpfile.Name() + + tmpfile.Close() + defer os.Remove(profilePath1) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + profilePath1, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + + tmpfile, _ = ioutil.TempFile("", "generate-seccomp.*.json") + profilePath2 := tmpfile.Name() + + tmpfile.Close() + defer os.Remove(profilePath2) + + result := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + profilePath2, ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + time.Sleep(time.Second * 2) + + profileJSON1, _ := ioutil.ReadFile(profilePath1) + profile1 := types.Seccomp{} + err := json.Unmarshal(profileJSON1, &profile1) + Expect(err).To(BeNil()) + + profileJSON2, _ := ioutil.ReadFile(profilePath2) + profile2 := types.Seccomp{} + err = json.Unmarshal(profileJSON2, &profile2) + Expect(err).To(BeNil()) + Expect(reflect.DeepEqual(profile1.Syscalls, profile1.Syscalls)).To(Equal(true)) }) }) From d7ff5f5967fc714188b5600e6f08049614c754d9 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 2 Aug 2019 03:35:09 +0530 Subject: [PATCH 13/27] Add install instructions for generate-seccomp Signed-off-by: Divyansh Kamboj --- contrib/cirrus/packer/fedora_setup.sh | 4 +++- contrib/cirrus/packer/ubuntu_setup.sh | 4 +++- install.md | 10 +++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 6709eecaf3..5c9ac61be1 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -85,7 +85,9 @@ ooe.sh sudo dnf install -y \ vim \ which \ xz \ - zip + zip \ + bcc-devel \ + kernel-headers # Ensure there are no disruptive periodic services enabled by default in image diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index da7d457a50..d9bfd67d24 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -103,7 +103,9 @@ $BIGTO $SUDOAPTGET install \ unzip \ vim \ xz-utils \ - zip + zip \ + bcc \ + linux-headers-$(uname -r) if [[ "$OS_RELEASE_VER" -ge "19" ]] then diff --git a/install.md b/install.md index eb4ecfa683..1ac431193d 100644 --- a/install.md +++ b/install.md @@ -117,7 +117,9 @@ sudo yum install -y \ ostree-devel \ pkgconfig \ runc \ - containers-common + containers-common \ + bcc-devel \ + kernel-headers ``` Debian, Ubuntu, and related distributions: @@ -143,7 +145,9 @@ sudo apt-get install \ libsystemd-dev \ pkg-config \ runc \ - uidmap + uidmap \ + bcc \ + linux-headers-$(uname -r) ``` On Manjaro (and maybe other Linux distributions): @@ -318,7 +322,7 @@ make BUILDTAGS='seccomp apparmor' ``` | Build Tag | Feature | Dependency | -|----------------------------------|------------------------------------|----------------------| +| -------------------------------- | ---------------------------------- | -------------------- | | apparmor | apparmor support | libapparmor | | exclude_graphdriver_btrfs | exclude btrfs | libbtrfs | | exclude_graphdriver_devicemapper | exclude device-mapper | libdm | From d101205fdf819edeb8ed6deaf82db4d99a902ce4 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 2 Aug 2019 17:15:20 +0530 Subject: [PATCH 14/27] Fix tests Signed-off-by: Divyansh Kamboj --- test/e2e/run_generate_seccomp_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go index 878d590488..3145c7453f 100644 --- a/test/e2e/run_generate_seccomp_test.go +++ b/test/e2e/run_generate_seccomp_test.go @@ -37,7 +37,7 @@ var _ = Describe("Podman generate profile", func() { f := CurrentGinkgoTestDescription() processTestResult(f) }) - FIt("podman generate-seccomp generates profile that works ", func() { + It("podman generate-seccomp generates profile that works ", func() { tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") fileName := tmpfile.Name() @@ -54,7 +54,7 @@ var _ = Describe("Podman generate profile", func() { result.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) - FIt("podman generate-seccomp should fail to run syscalls not present in the profile", func() { + It("podman generate-seccomp should fail to run syscalls not present in the profile", func() { tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") fileName := tmpfile.Name() @@ -71,7 +71,7 @@ var _ = Describe("Podman generate profile", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).ToNot(Equal(0)) }) - FIt("podman generate-seccomp should generate the same seccomp profile for identical containers", func() { + It("podman generate-seccomp should generate the same seccomp profile for identical containers", func() { tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") profilePath1 := tmpfile.Name() From 74d84e646d7324a33a64b06039923d2dff4b1f50 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 9 Aug 2019 11:35:42 +0200 Subject: [PATCH 15/27] Fix lint error gobpf Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 23bfd8d0e8..3a789d1e17 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -16,7 +16,7 @@ import ( "syscall" "github.com/docker/docker/api/types" - "github.com/iovisor/gobpf/bcc" + "github.com/iovisor/gobpf/bcc" //nolint spec "github.com/opencontainers/runtime-spec/specs-go" seccomp "github.com/seccomp/libseccomp-golang" "github.com/sirupsen/logrus" From e6f4a51b0ac6cd21a64118ecd3306d443b00effa Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 15 Aug 2019 19:14:43 +0530 Subject: [PATCH 16/27] CIRRUS: bcc packages to the image Signed-off-by: Divyansh Kamboj --- Dockerfile | 5 +++++ contrib/cirrus/packer/ubuntu_setup.sh | 4 +++- vendor/modules.txt | 5 +---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1f51cd8749..dcd2a24102 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ FROM golang:1.12 +RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD \ + && echo "deb https://repo.iovisor.org/apt/bionic bionic main" | tee /etc/apt/sources.list.d/iovisor.list + RUN apt-get update && apt-get install -y \ apparmor \ autoconf \ @@ -34,12 +37,14 @@ RUN apt-get update && apt-get install -y \ libdevmapper-dev \ libgpgme11-dev \ liblzma-dev \ + libtinfo5 \ netcat \ socat \ lsof \ xz-utils \ unzip \ python3-yaml \ + libbcc \ --no-install-recommends \ && apt-get clean diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index d9bfd67d24..73f996a698 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -32,6 +32,8 @@ if [[ "$OS_RELEASE_VER" -eq "18" ]] then $LILTO $SUDOAPTADD ppa:longsleep/golang-backports fi +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD +sudo echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list $LILTO $SUDOAPTGET update @@ -104,7 +106,7 @@ $BIGTO $SUDOAPTGET install \ vim \ xz-utils \ zip \ - bcc \ + libbcc \ linux-headers-$(uname -r) if [[ "$OS_RELEASE_VER" -ge "19" ]] diff --git a/vendor/modules.txt b/vendor/modules.txt index 4682fc4e1d..ed84851ed7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -178,12 +178,9 @@ github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/metrics # github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 -<<<<<<< HEAD -======= github.com/docker/docker/api/types -github.com/docker/docker/pkg/homedir ->>>>>>> Rename package bpf to bcc github.com/docker/docker/pkg/signal +github.com/docker/docker/pkg/homedir github.com/docker/docker/oci/caps github.com/docker/docker/pkg/namesgenerator github.com/docker/docker/pkg/term From 93e9e593da532b3754ce9e08570b58f75f661014 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Thu, 22 Aug 2019 15:50:51 +0530 Subject: [PATCH 17/27] Build only when libbcc is present Signed-off-by: Divyansh Kamboj --- Dockerfile | 2 +- Makefile | 16 ++++++++++------ contrib/cirrus/packer/fedora_setup.sh | 5 ++--- contrib/cirrus/packer/ubuntu_setup.sh | 5 +++-- test/e2e/run_generate_seccomp_test.go | 6 ++++++ 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index dcd2a24102..2a2625a4ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && apt-get install -y \ apparmor \ autoconf \ automake \ + bcc-tools \ bison \ build-essential \ curl \ @@ -44,7 +45,6 @@ RUN apt-get update && apt-get install -y \ xz-utils \ unzip \ python3-yaml \ - libbcc \ --no-install-recommends \ && apt-get clean diff --git a/Makefile b/Makefile index 83c6e0a5a1..1d836f7626 100644 --- a/Makefile +++ b/Makefile @@ -502,14 +502,18 @@ endef install.oci-trace-hook: - install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR) - install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) - install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook - install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) - install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) + if pkg-config libbcc 2> /dev/null ; then \ + install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR); \ + install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) ; \ + fi oci-trace-hook: - $(GO) build -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook + if pkg-config libbcc 2> /dev/null ; then \ + $(GO) build -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook ; \ + fi varlink_generate: .gopathok cmd/podman/varlink/iopodman.go ## Generate varlink varlink_api_generate: .gopathok API.md diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 5c9ac61be1..2b70c544ab 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -27,6 +27,7 @@ ooe.sh sudo dnf install -y \ autoconf \ automake \ bats \ + bcc-devel \ bridge-utils \ btrfs-progs-devel \ bzip2 \ @@ -85,9 +86,7 @@ ooe.sh sudo dnf install -y \ vim \ which \ xz \ - zip \ - bcc-devel \ - kernel-headers + zip # Ensure there are no disruptive periodic services enabled by default in image diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index 73f996a698..1c4d89c16b 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -33,7 +33,8 @@ then $LILTO $SUDOAPTADD ppa:longsleep/golang-backports fi sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD -sudo echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list +# The repository isn't available for ubnutu 19 but the binaries work perfectly +sudo echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list $LILTO $SUDOAPTGET update @@ -44,6 +45,7 @@ $BIGTO $SUDOAPTGET install \ autoconf \ automake \ bats \ + bcc-tools \ bison \ btrfs-tools \ build-essential \ @@ -106,7 +108,6 @@ $BIGTO $SUDOAPTGET install \ vim \ xz-utils \ zip \ - libbcc \ linux-headers-$(uname -r) if [[ "$OS_RELEASE_VER" -ge "19" ]] diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go index 3145c7453f..25b692ccc5 100644 --- a/test/e2e/run_generate_seccomp_test.go +++ b/test/e2e/run_generate_seccomp_test.go @@ -23,6 +23,9 @@ var _ = Describe("Podman generate profile", func() { BeforeEach(func() { SkipIfRootless() + if _, err := os.Stat("/usr/share/containers/oci/hooks.d/oci-trace-hook-run.json"); err != nil { + Skip("oci-trace-hook not installed, please install the hook") + } tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -48,6 +51,7 @@ var _ = Describe("Podman generate profile", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds time.Sleep(time.Second * 2) result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) @@ -65,6 +69,7 @@ var _ = Describe("Podman generate profile", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds time.Sleep(time.Second * 2) result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) @@ -92,6 +97,7 @@ var _ = Describe("Podman generate profile", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds time.Sleep(time.Second * 2) profileJSON1, _ := ioutil.ReadFile(profilePath1) From fd85333d553a7f6820687ee84f71323b3108e07a Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 23 Aug 2019 11:41:44 +0530 Subject: [PATCH 18/27] Fix linting errors Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 3a789d1e17..8d73d89b31 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -182,7 +182,10 @@ func startFloatingProcess() error { return fmt.Errorf("cannot write pid to file err:%q", err.Error()) } defer f.Close() - f.WriteString(strconv.Itoa(processPID)) + _, err = f.WriteString(strconv.Itoa(processPID)) + if err != nil { + return fmt.Errorf("cannot write pid to the file") + } err = process.Release() if err != nil { return fmt.Errorf("cannot detach process err:%q", err.Error()) @@ -246,7 +249,7 @@ func runBPFSource(pid int, profilePath string) error { logrus.Errorf("failed to decode received data '%s': %s\n", data, err) continue } - name, err := getName(uint32(e.ID)) + name, err := getName(e.ID) if err != nil { logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) } @@ -285,7 +288,10 @@ func sendSIGINT() error { if err != nil { return fmt.Errorf("cannot find process with PID %d \nerror msg: %q", pid, err.Error()) } - p.Signal(os.Interrupt) + err = p.Signal(os.Interrupt) + if err != nil { + return fmt.Errorf("cannot send signal to child process err: %q", err.Error()) + } return nil } From 44c5b6f121d098a047f06974feff83455a998970 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Sat, 24 Aug 2019 22:12:21 +0530 Subject: [PATCH 19/27] Use buildtags to build oci-trace-hook Signed-off-by: Divyansh Kamboj --- Makefile | 13 ++++++++++--- cmd/oci-trace-hook/trace.go | 2 ++ hack/generate_seccomp_tag.sh | 4 ++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100755 hack/generate_seccomp_tag.sh diff --git a/Makefile b/Makefile index 1d836f7626..f546dbf138 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ BUILDTAGS ?= \ seccomp \ varlink +BUILDTAG_TRACE_HOOK ?= $(shell hack/generate_seccomp_tag.sh) GO_BUILD=$(GO) build # Go module support: set `-mod=vendor` to use the vendored sources ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true) @@ -48,6 +49,12 @@ $(warning \ Install libsystemd for journald support) endif +ifeq (,$(BUILDTAG_TRACE_HOOK)) +$(warning \ + Podman is being compiled without the oci_trace_hook build tag.\ + Install libbcc to use the oci-trace-hook) +endif + BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay ifneq (,$(findstring varlink,$(BUILDTAGS))) PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go @@ -502,7 +509,7 @@ endef install.oci-trace-hook: - if pkg-config libbcc 2> /dev/null ; then \ + if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ]; then \ install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR); \ install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) ; \ install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook ; \ @@ -511,8 +518,8 @@ install.oci-trace-hook: fi oci-trace-hook: - if pkg-config libbcc 2> /dev/null ; then \ - $(GO) build -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook ; \ + if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ] ; then \ + $(GO_BUILD) -tags $(BUILDTAG_TRACE_HOOK) -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook; \ fi varlink_generate: .gopathok cmd/podman/varlink/iopodman.go ## Generate varlink diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 8d73d89b31..4fa7be6fdb 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -1,3 +1,5 @@ +// +build oci_trace_hook + package main import ( diff --git a/hack/generate_seccomp_tag.sh b/hack/generate_seccomp_tag.sh new file mode 100755 index 0000000000..0cb54cdf01 --- /dev/null +++ b/hack/generate_seccomp_tag.sh @@ -0,0 +1,4 @@ +#!/bin/bash +if pkg-config libbcc 2> /dev/null; then + echo oci_trace_hook +fi \ No newline at end of file From c534b97ac5fd20e4346779026be21687aea6fd09 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 30 Aug 2019 16:47:59 +0530 Subject: [PATCH 20/27] Install bcc-devel on F30 and above Signed-off-by: Divyansh Kamboj --- contrib/cirrus/packer/fedora_setup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 2b70c544ab..1374cadadd 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -27,7 +27,6 @@ ooe.sh sudo dnf install -y \ autoconf \ automake \ bats \ - bcc-devel \ bridge-utils \ btrfs-progs-devel \ bzip2 \ @@ -96,6 +95,11 @@ sudo /tmp/libpod/hack/install_catatonit.sh # Same script is used for several related contexts case "$PACKER_BUILDER_NAME" in + + *fedora-30) + ooe.sh sudo dnf install -y bcc-devel + ;; + xfedora*) echo "Configuring CGroups v2 enabled on next boot" sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1" From 95296f6a54f36d368d6c712879c3d34f555e8cd5 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 30 Aug 2019 18:13:31 +0530 Subject: [PATCH 21/27] Remove extra lines Signed-off-by: Divyansh Kamboj --- contrib/cirrus/packer/fedora_setup.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 1374cadadd..ff7b87fd27 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -87,7 +87,6 @@ ooe.sh sudo dnf install -y \ xz \ zip - # Ensure there are no disruptive periodic services enabled by default in image systemd_banish @@ -96,9 +95,9 @@ sudo /tmp/libpod/hack/install_catatonit.sh # Same script is used for several related contexts case "$PACKER_BUILDER_NAME" in - *fedora-30) + *fedora-3*) ooe.sh sudo dnf install -y bcc-devel - ;; + ;& xfedora*) echo "Configuring CGroups v2 enabled on next boot" From 28e19c865fd70e39419dcc51734ed91982c71156 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 6 Sep 2019 11:47:21 +0530 Subject: [PATCH 22/27] Remove unnecessary packages from `ubuntu_setup.sh` Signed-off-by: Divyansh Kamboj --- contrib/cirrus/packer/ubuntu_setup.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index 1c4d89c16b..b82954db89 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -34,7 +34,7 @@ then fi sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD # The repository isn't available for ubnutu 19 but the binaries work perfectly -sudo echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list +sudo echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list $LILTO $SUDOAPTGET update @@ -107,8 +107,7 @@ $BIGTO $SUDOAPTGET install \ unzip \ vim \ xz-utils \ - zip \ - linux-headers-$(uname -r) + zip if [[ "$OS_RELEASE_VER" -ge "19" ]] then From 8372ffe9d36b98f152b33bd453a4f17dec226ef1 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 6 Sep 2019 17:25:53 +0530 Subject: [PATCH 23/27] Fix repeating comments Signed-off-by: Divyansh Kamboj --- cmd/oci-trace-hook/trace.go | 2 +- contrib/cirrus/packer/fedora_setup.sh | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 4fa7be6fdb..efa844facc 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -24,7 +24,7 @@ import ( "github.com/sirupsen/logrus" ) -// event struct used to read data from the perf ring buffer from the +// event struct used to read data from the perf ring buffer type event struct { // PID of the process making the syscall Pid uint32 diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index ff7b87fd27..1ed3f81786 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -95,15 +95,16 @@ sudo /tmp/libpod/hack/install_catatonit.sh # Same script is used for several related contexts case "$PACKER_BUILDER_NAME" in - *fedora-3*) - ooe.sh sudo dnf install -y bcc-devel - ;& - xfedora*) echo "Configuring CGroups v2 enabled on next boot" sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1" sudo dnf install -y crun ;& # continue to next matching item + + *fedora-3*) + ooe.sh sudo dnf install -y bcc-devel + ;& + *) echo "Finalizing $PACKER_BUILDER_NAME VM image" ;; From 59f1eab2101f5ef64ffb6102a971fd2dbe02f706 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Sat, 7 Sep 2019 20:56:39 +0530 Subject: [PATCH 24/27] Fix hook install destination Signed-off-by: Divyansh Kamboj --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index f546dbf138..5a59994688 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ BINDIR ?= ${PREFIX}/bin LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers -HOOK_BIN_DIR ?= /usr/libexec/oci/hooks.d/ -HOOK_DIR ?= /usr/share/containers/oci/hooks.d +HOOK_BIN_DIR ?= ${PREFIX}/libexec/oci/hooks.d/ +HOOK_DIR ?= ${PREFIX}/share/containers/oci/hooks.d ETCDIR ?= /etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system @@ -510,11 +510,11 @@ endef install.oci-trace-hook: if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ]; then \ - install ${SELINUXOPT} -d -m 755 $(HOOK_BIN_DIR); \ - install ${SELINUXOPT} -d -m 755 $(HOOK_DIR) ; \ - install ${SELINUXOPT} -m 755 bin/oci-trace-hook $(HOOK_BIN_DIR)/oci-trace-hook ; \ - install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json $(HOOK_DIR) ; \ - install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json $(HOOK_DIR) ; \ + install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_BIN_DIR); \ + install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 755 bin/oci-trace-hook ${DESTDIR}$(HOOK_BIN_DIR)/oci-trace-hook ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json ${DESTDIR}$(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json ${DESTDIR}$(HOOK_DIR) ; \ fi oci-trace-hook: From 54c0fc8ec95ea6be0dc7c8e4f9ac46037a44ea97 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Sat, 14 Sep 2019 08:22:46 +0530 Subject: [PATCH 25/27] Sort the syscalls written in the json file Signed-off-by: Divyansh Kamboj --- Makefile | 4 ++-- cmd/oci-trace-hook/oci-trace-hook-run.json | 4 ++-- cmd/oci-trace-hook/oci-trace-hook-stop.json | 4 ++-- cmd/oci-trace-hook/trace.go | 6 ++++-- cmd/podman/shared/create.go | 4 ++-- test/e2e/run_generate_seccomp_test.go | 15 +++++++++------ 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 5a59994688..42ba04d081 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers HOOK_BIN_DIR ?= ${PREFIX}/libexec/oci/hooks.d/ -HOOK_DIR ?= ${PREFIX}/share/containers/oci/hooks.d ETCDIR ?= /etc +HOOK_DIR ?= ${ETCDIR}/containers/oci/hook.d TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user @@ -512,7 +512,7 @@ install.oci-trace-hook: if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ]; then \ install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_BIN_DIR); \ install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_DIR) ; \ - install ${SELINUXOPT} -m 755 bin/oci-trace-hook ${DESTDIR}$(HOOK_BIN_DIR)/oci-trace-hook ; \ + install ${SELINUXOPT} -m 755 bin/oci-trace-hook ${DESTDIR}$(HOOK_BIN_DIR) ; \ install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json ${DESTDIR}$(HOOK_DIR) ; \ install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json ${DESTDIR}$(HOOK_DIR) ; \ fi diff --git a/cmd/oci-trace-hook/oci-trace-hook-run.json b/cmd/oci-trace-hook/oci-trace-hook-run.json index 53a2a7c5d1..8e773c03dd 100644 --- a/cmd/oci-trace-hook/oci-trace-hook-run.json +++ b/cmd/oci-trace-hook/oci-trace-hook-run.json @@ -1,7 +1,7 @@ { "version": "1.0.0", "hook": { - "path": "/usr/libexec/oci/hooks.d/oci-trace-hook", + "path": "/usr/local/libexec/oci/hooks.d/oci-trace-hook", "args": [ "oci-trace-hook", "-s" @@ -9,7 +9,7 @@ }, "when": { "annotations": { - "io.podman.trace-syscall": ".*" + "io.containers.trace-syscall": ".*" } }, "stages": [ diff --git a/cmd/oci-trace-hook/oci-trace-hook-stop.json b/cmd/oci-trace-hook/oci-trace-hook-stop.json index 56e3a10cb0..e3c0d04220 100644 --- a/cmd/oci-trace-hook/oci-trace-hook-stop.json +++ b/cmd/oci-trace-hook/oci-trace-hook-stop.json @@ -1,7 +1,7 @@ { "version": "1.0.0", "hook": { - "path": "/usr/libexec/oci/hooks.d/oci-trace-hook", + "path": "/usr/local/libexec/oci/hooks.d/oci-trace-hook", "args": [ "oci-trace-hook", "-t" @@ -9,7 +9,7 @@ }, "when": { "annotations": { - "io.podman.trace-syscall": ".*" + "io.containers.trace-syscall": ".*" } }, "stages": [ diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index efa844facc..342191c227 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -13,6 +13,7 @@ import ( "os" "os/signal" "path/filepath" + "sort" "strconv" "strings" "syscall" @@ -157,7 +158,7 @@ func startFloatingProcess() error { return err } pid := s.Pid - fileName := s.Annotations["io.podman.trace-syscall"] + fileName := s.Annotations["io.containers.trace-syscall"] attr := &os.ProcAttr{ Dir: ".", Env: os.Environ(), @@ -171,7 +172,7 @@ func startFloatingProcess() error { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGUSR1) - process, err := os.StartProcess("/usr/libexec/oci/hooks.d/oci-trace-hook", []string{"/usr/libexec/oci/hooks.d/trace", "-r", strconv.Itoa(pid), "-f", fileName}, attr) + process, err := os.StartProcess(os.Args[0], []string{os.Args[0], "-r", strconv.Itoa(pid), "-f", fileName}, attr) if err != nil { return fmt.Errorf("cannot launch process err: %q", err.Error()) } @@ -306,6 +307,7 @@ func generateProfile(c map[string]int, fileName string) error { names = append(names, s) } } + sort.Strings(names) s.DefaultAction = types.ActErrno s.Syscalls = []*types.Syscall{ diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 576301106a..41cd6433f6 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -586,8 +586,8 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. annotations[splitAnnotation[0]] = splitAnnotation[1] } - if annotations["io.podman.trace-syscall"] != "" { - if !filepath.IsAbs(annotations["io.podman.trace-syscall"]) { + if annotations["io.containers.trace-syscall"] != "" { + if !filepath.IsAbs(annotations["io.containers.trace-syscall"]) { return nil, errors.Errorf("Invalid Path, Please enter an absolute path") } diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go index 25b692ccc5..62b677e00a 100644 --- a/test/e2e/run_generate_seccomp_test.go +++ b/test/e2e/run_generate_seccomp_test.go @@ -23,8 +23,11 @@ var _ = Describe("Podman generate profile", func() { BeforeEach(func() { SkipIfRootless() - if _, err := os.Stat("/usr/share/containers/oci/hooks.d/oci-trace-hook-run.json"); err != nil { - Skip("oci-trace-hook not installed, please install the hook") + if _, err := os.Stat("/etc/containers/oci/hooks.d/oci-trace-hook-run.json"); err != nil { + Skip("oci-trace-hook prestart hook is not installed, please install the hook") + } + if _, err := os.Stat("/etc/containers/oci/hooks.d/oci-trace-hook-stop.json"); err != nil { + Skip("oci-trace-hook poststop hook is not installed, please install the hook") } tempdir, err = CreateTempDirInTempDir() if err != nil { @@ -47,7 +50,7 @@ var _ = Describe("Podman generate profile", func() { defer tmpfile.Close() defer os.Remove(fileName) - session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + fileName, ALPINE, "ls"}) + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + fileName, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -65,7 +68,7 @@ var _ = Describe("Podman generate profile", func() { defer tmpfile.Close() defer os.Remove(fileName) - session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + fileName, ALPINE, "true"}) + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + fileName, ALPINE, "true"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -83,7 +86,7 @@ var _ = Describe("Podman generate profile", func() { tmpfile.Close() defer os.Remove(profilePath1) - session := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + profilePath1, ALPINE, "ls"}) + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + profilePath1, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -93,7 +96,7 @@ var _ = Describe("Podman generate profile", func() { tmpfile.Close() defer os.Remove(profilePath2) - result := podmanTest.Podman([]string{"run", "--annotation", "io.podman.trace-syscall=" + profilePath2, ALPINE, "ls"}) + result := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + profilePath2, ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) From 0d91b3c0c69524f3b14dd76d0a5cd9f0b0293107 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Sun, 15 Sep 2019 09:08:42 +0530 Subject: [PATCH 26/27] Add the repository url only to bionic Signed-off-by: Divyansh Kamboj --- contrib/cirrus/packer/ubuntu_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index b82954db89..58463fa72c 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -34,7 +34,7 @@ then fi sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD # The repository isn't available for ubnutu 19 but the binaries work perfectly -sudo echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list +sudo echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list $LILTO $SUDOAPTGET update From fb95d0e333c898b68883263f3311b82a9fcf45c0 Mon Sep 17 00:00:00 2001 From: Divyansh Kamboj Date: Fri, 20 Sep 2019 17:57:08 +0530 Subject: [PATCH 27/27] fix hook install dir Signed-off-by: Divyansh Kamboj --- Makefile | 2 +- cmd/oci-trace-hook/trace.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 42ba04d081..d628736037 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers HOOK_BIN_DIR ?= ${PREFIX}/libexec/oci/hooks.d/ ETCDIR ?= /etc -HOOK_DIR ?= ${ETCDIR}/containers/oci/hook.d +HOOK_DIR ?= ${ETCDIR}/containers/oci/hooks.d/ TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go index 342191c227..7d5329b22b 100644 --- a/cmd/oci-trace-hook/trace.go +++ b/cmd/oci-trace-hook/trace.go @@ -172,7 +172,7 @@ func startFloatingProcess() error { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGUSR1) - process, err := os.StartProcess(os.Args[0], []string{os.Args[0], "-r", strconv.Itoa(pid), "-f", fileName}, attr) + process, err := os.StartProcess("/usr/local/libexec/oci/hooks.d/oci-trace-hook", []string{"oci-trace-hook", "-r", strconv.Itoa(pid), "-f", fileName}, attr) if err != nil { return fmt.Errorf("cannot launch process err: %q", err.Error()) }