Skip to content

Commit fa970a9

Browse files
committed
update: add memory check for cxx runtime
1 parent a55b78b commit fa970a9

File tree

6 files changed

+130
-39
lines changed

6 files changed

+130
-39
lines changed

.github/workflows/c-cpp.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ jobs:
2828
- name: examples with cpp runtime
2929
run: IMPL=cpp make -C examples
3030
- name: make test
31-
run: make -C runtime/cpp test
31+
run: |
32+
make clean
33+
make -C runtime/cpp test
3234
- name: Upload build result
3335
uses: actions/[email protected]
3436
with:

runtime/Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtime/cpp/Makefile

+6-3
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ BROWSER := python -c "$$BROWSER_PYSCRIPT"
2828
INSTALL_LOCATION := ~/.local
2929

3030
build: ## build as a tool
31-
rm -rf build/
3231
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release # -Dwasm-bpf_ENABLE_ASAN=1
3332
cmake --build build --config Release
3433

34+
build-debug: ## build debug version
35+
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -Dwasm-bpf_ENABLE_ASAN=1
36+
cmake --build build --config Debug
37+
3538
build-lib: ## build as a library
36-
rm -rf build/
3739
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -Dwasm-bpf_BUILD_EXECUTABLE=0 # -Dwasm-bpf_ENABLE_ASAN=1
3840
cmake --build build --config Release
3941

@@ -47,7 +49,7 @@ help:
4749

4850

4951
test: ## run tests quickly with ctest
50-
sudo rm -rf build/
52+
cd ./test && make
5153
cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dwasm-bpf_ENABLE_UNIT_TESTING=1 -Dwasm-bpf_ENABLE_ASAN=1 -Dwasm-bpf_ENABLE_CODE_COVERAGE=1
5254
cmake --build build
5355
cd build/ && sudo ctest -VV
@@ -76,3 +78,4 @@ format: ## format the project sources
7678
clean: ## clean the project build files
7779
rm -rf build/
7880
rm -rf docs/
81+
cd test && make clean

runtime/cpp/include/bpf-api.h

+13-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
#include <cstdlib>
1212
#include <memory>
1313
#include <unordered_set>
14+
#include <unordered_map>
1415
#include <vector>
15-
16+
#include <bpf/libbpf.h>
1617
#include "wasm_export.h"
1718

1819
#define POLL_TIMEOUT_MS 100
@@ -55,6 +56,7 @@ class bpf_buffer {
5556
virtual ~bpf_buffer() = default;
5657
};
5758

