Skip to content

Commit

Permalink
add support kfunc/modify_return bpf programs
Browse files Browse the repository at this point in the history
Add support for tracing program with BPF_MODIFY_RETURN
attachment type. This is used for security oriented bpf
programs which can modify return values for certain
kernel functions:
  - whitelisted for error injection by checking
    within_error_injection_list including all syscalls.
  - lsm security function (prefix "security_").

Also extended load_func() API to allow specify bpf program
is sleepable and this will permits to use some sleepable
helpers like bpf_copy_from_user() and d_path().
  • Loading branch information
yonghong-song committed Jan 27, 2021
1 parent 3318c26 commit 25438d3
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 5 deletions.
196 changes: 196 additions & 0 deletions examples/cpp/KModRetExample.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*
* Usage:
* $ ./KModRetExample
* opened file: /bin/true
* security_file_open() is called 1 times, expecting 1
*
* Kfunc modify_ret support is only available at kernel version 5.6 and later.
* This example only works for x64. Currently, only the kernel functions can
* be attached with BPF_MODIFY_RETURN:
* - Whitelisted for error injection by checking within_error_injection_list.
* Similar discussions happened for the bpf_override_return helper.
* - The LSM security hooks (kernel global function with prefix "security_").
*/

#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>

#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "bcc_version.h"
#include "BPF.h"

const std::string BPF_PROGRAM = R"(
#include <linux/fs.h>
#include <asm/errno.h>
BPF_ARRAY(target_pid, u32, 1);
static bool match_target_pid()
{
int key = 0, *val, tpid, cpid;
val = target_pid.lookup(&key);
if (!val)
return false;
tpid = *val;
cpid = bpf_get_current_pid_tgid() >> 32;
if (tpid == 0 || tpid != cpid)
return false;
return true;
}
struct fname_buf {
char buf[16];
};
BPF_ARRAY(fname_table, struct fname_buf, 1);
KMOD_RET(__x64_sys_openat, struct pt_regs *regs, int ret)
{
if (!match_target_pid())
return 0;
// openat syscall arguments:
// int dfd, const char __user * filename, int flags, umode_t mode
char *filename = (char *)PT_REGS_PARM2_SYSCALL(regs);
int key = 0;
struct fname_buf *val;
val = fname_table.lookup(&key);
if (!val)
return false;
if (bpf_copy_from_user(val, sizeof(*val), filename) < 0)
return 0;
/* match target_pid, return -EINVAL. */
return -EINVAL;
}
BPF_ARRAY(count, u32, 1);
KMOD_RET(security_file_open, struct file *file, int ret)
{
if (!match_target_pid())
return 0;
int key = 0, *val;
val = count.lookup(&key);
if (!val)
return 0;
/* no modification, kernel func continues to execute after this. */
lock_xadd(val, 1);
return 0;
}
)";

struct fname_buf {
char buf[16];
};

static int modify_return(ebpf::BPF &bpf) {
int prog_fd;
auto res = bpf.load_func("kmod_ret____x64_sys_openat",
BPF_PROG_TYPE_TRACING, prog_fd, BPF_F_SLEEPABLE);
if (res.code() != 0) {
std::cerr << res.msg() << std::endl;
return 1;
}

int attach_fd = bpf_attach_kfunc(prog_fd);
if (attach_fd < 0) {
std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
return 1;
}

int ret = open("/bin/true", O_RDONLY);
if (ret >= 0 || errno != EINVAL) {
close(attach_fd);
std::cerr << "incorrect open result" << std::endl;
return 1;
}

auto fname_table = bpf.get_array_table<struct fname_buf>("fname_table");
uint32_t key = 0;
struct fname_buf val;
res = fname_table.get_value(key, val);
if (res.code() != 0) {
close(attach_fd);
std::cerr << res.msg() << std::endl;
return 1;
}
std::cout << "opened file: " << val.buf << std::endl;

// detach the kfunc.
close(attach_fd);
return 0;
}

static int not_modify_return(ebpf::BPF &bpf) {
int prog_fd;
auto res = bpf.load_func("kmod_ret__security_file_open",
BPF_PROG_TYPE_TRACING, prog_fd);
if (res.code() != 0) {
std::cerr << res.msg() << std::endl;
return 1;
}

int attach_fd = bpf_attach_kfunc(prog_fd);
if (attach_fd < 0) {
std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
return 1;
}

int ret = open("/bin/true", O_RDONLY);
if (ret < 0) {
close(attach_fd);
std::cerr << "incorrect open result" << std::endl;
return 1;
}

auto count_table = bpf.get_array_table<uint32_t>("count");
uint32_t key = 0, val = 0;
res = count_table.get_value(key, val);
if (res.code() != 0) {
close(attach_fd);
std::cerr << res.msg() << std::endl;
return 1;
}

close(attach_fd);
std::cout << "security_file_open() is called " << val << " times, expecting 1\n";
return 0;
}

