File: KModRetExample.cc

package info (click to toggle)
bpfcc 0.31.0%2Bds-7
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 27,208 kB
  • sloc: ansic: 758,058; python: 40,671; cpp: 25,637; sh: 780; makefile: 279
file content (196 lines) | stat: -rw-r--r-- 4,542 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
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.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;
}