diff --git a/.gitignore b/.gitignore index 75930d3f..83b77128 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ documents/dlsym/dlsym documents/elfoffset/myprogram documents/elfoffset/*.txt documents/elfoffset/maps -Catch2 libebpf.off.txt test/pyvenv.cfg ecli diff --git a/.vscode/launch.json b/.vscode/launch.json index 55f44a94..274f75d4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,14 +8,14 @@ "name": "(gdb) runtime", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/example/malloc/test", + "program": "${workspaceFolder}/example/malloc/victim", "args": [], "stopAtEntry": true, - "cwd": "${workspaceFolder}/build/runtime/test/", + "cwd": "${workspaceFolder}/", "environment": [ { "name": "LD_PRELOAD", - "value": "/home/yunwei/bpftime/build/runtime/agent/libbpftime-agent.so" + "value": "${workspaceFolder}/build/runtime/agent/libbpftime-agent.so" }, // { // "name": "LD_PRELOAD", diff --git a/README.md b/README.md index e859f3c9..3c7b4090 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ `bpftime`, a full-featured, high-performance eBPF runtime designed to operate in userspace. It offers fast Uprobe and Syscall hook capabilities: Userspace uprobe can be **10x faster than kernel uprobe!** and can programmatically **hook all syscalls of a process** safely and efficiently. -📦 [Features](#key-features) \ +📦 [Key Features](#key-features) \ 🔨 [Quick Start](#quick-start) \ ⌨️ [Linux Plumbers 23 talk](https://lpc.events/event/17/contributions/1639/) \ 📖 [Slides](https://github.com/eunomia-bpf/bpftime/tree/master/documents/userspace-ebpf-bpftime-lpc.pdf) \ @@ -144,12 +144,12 @@ see [arxiv preprint: https://arxiv.org/abs/2311.07923](https://arxiv.org/abs/231 How is the performance of `userspace uprobe` compared to `kernel uprobes`? -| Probe/Tracepoint Types | Kernel (ns) | Userspace (ns) | Insn Count | -|------------------------|-------------:|---------------:|---------------:| -| Uprobe | 3224.172760 | 314.569110 | 4 | -| Uretprobe | 3996.799580 | 381.270270 | 2 | -| Syscall Tracepoint | 151.82801 | 232.57691 | 4 | -| Embedding runtime | Not avaliable | 110.008430 | 4 | +| Probe/Tracepoint Types | Kernel (ns) | Userspace (ns) | +|------------------------|-------------:|---------------:| +| Uprobe | 3224.172760 | 314.569110 | +| Uretprobe | 3996.799580 | 381.270270 | +| Syscall Tracepoint | 151.82801 | 232.57691 | +| Embedding runtime | Not avaliable | 110.008430 | It can be attached to functions in running process just like the kernel uprobe does. diff --git a/runtime/include/bpftime_shm.hpp b/runtime/include/bpftime_shm.hpp index 4a49ba73..50f1ed47 100644 --- a/runtime/include/bpftime_shm.hpp +++ b/runtime/include/bpftime_shm.hpp @@ -163,6 +163,11 @@ void bpftime_destroy_global_shm(); // remove the global shared memory from system void bpftime_remove_global_shm(); +// enable memory protect, e.g. mpk +void bpftime_protect_enable(); +// disable memory protect +void bpftime_protect_disable(); + // import the global shared memory from json file int bpftime_import_global_shm_from_json(const char *filename); // export the global shared memory to json file diff --git a/runtime/src/attach/bpf_attach_ctx.cpp b/runtime/src/attach/bpf_attach_ctx.cpp index 5d09458e..eb246322 100644 --- a/runtime/src/attach/bpf_attach_ctx.cpp +++ b/runtime/src/attach/bpf_attach_ctx.cpp @@ -95,6 +95,7 @@ bool bpf_attach_ctx::check_syscall_trace_setup(int pid) { return shm_holder.global_shared_memory.check_syscall_trace_setup(pid); } + // Set whether a certain pid was already equipped with syscall tracer // Using a set stored in the shared memory void bpf_attach_ctx::set_syscall_trace_setup(int pid, bool whether) @@ -519,4 +520,5 @@ bpf_attach_ctx::bpf_attach_ctx(void) spdlog::debug("Initialzing frida gum"); current_id = CURRENT_ID_OFFSET; } + } // namespace bpftime diff --git a/runtime/src/bpf_map/README.md b/runtime/src/bpf_map/README.md new file mode 100644 index 00000000..0da39e2c --- /dev/null +++ b/runtime/src/bpf_map/README.md @@ -0,0 +1,4 @@ +# bpf maps source + +- shared: maps between kernel and userspace +- userspace: maps runs only in userspace diff --git a/runtime/src/bpftime_prog.cpp b/runtime/src/bpftime_prog.cpp index eb7af34b..4386668e 100644 --- a/runtime/src/bpftime_prog.cpp +++ b/runtime/src/bpftime_prog.cpp @@ -72,6 +72,8 @@ int bpftime_prog::bpftime_prog_exec(void *memory, size_t memory_size, { uint64_t val = 0; int res = 0; + // set share memory read and write able + bpftime_protect_disable(); spdlog::debug( "Calling bpftime_prog::bpftime_prog_exec, memory={:x}, memory_size={}, return_val={:x}, prog_name={}", (uintptr_t)memory, memory_size, (uintptr_t)return_val, @@ -88,6 +90,8 @@ int bpftime_prog::bpftime_prog_exec(void *memory, size_t memory_size, } } *return_val = val; + // set share memory read only + bpftime_protect_enable(); return res; } diff --git a/runtime/src/bpftime_shm.cpp b/runtime/src/bpftime_shm.cpp index a962d4ca..346a8750 100644 --- a/runtime/src/bpftime_shm.cpp +++ b/runtime/src/bpftime_shm.cpp @@ -79,6 +79,7 @@ long bpftime_map_delete_elem(int fd, const void *key) { return shm_holder.global_shared_memory.bpf_delete_elem(fd, key, true); } + int bpftime_map_get_next_key(int fd, const void *key, void *next_key) { return shm_holder.global_shared_memory.bpf_map_get_next_key( @@ -156,6 +157,18 @@ int bpftime_is_ringbuf_map(int fd) return shm_holder.global_shared_memory.is_ringbuf_map_fd(fd); } +void bpftime_protect_enable() { +#if BPFTIME_ENABLE_MPK + return shm_holder.global_shared_memory.enable_mpk(); +#endif +} + +void bpftime_protect_disable() { +#if BPFTIME_ENABLE_MPK + return shm_holder.global_shared_memory.disable_mpk(); +#endif +} + int bpftime_is_map_fd(int fd) { return shm_holder.global_shared_memory.is_map_fd(fd); diff --git a/runtime/src/bpftime_shm_internal.cpp b/runtime/src/bpftime_shm_internal.cpp index 930fe642..4823335c 100644 --- a/runtime/src/bpftime_shm_internal.cpp +++ b/runtime/src/bpftime_shm_internal.cpp @@ -7,6 +7,7 @@ #include #include #include +#include static bool global_shm_initialized = false; @@ -432,6 +433,28 @@ void bpftime_shm::close_fd(int fd) } } +#if BPFTIME_ENABLE_MPK +void bpftime_shm::enable_mpk() +{ + if (manager == nullptr|| !is_mpk_init) { + return; + } + if (pkey_set(pkey, PKEY_DISABLE_WRITE) == -1) { + spdlog::error("pkey_set read only failed"); + } +} + +void bpftime_shm::disable_mpk() +{ + if (manager == nullptr || !is_mpk_init) { + return; + } + if (pkey_set(pkey, 0) == -1) { + spdlog::error("pkey_set disable failed"); + } +} +#endif + bool bpftime_shm::is_exist_fake_fd(int fd) const { if (manager == nullptr || fd < 0 || @@ -518,6 +541,23 @@ bpftime_shm::bpftime_shm(const char *shm_name, shm_open_type type) "NOT creating global shm. This is only for testing purpose."); return; } + +#if BPFTIME_ENABLE_MPK + // init mpk key + pkey = pkey_alloc(0, PKEY_DISABLE_WRITE); + if (pkey == -1) { + spdlog::error("pkey_alloc failed"); + return; + } + + // protect shm segment + if (pkey_mprotect(segment.get_address(), segment.get_size(), + PROT_READ | PROT_WRITE, pkey) == -1) { + spdlog::error("pkey_mprotect failed"); + return; + } + is_mpk_init = true; +#endif } bpftime_shm::bpftime_shm(bpftime::shm_open_type type) diff --git a/runtime/src/bpftime_shm_internal.hpp b/runtime/src/bpftime_shm_internal.hpp index bb1f45f0..9515b25d 100644 --- a/runtime/src/bpftime_shm_internal.hpp +++ b/runtime/src/bpftime_shm_internal.hpp @@ -31,6 +31,12 @@ class bpftime_shm { // Configuration for the agent. e.g, which helpers are enabled struct bpftime::agent_config *agent_config = nullptr; +#if BPFTIME_ENABLE_MPK + // mpk key for protect shm + bool is_mpk_init = false; + int pkey = 0; +#endif + public: // Get the configuration object const struct agent_config &get_agent_config() @@ -126,6 +132,11 @@ class bpftime_shm { void close_fd(int fd); bool is_exist_fake_fd(int fd) const; +#if BPFTIME_ENABLE_MPK + void enable_mpk(); + void disable_mpk(); +#endif + // initialize the shared memory globally bpftime_shm(bpftime::shm_open_type type); // initialize the shared memory with a given name