xref: /aosp_15_r20/external/bcc/examples/cpp/KModRetExample.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright (c) Facebook, Inc.
3  * Licensed under the Apache License, Version 2.0 (the "License")
4  *
5  * Usage:
6  *   $ ./KModRetExample
7  *   opened file: /bin/true
8  *   security_file_open() is called 1 times, expecting 1
9  *
10  * Kfunc modify_ret support is only available at kernel version 5.6 and later.
11  * This example only works for x64. Currently, only the kernel functions can
12  * be attached with BPF_MODIFY_RETURN:
13  *   - Whitelisted for error injection by checking within_error_injection_list.
14  *     Similar discussions happened for the bpf_override_return helper.
15  *   - The LSM security hooks (kernel global function with prefix "security_").
16  */
17 
18 #include <fstream>
19 #include <iostream>
20 #include <iomanip>
21 #include <string>
22 
23 #include <error.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 
28 #include "bcc_version.h"
29 #include "BPF.h"
30 
31 const std::string BPF_PROGRAM = R"(
32 #include <linux/fs.h>
33 #include <asm/errno.h>
34 
35 BPF_ARRAY(target_pid, u32, 1);
36 static bool match_target_pid()
37 {
38   int key = 0, *val, tpid, cpid;
39 
40   val = target_pid.lookup(&key);
41   if (!val)
42     return false;
43 
44   tpid = *val;
45   cpid = bpf_get_current_pid_tgid() >> 32;
46   if (tpid == 0 || tpid != cpid)
47      return false;
48   return true;
49 }
50 
51 struct fname_buf {
52   char buf[16];
53 };
54 BPF_ARRAY(fname_table, struct fname_buf, 1);
55 
56 KMOD_RET(__x64_sys_openat, struct pt_regs *regs, int ret)
57 {
58   if (!match_target_pid())
59     return 0;
60 
61   // openat syscall arguments:
62   //   int dfd, const char __user * filename, int flags, umode_t mode
63   char *filename = (char *)PT_REGS_PARM2_SYSCALL(regs);
64 
65   int key = 0;
66   struct fname_buf *val;
67   val = fname_table.lookup(&key);
68   if (!val)
69     return false;
70 
71   if (bpf_copy_from_user(val, sizeof(*val), filename) < 0)
72     return 0;
73 
74   /* match target_pid, return -EINVAL. */
75   return -EINVAL;
76 }
77 
78 BPF_ARRAY(count, u32, 1);
79 KMOD_RET(security_file_open, struct file *file, int ret)
80 {
81   if (!match_target_pid())
82     return 0;
83 
84   int key = 0, *val;
85   val = count.lookup(&key);
86   if (!val)
87     return 0;
88 
89   /* no modification, kernel func continues to execute after this. */
90   lock_xadd(val, 1);
91   return 0;
92 }
93 )";
94 
95 struct fname_buf {
96   char buf[16];
97 };
98 
modify_return(ebpf::BPF & bpf)99 static int modify_return(ebpf::BPF &bpf) {
100   int prog_fd;
101   auto res = bpf.load_func("kmod_ret____x64_sys_openat",
102                            BPF_PROG_TYPE_TRACING, prog_fd, BPF_F_SLEEPABLE);
103   if (!res.ok()) {
104     std::cerr << res.msg() << std::endl;
105     return 1;
106   }
107 
108   int attach_fd = bpf_attach_kfunc(prog_fd);
109   if (attach_fd < 0) {
110     std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
111     return 1;
112   }
113 
114   int ret = open("/bin/true", O_RDONLY);
115   if (ret >= 0 || errno != EINVAL) {
116     close(attach_fd);
117     std::cerr << "incorrect open result" << std::endl;
118     return 1;
119   }
120 
121   auto fname_table = bpf.get_array_table<struct fname_buf>("fname_table");
122   uint32_t key = 0;
123   struct fname_buf val;
124   res = fname_table.get_value(key, val);
125   if (!res.ok()) {
126     close(attach_fd);
127     std::cerr << res.msg() << std::endl;
128     return 1;
129   }
130   std::cout << "opened file: " << val.buf << std::endl;
131 
132   // detach the kfunc.
133   close(attach_fd);
134   return 0;
135 }
136 
not_modify_return(ebpf::BPF & bpf)137 static int not_modify_return(ebpf::BPF &bpf) {
138   int prog_fd;
139   auto res = bpf.load_func("kmod_ret__security_file_open",
140                             BPF_PROG_TYPE_TRACING, prog_fd);
141   if (!res.ok()) {
142     std::cerr << res.msg() << std::endl;
143     return 1;
144   }
145 
146   int attach_fd = bpf_attach_kfunc(prog_fd);
147   if (attach_fd < 0) {
148     std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
149     return 1;
150   }
151 
152   int ret = open("/bin/true", O_RDONLY);
153   if (ret < 0) {
154     close(attach_fd);
155     std::cerr << "incorrect open result" << std::endl;
156     return 1;
157   }
158 
159   auto count_table = bpf.get_array_table<uint32_t>("count");
160   uint32_t key = 0, val = 0;
161   res = count_table.get_value(key, val);
162   if (!res.ok()) {
163     close(attach_fd);
164     std::cerr << res.msg() << std::endl;
165     return 1;
166   }
167 
168   close(attach_fd);
169   std::cout << "security_file_open() is called " << val << " times, expecting 1\n";
170   return 0;
171 }
172 
main()173 int main() {
174   ebpf::BPF bpf;
175   auto res = bpf.init(BPF_PROGRAM);
176   if (!res.ok()) {
177     std::cerr << res.msg() << std::endl;
178     return 1;
179   }
180 
181   uint32_t key = 0, val = getpid();
182   auto pid_table = bpf.get_array_table<uint32_t>("target_pid");
183   res = pid_table.update_value(key, val);
184   if (!res.ok()) {
185     std::cerr << res.msg() << std::endl;
186     return 1;
187   }
188 
189   if (modify_return(bpf))
190     return 1;
191 
192   if (not_modify_return(bpf))
193     return 1;
194 
195   return 0;
196 }
197