int main() {
ebpf::BPF bpf;
auto res = bpf.init(BPF_PROGRAM);
if (res.code() != 0) {
std::cerr << res.msg() << std::endl;
return 1;
}

uint32_t key = 0, val = getpid();
auto pid_table = bpf.get_array_table<uint32_t>("target_pid");
res = pid_table.update_value(key, val);
if (res.code() != 0) {
std::cerr << res.msg() << std::endl;
return 1;
}

if (modify_return(bpf))
return 1;

if (not_modify_return(bpf))
return 1;

return 0;
}
4 changes: 2 additions & 2 deletions src/cc/api/BPF.cc
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ int BPF::poll_perf_buffer(const std::string& name, int timeout_ms) {
}

StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type,
int& fd) {
int& fd, unsigned flags) {
if (funcs_.find(func_name) != funcs_.end()) {
fd = funcs_[func_name];
return StatusTuple::OK();
Expand All @@ -692,7 +692,7 @@ StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type,
fd = bpf_module_->bcc_func_load(type, func_name.c_str(),
reinterpret_cast<struct bpf_insn*>(func_start), func_size,
bpf_module_->license(), bpf_module_->kern_version(),
log_level, nullptr, 0);
log_level, nullptr, 0, nullptr, flags);

if (fd < 0)
return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
Expand Down
2 changes: 1 addition & 1 deletion src/cc/api/BPF.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class BPF {
int poll_perf_buffer(const std::string& name, int timeout_ms = -1);

StatusTuple load_func(const std::string& func_name, enum bpf_prog_type type,
int& fd);
int& fd, unsigned flags = 0);
StatusTuple unload_func(const std::string& func_name);

StatusTuple attach_func(int prog_fd, int attachable_fd,
Expand Down
3 changes: 2 additions & 1 deletion src/cc/bpf_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ int BPFModule::bcc_func_load(int prog_type, const char *name,
const struct bpf_insn *insns, int prog_len,
const char *license, unsigned kern_version,
int log_level, char *log_buf, unsigned log_buf_size,
const char *dev_name) {
const char *dev_name, unsigned flags) {
struct bpf_load_program_attr attr = {};
unsigned func_info_cnt, line_info_cnt, finfo_rec_size, linfo_rec_size;
void *func_info = NULL, *line_info = NULL;
Expand All @@ -921,6 +921,7 @@ int BPFModule::bcc_func_load(int prog_type, const char *name,
attr.prog_type != BPF_PROG_TYPE_EXT) {
attr.kern_version = kern_version;
}
attr.prog_flags = flags;
attr.log_level = log_level;
if (dev_name)
attr.prog_ifindex = if_nametoindex(dev_name);
Expand Down
3 changes: 2 additions & 1 deletion src/cc/bpf_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ class BPFModule {
const struct bpf_insn *insns, int prog_len,
const char *license, unsigned kern_version,
int log_level, char *log_buf, unsigned log_buf_size,
const char *dev_name = nullptr);
const char *dev_name = nullptr,
unsigned flags = 0);
int bcc_func_attach(int prog_fd, int attachable_fd,
int attach_type, unsigned int flags);
int bcc_func_detach(int prog_fd, int attachable_fd, int attach_type);
Expand Down
3 changes: 3 additions & 0 deletions src/cc/export/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,9 @@ static int ____##name(unsigned long long *ctx, ##args)
#define KRETFUNC_PROBE(event, args...) \
BPF_PROG(kretfunc__ ## event, args)

#define KMOD_RET(event, args...) \
BPF_PROG(kmod_ret__ ## event, args)

#define LSM_PROBE(event, args...) \
BPF_PROG(lsm__ ## event, args)

Expand Down
3 changes: 3 additions & 0 deletions src/cc/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,9 @@ int bcc_prog_load_xattr(struct bpf_load_program_attr *attr, int prog_len,
else if (strncmp(attr->name, "kfunc__", 7) == 0) {
name_offset = 7;
expected_attach_type = BPF_TRACE_FENTRY;
} else if (strncmp(attr->name, "kmod_ret__", 10) == 0) {
name_offset = 10;
expected_attach_type = BPF_MODIFY_RETURN;
} else if (strncmp(attr->name, "kretfunc__", 10) == 0) {
name_offset = 10;
expected_attach_type = BPF_TRACE_FEXIT;
Expand Down

0 comments on commit 25438d3

Please sign in to comment.