android13/external/bcc/examples/cpp/KModRetExample.cc

197 lines
4.4 KiB
C++

/*
* 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.ok()) {
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.ok()) {
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.ok()) {
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.ok()) {
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.ok()) {
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.ok()) {
std::cerr << res.msg() << std::endl;
return 1;
}
if (modify_return(bpf))
return 1;
if (not_modify_return(bpf))
return 1;
return 0;
}