59+
5860
/// @brief bpf program instance
5961
class wasm_bpf_program {
6062
std::unique_ptr<bpf_object, void (*)(bpf_object *obj)> obj{
@@ -64,13 +66,22 @@ class wasm_bpf_program {
6466

6567
public:
6668
int bpf_map_fd_by_name(const char *name);
67-
int load_bpf_object(const void *obj_buf, size_t obj_buf_sz);
69+
int load_bpf_object(const void* obj_buf,
70+
size_t obj_buf_sz);
6871
int attach_bpf_program(const char *name, const char *attach_target);
6972
int bpf_buffer_poll(wasm_exec_env_t exec_env, int fd, int32_t sample_func,
7073
uint32_t ctx, void *buffer_data, size_t max_size,
7174
int timeout_ms);
75+
bpf_map* map_ptr_by_fd(int fd);
7276
};
7377

78+
/// @brief A user data structure whose instance will be shared in a wasm
79+
/// runtime. It will store a map containing id->bpf_program and map fds opened
80+
/// by a bpf program
81+
/// Note that we need to remove fds opened by a bpf program when it's closed
82+
struct bpf_program_manager {
83+
std::unordered_map<uint64_t, std::unique_ptr<wasm_bpf_program>> programs;
84+
};
7485
enum bpf_map_cmd {
7586
_BPF_MAP_LOOKUP_ELEM = 1,
7687
_BPF_MAP_UPDATE_ELEM,

runtime/cpp/src/wasm-bpf.cpp

+105-30
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format,
3131
if (DEBUG_LIBBPF_RUNTIME) return vfprintf(stderr, format, args);
3232
return 0;
3333
}
34-
using bpf_program_manager =
35-
std::unordered_map<uint64_t, std::unique_ptr<wasm_bpf_program>>;
3634

3735
/// @brief initialize libbpf library
3836
void init_libbpf(void) {
@@ -45,14 +43,6 @@ static void perfbuf_sample_fn(void *ctx, int cpu, void *data, __u32 size) {
4543
bpf_buffer_sample(ctx, data, size);
4644
}
4745

48-
/// @brief get the bpf map in a object by fd
49-
static struct bpf_map *bpf_obj_get_map_by_fd(int fd, bpf_object *obj) {
50-
bpf_map *map;
51-
bpf_object__for_each_map(map, obj) {
52-
if (bpf_map__fd(map) == fd) return map;
53-
}
54-
return NULL;
55-
}
5646

5747
#define PERF_BUFFER_PAGES 64
5848

@@ -124,6 +114,26 @@ int bpf_buffer::bpf_buffer_sample(void *data, size_t size) {
124114
return 0;
125115
}
126116

117+
/// @brief verify that if an native address is valid in the wasm memory space
118+
static inline bool verify_wasm_buffer_by_native_addr(wasm_exec_env_t exec_env,
119+
void* ptr,
120+
uint32_t length) {
121+
wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env);
122+
if (!module)
123+
return false;
124+
125+
return wasm_runtime_validate_native_addr(module, ptr, length);
126+
}
127+
/// @brief verify that if a all chars of a zero-terminated string sit in the valid wasm memory space
128+
static inline bool verify_wasm_string_by_native_addr(wasm_exec_env_t exec_env,
129+
const char* str) {
130+
wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env);
131+
if (!module)
132+
return false;
133+
uint32_t wasm_addr = wasm_runtime_addr_native_to_app(module, (void*)str);
134+
return wasm_runtime_validate_app_str_addr(module, wasm_addr);
135+
}
136+
127137
/// @brief sample the perf buffer and ring buffer
128138
static int bpf_buffer_sample(void *ctx, void *data, size_t size) {
129139
bpf_buffer *buffer = (bpf_buffer *)ctx;
@@ -145,6 +155,17 @@ std::unique_ptr<bpf_buffer> bpf_buffer__new(struct bpf_map *events) {
145155
int wasm_bpf_program::bpf_map_fd_by_name(const char *name) {
146156
return bpf_object__find_map_fd_by_name(obj.get(), name);
147157
}
158+
/// @brief get map pointer by fd through iterating over all maps
159+
bpf_map* wasm_bpf_program::map_ptr_by_fd(int fd) {
160+
bpf_map* curr = nullptr;
161+
bpf_map__for_each(curr, obj.get()) {
162+
if (bpf_map__fd(curr) == fd) {
163+
return curr;
164+
}
165+
}
166+
return nullptr;
167+
}
168+
148169
/// @brief load all bpf programs and maps in a object file.
149170
int wasm_bpf_program::load_bpf_object(const void *obj_buf, size_t obj_buf_sz) {
150171
auto object = bpf_object__open_mem(obj_buf, obj_buf_sz, NULL);
@@ -212,7 +233,7 @@ int wasm_bpf_program::bpf_buffer_poll(wasm_exec_env_t exec_env, int fd,
212233
int timeout_ms) {
213234
if (buffer.get() == nullptr) {
214235
// create buffer
215-
auto map = bpf_obj_get_map_by_fd(fd, obj.get());
236+
auto map = this->map_ptr_by_fd(fd);
216237
buffer = bpf_buffer__new(map);
217238
buffer->bpf_buffer__open(fd, bpf_buffer_sample, buffer.get());
218239
return 0;
@@ -228,16 +249,47 @@ int wasm_bpf_program::bpf_buffer_poll(wasm_exec_env_t exec_env, int fd,
228249
}
229250

230251
/// a wrapper function to call the bpf syscall
231-
int bpf_map_operate(int fd, int cmd, void *key, void *value, void *next_key,
252+
int bpf_map_operate(wasm_exec_env_t exec_env,
253+
int fd,
254+
int cmd,
255+
void* key,
256+
void* value,
257+
void* next_key,
232258
uint64_t flags) {
259+
bpf_map_info map_info;
260+
memset(&map_info, 0, sizeof(map_info));
261+
__u32 info_len = sizeof(map_info);
262+
int err;
263+
if ((err = bpf_map_get_info_by_fd(fd, &map_info, &info_len)) != 0) {
264+
// Invalid map fd
265+
return err;
266+
}
267+
auto key_size = map_info.key_size;
268+
auto value_size = map_info.value_size;
269+
270+
auto verify_size = [&](void* ptr, uint32_t size) -> bool {
271+
return verify_wasm_buffer_by_native_addr(exec_env, ptr, size);
272+
};
233273
switch (cmd) {
234274
case BPF_MAP_GET_NEXT_KEY:
275+
if (!verify_size(key, key_size) || !verify_size(next_key, key_size))
276+
return -EFAULT;
277+
235278
return bpf_map_get_next_key(fd, key, next_key);
236279
case BPF_MAP_LOOKUP_ELEM:
280+
if (!verify_size(key, key_size) || !verify_size(value, value_size))
281+
return -EFAULT;
282+
237283
return bpf_map_lookup_elem_flags(fd, key, value, flags);
238284
case BPF_MAP_UPDATE_ELEM:
285+
if (!verify_size(key, key_size) || !verify_size(value, value_size))
286+
return -EFAULT;
287+
239288
return bpf_map_update_elem(fd, key, value, flags);
240289
case BPF_MAP_DELETE_ELEM:
290+
if (!verify_size(key, key_size))
291+
return -EFAULT;
292+
241293
return bpf_map_delete_elem_flags(fd, key, flags);
242294
default: // More syscall commands can be allowed here
243295
return -EINVAL;
@@ -249,28 +301,39 @@ extern "C" {
249301
uint64_t wasm_load_bpf_object(wasm_exec_env_t exec_env, void *obj_buf,
250302
int obj_buf_sz) {
251303
if (obj_buf_sz <= 0) return 0;
304+
// Ensure that the buffer passed from wasm program is valid
305+
if (!verify_wasm_buffer_by_native_addr(exec_env, obj_buf,
306+
(uint32_t)obj_buf_sz)) {
307+
return 0;
308+
}
252309
bpf_program_manager *bpf_programs =
253310
(bpf_program_manager *)wasm_runtime_get_user_data(exec_env);
254311
auto program = std::make_unique<wasm_bpf_program>();
255312
int res = program->load_bpf_object(obj_buf, (size_t)obj_buf_sz);
256313
if (res < 0) return 0;
257314
auto key = (uint64_t)program.get();
258-
bpf_programs->emplace(key, std::move(program));
315+
bpf_programs->programs.emplace(key, std::move(program));
259316
return key;
260317
}
261318

262319
int wasm_close_bpf_object(wasm_exec_env_t exec_env, uint64_t program) {
263-
bpf_program_manager *bpf_programs =
264-
(bpf_program_manager *)wasm_runtime_get_user_data(exec_env);
265-
return !bpf_programs->erase(program);
320+
bpf_program_manager* bpf_programs =
321+
(bpf_program_manager*)wasm_runtime_get_user_data(exec_env);
322+
if (!bpf_programs->programs.count(program))
323+
return 0;
324+
return !bpf_programs->programs.erase(program);
266325
}
267326

268327
int wasm_attach_bpf_program(wasm_exec_env_t exec_env, uint64_t program,
269328
char *name, char *attach_target) {
270-
bpf_program_manager *bpf_programs =
271-
(bpf_program_manager *)wasm_runtime_get_user_data(exec_env);
272-
if (bpf_programs->find(program) != bpf_programs->end()) {
273-
return (*bpf_programs)[program]->attach_bpf_program(name,
329+
bpf_program_manager* bpf_programs =
330+
(bpf_program_manager*)wasm_runtime_get_user_data(exec_env);
331+
if (bpf_programs->programs.find(program) != bpf_programs->programs.end()) {
332+
// Ensure that the string pointer passed from wasm program is valid
333+
if ((!verify_wasm_string_by_native_addr(exec_env, name)) ||
334+
(!verify_wasm_string_by_native_addr(exec_env, attach_target)))
335+
return -EFAULT;
336+
return bpf_programs->programs[program]->attach_bpf_program(name,
274337
attach_target);
275338
}
276339
return -EINVAL;
@@ -281,8 +344,11 @@ int wasm_bpf_buffer_poll(wasm_exec_env_t exec_env, uint64_t program, int fd,
281344
int max_size, int timeout_ms) {
282345
bpf_program_manager *bpf_programs =
283346
(bpf_program_manager *)wasm_runtime_get_user_data(exec_env);
284-
if (bpf_programs->find(program) != bpf_programs->end()) {
285-
return (*bpf_programs)[program]->bpf_buffer_poll(
347+
if (bpf_programs->programs.find(program) != bpf_programs->programs.end()) {
348+
// Ensure that the buffer is valid and can hold the data received
349+
if (!verify_wasm_buffer_by_native_addr(exec_env, data, (uint32_t)max_size))
350+
return -EFAULT;
351+
return bpf_programs->programs[program]->bpf_buffer_poll(
286352
exec_env, fd, sample_func, ctx, data, (size_t)max_size, timeout_ms);
287353
}
288354
return -EINVAL;
@@ -292,16 +358,25 @@ int wasm_bpf_map_fd_by_name(wasm_exec_env_t exec_env, uint64_t program,
292358
const char *name) {
293359
bpf_program_manager *bpf_programs =
294360
(bpf_program_manager *)wasm_runtime_get_user_data(exec_env);
295-
if (bpf_programs->find(program) != bpf_programs->end()) {
296-
return (*bpf_programs)[program]->bpf_map_fd_by_name(name);
361+
if (bpf_programs->programs.find(program) != bpf_programs->programs.end()) {
362+
// Ensure that the string is valid
363+
if (!verify_wasm_string_by_native_addr(exec_env, name))
364+
return -EFAULT;
365+
return bpf_programs->programs[program]->bpf_map_fd_by_name(name);
297366
}
298367
return -EINVAL;
299368
}
300369

301370
/// @brief a wrapper function to the bpf syscall to operate the bpf maps
302-
int wasm_bpf_map_operate(wasm_exec_env_t exec_env, int fd, int cmd, void *key,
303-
void *value, void *next_key, uint64_t flags) {
304-
return bpf_map_operate(fd, (bpf_map_cmd)cmd, key, value, next_key, flags);
371+
int wasm_bpf_map_operate(wasm_exec_env_t exec_env,
372+
int fd,
373+
int cmd,
374+
void* key,
375+
void* value,
376+
void* next_key,
377+
uint64_t flags) {
378+
return bpf_map_operate(
379+
exec_env, fd, (bpf_map_cmd)cmd, key, value, next_key, flags);
305380
}
306381
}
307382

@@ -354,8 +429,8 @@ int wasm_main(unsigned char *buf, unsigned int size, int argc, char *argv[]) {
354429
printf("Create wasm execution environment failed.\n");
355430
return -1;
356431
}
357-
std::unordered_set<std::unique_ptr<wasm_bpf_program>> bpf_programs;
358-
wasm_runtime_set_user_data(exec_env, &bpf_programs);
432+
bpf_program_manager prog_manager;
433+
wasm_runtime_set_user_data(exec_env, &prog_manager);
359434
wasm_runtime_set_module_inst(exec_env, module_inst);
360435
if (!(start_func = wasm_runtime_lookup_wasi_start_function(module_inst))) {
361436
printf("The start wasm function is not found.\n");
@@ -367,7 +442,7 @@ int wasm_main(unsigned char *buf, unsigned int size, int argc, char *argv[]) {
367442
wasm_runtime_get_exception(module_inst));
368443
return -1;
369444
}
370-
exit_code = wasm_runtime_get_wasi_exit_code(module_inst);
445+
exit_code = (int)wasm_runtime_get_wasi_exit_code(module_inst);
371446
if (exec_env) wasm_runtime_destroy_exec_env(exec_env);
372447
if (module_inst) {
373448
if (wasm_buffer) {

runtime/cpp/third_party/bpftool

Submodule bpftool updated 1 file

0 commit comments

Comments
 (0)