diff --git a/.gitignore b/.gitignore index 3b9a3efce0..9e7ac4e876 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ driver/driver_config.h .vscode .cache.mk build* +.idea/ +cmake-build-debug/ +kindling-falcolib-probe.tar.gz \ No newline at end of file diff --git a/NOTICES b/NOTICES index a1d8684f5c..0719b4edf6 100644 --- a/NOTICES +++ b/NOTICES @@ -15,7 +15,7 @@ limitations under the License. SUBCOMPONENTS: -- The file is licensed separately, see the header +- The file is licensed separately, see the header of the file. Copyright (c) 2006 Alexander Chemeris. - The files in and its subdirectories are used to compile the @@ -25,4 +25,8 @@ SUBCOMPONENTS: - The files in and its subdirectories are licensed separately, see the headers of each file. - Copyright (c) 2007-2010 Baptiste Lepilleur. \ No newline at end of file + Copyright (c) 2007-2010 Baptiste Lepilleur. + +- The two files userspace/libscap/scap_func_symbol.c and + userspace/libscap/scap_func_symbol.h are licensed with apache 2.0. + Copyright (c) 2016 GitHub, Inc. \ No newline at end of file diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 8c81a3025c..9058c6bf70 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -119,6 +119,38 @@ static __always_inline int bpf_##x(void *ctx) \ \ static __always_inline int __bpf_##x(struct filler_data *data) \ +#define UP_FILLER_RAW(x) \ +static __always_inline int __bpf_##x(struct filler_data *data); \ + \ +static __always_inline int bpf_##x(void *ctx) \ + +#define UP_FILLER(x) \ +static __always_inline int __bpf_##x(struct filler_data *data); \ + \ +static __always_inline int bpf_##x(void *ctx) \ +{ \ + struct filler_data data; \ + int res; \ + \ + res = init_filler_data(ctx, &data, false); \ + if (res == PPM_SUCCESS) { \ + if (!data.state->tail_ctx.len) \ + write_evt_hdr(&data); \ + res = __bpf_##x(&data); \ + } \ + \ + if (res == PPM_SUCCESS) \ + res = push_evt_frame(ctx, &data); \ + \ + if (data.state) \ + data.state->tail_ctx.prev_res = res; \ + \ + bpf_kp_terminate_filler(&data); \ + return 0; \ +} \ + \ +static __always_inline int __bpf_##x(struct filler_data *data) \ + FILLER_RAW(terminate_filler) { struct sysdig_bpf_per_cpu_state *state; @@ -4966,7 +4998,6 @@ KP_FILLER(tcp_send_loss_probe_e) KP_FILLER(tcp_connect_kprobe_x) { - struct pt_regs *args = (struct pt_regs*)data->ctx; int retval = 0; retval= regs_return_value(args); @@ -5214,4 +5245,265 @@ FILLER(cpu_analysis_e, false) { return 0; } + +static __always_inline int32_t get_fd_from_conn_intf_core(struct go_interface conn_intf) +{ + void *fd_ptr; + bpf_probe_read(&fd_ptr, sizeof(fd_ptr), conn_intf.ptr); + + int64_t sysfd; + bpf_probe_read(&sysfd, sizeof(int64_t), fd_ptr + 16); + return sysfd; +} + +static __always_inline int32_t get_fd_from_http2_Framer(const void *framer_ptr) +{ + struct go_interface io_writer_interface; + // At this point, we have the following struct: + // go.itab.*google.golang.org/grpc/internal/transport.bufWriter,io.Writer + bpf_probe_read(&io_writer_interface, sizeof(io_writer_interface), framer_ptr + 112); + + struct go_interface conn_intf; + bpf_probe_read(&conn_intf, sizeof(conn_intf), io_writer_interface.ptr + 40); + + return get_fd_from_conn_intf_core(conn_intf); +} + +static __always_inline void parse_header_field(char *dst, int *size, const void *header_field_ptr) +{ + struct gostring str = {}; + bpf_probe_read(&str, sizeof(str), header_field_ptr); + if (str.len <= 0) + { + *size = 0; + return; + } + + *size = (int)min(str.len, HEADER_FIELD_STR_SIZE); + + bpf_probe_read(dst, *size, str.ptr); +} + +// encode grpc-header, then send +UP_FILLER(probe_loopy_writer_write_header){ + struct pt_regs* regs = (struct pt_regs*) data->ctx; + const void *sp = (const void *)_READ(regs->sp); + + uint32_t stream_id = 0; + bpf_probe_read(&stream_id, sizeof(uint32_t), sp + 16); + + bool end_stream = false; + bpf_probe_read(&end_stream, sizeof(bool), sp + 20); + + void *fields_ptr; + bpf_probe_read(&fields_ptr, sizeof(void *), sp + 24); + + int64_t fields_len; + bpf_probe_read(&fields_len, sizeof(int64_t), sp + 24 + 8); + + void *loopy_writer_ptr = NULL; + bpf_probe_read(&loopy_writer_ptr, sizeof(loopy_writer_ptr), sp + 8); + + void *framer_ptr; + bpf_probe_read(&framer_ptr, sizeof(framer_ptr), loopy_writer_ptr + 40); + + struct go_grpc_framer_t go_grpc_framer; + bpf_probe_read(&go_grpc_framer, sizeof(go_grpc_framer), framer_ptr); + + const int32_t fd = get_fd_from_http2_Framer(go_grpc_framer.http2_framer); + + struct key_field key = {0}; + + struct value_field status = {0}; + struct value_field grpc_status = {0}; + struct value_field scheme = {0}; + struct value_field authority = {0}; + struct value_field path = {0}; + +#pragma unroll + for (size_t i = 0; i < 10; ++i) + { + if (i >= fields_len) + { + continue; + } + // Size of the golang hpack.HeaderField struct = 40 + parse_header_field(&key.msg, &key.size, fields_ptr + i * 40); + + // :status, grpc-status, :scheme, :path, :authority + if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 't' && key.msg[3] == 'a') + { + parse_header_field(&status.msg, &status.size, fields_ptr + i * 40 + 16); + break; + } + else if(key.size == 11 && key.msg[5] == 's' && key.msg[6] == 't' && key.msg[7] == 'a' && key.msg[8] == 't') + { + parse_header_field(&grpc_status.msg, &grpc_status.size, fields_ptr + i * 40 + 16); + break; + } + else if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 'c' && key.msg[3] == 'h') + { + parse_header_field(&scheme.msg, &scheme.size, fields_ptr + i * 40 + 16); + } + else if(key.size == 5 && key.msg[0] == ':' && key.msg[1] == 'p' && key.msg[2] == 'a' && key.msg[3] == 't') + { + parse_header_field(&path.msg, &path.size, fields_ptr + i * 40 + 16); + } else if(key.size == 10 && key.msg[0] == ':' && key.msg[1] == 'a' && key.msg[2] == 'u' && key.msg[3] == 't') + { + parse_header_field(&authority.msg, &authority.size, fields_ptr + i * 40 + 16); + } + } + + int res; + res = bpf_val_to_ring(data, stream_id); + res = bpf_val_to_ring(data, fd); + res = bpf_val_to_ring(data, (uint32_t)end_stream); + res = bpf_val_to_ring_type(data, (unsigned long long)status.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)grpc_status.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)scheme.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)authority.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)path.msg, PT_CHARBUF); + return 0; +} + +// server side: receive grpc-header +UP_FILLER(probe_http2_server_operate_headers){ + struct pt_regs* regs = (struct pt_regs*) data->ctx; + const void *sp = (const void *)_READ(regs->sp); + + void *http2_server_ptr = NULL; + bpf_probe_read(&http2_server_ptr, sizeof(http2_server_ptr), sp + 8); + + void *frame_ptr; + bpf_probe_read(&frame_ptr, sizeof(void *), sp + 16); + + struct go_interface conn_intf; + bpf_probe_read(&conn_intf, sizeof(conn_intf), http2_server_ptr + 32); + + const int32_t fd = get_fd_from_conn_intf_core(conn_intf); + + void *fields_ptr; + bpf_probe_read(&fields_ptr, sizeof(void *), frame_ptr + 8); + + int64_t fields_len; + bpf_probe_read(&fields_len, sizeof(int64_t), frame_ptr + 8 + 8); + + void *HeadersFrame_ptr; + bpf_probe_read(&HeadersFrame_ptr, sizeof(HeadersFrame_ptr), frame_ptr + 0); + + uint32_t stream_id; + bpf_probe_read(&stream_id, sizeof(uint32_t), HeadersFrame_ptr + 8); + + uint8_t flags; + bpf_probe_read(&flags, sizeof(uint8_t), HeadersFrame_ptr + 2); + const bool end_stream = flags & (0x1); + + struct key_field key = {0}; + struct value_field scheme = {0}; + struct value_field authority = {0}; + struct value_field path = {0}; + +#pragma unroll + for (size_t i = 0; i < 10; ++i) + { + if (i >= fields_len) + { + continue; + } + // Size of the golang hpack.HeaderField struct = 40 + parse_header_field(&key.msg, &key.size, fields_ptr + i * 40); + + // :scheme, :path, :authority + if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 'c' && key.msg[3] == 'h') + { + parse_header_field(&scheme.msg, &scheme.size, fields_ptr + i * 40 + 16); + } + else if(key.size == 5 && key.msg[0] == ':' && key.msg[1] == 'p' && key.msg[2] == 'a' && key.msg[3] == 't') + { + parse_header_field(&path.msg, &path.size, fields_ptr + i * 40 + 16); + } + else if(key.size == 10 && key.msg[0] == ':' && key.msg[1] == 'a' && key.msg[2] == 'u' && key.msg[3] == 't') + { + parse_header_field(&authority.msg, &authority.size, fields_ptr + i * 40 + 16); + } + } + + int res; + res = bpf_val_to_ring(data, stream_id); + res = bpf_val_to_ring(data, fd); + res = bpf_val_to_ring(data, (uint32_t)end_stream); + res = bpf_val_to_ring_type(data, (unsigned long long)scheme.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)authority.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)path.msg, PT_CHARBUF); + return 0; +} + +// client side: receive grpc-header +UP_FILLER(probe_http2_client_operate_headers){ + struct pt_regs* regs = (struct pt_regs*) data->ctx; + const void *sp = (const void *)_READ(regs->sp); + + void *http2_client_ptr = NULL; + bpf_probe_read(&http2_client_ptr, sizeof(http2_client_ptr), sp + 8); + + void *frame_ptr; + bpf_probe_read(&frame_ptr, sizeof(void *), sp + 16); + + struct go_interface conn_intf; + bpf_probe_read(&conn_intf, sizeof(conn_intf), http2_client_ptr + 64); + + const int32_t fd = get_fd_from_conn_intf_core(conn_intf); + + void *fields_ptr; + bpf_probe_read(&fields_ptr, sizeof(void *), frame_ptr + 8); + + int64_t fields_len; + bpf_probe_read(&fields_len, sizeof(int64_t), frame_ptr + 8 + 8); + + void *HeadersFrame_ptr; + bpf_probe_read(&HeadersFrame_ptr, sizeof(HeadersFrame_ptr), frame_ptr); + + uint32_t stream_id; + bpf_probe_read(&stream_id, sizeof(uint32_t), HeadersFrame_ptr + 8); + + uint8_t flags; + bpf_probe_read(&flags, sizeof(uint8_t), HeadersFrame_ptr + 2); + const bool end_stream = flags & (0x1); + + struct key_field key = {0}; + struct value_field status = {0}; + struct value_field grpc_status = {0}; + +#pragma unroll + for (size_t i = 0; i < 10; ++i) + { + if (i >= fields_len) + { + continue; + } + // Size of the golang hpack.HeaderField struct = 40 + parse_header_field(&key.msg, &key.size, fields_ptr + i * 40); + + // :status, grpc-status + if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 't' && key.msg[3] == 'a') + { + parse_header_field(&status.msg, &status.size, fields_ptr + i * 40 + 16); + break; + } + else if(key.size == 11 && key.msg[5] == 's' && key.msg[6] == 't' && key.msg[7] == 'a' && key.msg[8] == 't') + { + parse_header_field(&grpc_status.msg, &grpc_status.size, fields_ptr + i * 40 + 16); + break; + } + } + + int res; + res = bpf_val_to_ring(data, stream_id); + res = bpf_val_to_ring(data, fd); + res = bpf_val_to_ring(data, (uint32_t)end_stream); + res = bpf_val_to_ring_type(data, (unsigned long long)status.msg, PT_CHARBUF); + res = bpf_val_to_ring_type(data, (unsigned long long)grpc_status.msg, PT_CHARBUF); + return 0; +} + #endif diff --git a/driver/bpf/maps.h b/driver/bpf/maps.h index c6402a802d..6503ba8bbb 100644 --- a/driver/bpf/maps.h +++ b/driver/bpf/maps.h @@ -198,6 +198,39 @@ struct bpf_map_def __bpf_section("maps") cpu_focus_threads = { .value_size = sizeof(u64), .max_entries = 65535, }; + +// grpc +#define MAX_HEADER_COUNT 32 +#define HEADER_FIELD_STR_SIZE 40 +struct go_grpc_framer_t +{ + void *writer; + void *http2_framer; +}; + +struct go_interface +{ + int64_t type; + void *ptr; +}; + +struct key_field { + uint32_t size; + char msg[40]; +}; + +struct value_field { + uint32_t size; + char msg[40]; +}; + +// This matches the golang string object memory layout. Used to help read golang string objects in BPF code. +struct gostring +{ + const char *ptr; + int64_t len; +}; + #endif // __KERNEL__ #endif diff --git a/driver/bpf/plumbing_helpers.h b/driver/bpf/plumbing_helpers.h index 99c86df855..8c1c081529 100644 --- a/driver/bpf/plumbing_helpers.h +++ b/driver/bpf/plumbing_helpers.h @@ -17,6 +17,8 @@ or GPL2.txt for full copies of the license. #include "types.h" #include "builtins.h" +// #define BPF_DEBUG + #define _READ(P) ({ typeof(P) _val; \ memset(&_val, 0, sizeof(_val)); \ bpf_probe_read(&_val, sizeof(_val), &P); \ diff --git a/driver/bpf/probe.c b/driver/bpf/probe.c index 5b9175d291..c3b437b6e4 100644 --- a/driver/bpf/probe.c +++ b/driver/bpf/probe.c @@ -47,6 +47,14 @@ int bpf_kp_##event(struct pt_regs *ctx) __bpf_section(KRET_NAME #event) \ int bpf_kret_##event(struct pt_regs *ctx) +#define BPF_UPROBE(event, func_symbol) \ +__bpf_section(UP_NAME #event ":" #func_symbol) \ +int bpf_up_##event(struct pt_regs *ctx) + +#define BPF_URET_PROBE(event, func_symbol) \ +__bpf_section(URET_NAME #event ":" #func_symbol) \ +int bpf_uret_##event(struct pt_regs *ctx) + BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args) { const struct syscall_evt_pair *sc_evt; @@ -131,7 +139,7 @@ BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args) u64 exit_time = bpf_ktime_get_ns(); bpf_map_update_elem(&cpu_focus_threads, &tid, &exit_time, BPF_ANY); } - + #endif if (!settings->capture_enabled) return 0; @@ -750,6 +758,50 @@ BPF_KPROBE(sock_sendmsg) { return 0; } #endif + + +BPF_UPROBE(probe_loopy_writer_write_header, google.golang.org/grpc/internal/transport.(*loopyWriter).writeHeader) +{ + struct sysdig_bpf_settings *settings; + + settings = get_bpf_settings(); + if (!settings) + return 0; + + if(prepare_filler(ctx, ctx, PPME_GRPC_HEADER_ENCODE_E, settings, UF_NEVER_DROP)) { + bpf_probe_loopy_writer_write_header(ctx); + } + return 0; +} + +BPF_UPROBE(probe_http2_server_operate_headers, google.golang.org/grpc/internal/transport.(*http2Server).operateHeaders) +{ + struct sysdig_bpf_settings *settings; + + settings = get_bpf_settings(); + if (!settings) + return 0; + + if(prepare_filler(ctx, ctx, PPME_GRPC_HEADER_SERVER_RECV_E, settings, UF_NEVER_DROP)) { + bpf_probe_http2_server_operate_headers(ctx); + } + return 0; +} + +BPF_UPROBE(probe_http2_client_operate_headers, google.golang.org/grpc/internal/transport.(*http2Client).operateHeaders) +{ + struct sysdig_bpf_settings *settings; + + settings = get_bpf_settings(); + if (!settings) + return 0; + + if(prepare_filler(ctx, ctx, PPME_GRPC_HEADER_CLIENT_RECV_E, settings, UF_NEVER_DROP)) { + bpf_probe_http2_client_operate_headers(ctx); + } + return 0; +} + char kernel_ver[] __bpf_section("kernel_version") = UTS_RELEASE; char __license[] __bpf_section("license") = "GPL"; diff --git a/driver/bpf/types.h b/driver/bpf/types.h index 613c243a87..28d6185b58 100644 --- a/driver/bpf/types.h +++ b/driver/bpf/types.h @@ -29,6 +29,9 @@ or GPL2.txt for full copies of the license. #define KP_NAME "kprobe/" #define KRET_NAME "kretprobe/" +#define UP_NAME "uprobe/" +#define URET_NAME "uretprobe/" + #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sys_enter_args { unsigned long regs; diff --git a/driver/event_table.c b/driver/event_table.c index 5961ec3735..35b3510eb2 100644 --- a/driver/event_table.c +++ b/driver/event_table.c @@ -355,7 +355,13 @@ const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = { /* PPME_TCP_RECEIVE_RESET_E */{"tcp_receive_reset", EC_NET, EF_DROP_SIMPLE_CONS | EF_NONE_PARSE, 2, {{"tuple", PT_SOCKTUPLE, PF_NA}, {"state", PT_UINT32, PF_DEC} } }, /* PPME_TCP_RECEIVE_RESET_X */{"tcp_send_reset", EC_NET, EF_UNUSED, 0}, /* PPME_CPU_ANALYSIS_E */{"cpu_analysis", EC_PROCESS, EF_NONE_PARSE, 6, {{"start_ts", PT_UINT64, PF_DEC}, {"end_ts", PT_UINT64, PF_DEC}, {"cnt", PT_UINT32, PF_DEC}, {"time_specs", PT_BYTEBUF, PF_NA}, {"runq_latency", PT_BYTEBUF, PF_NA}, {"time_type", PT_BYTEBUF, PF_NA}}}, - /* PPME_CPU_ANALYSIS_X */{"cpu_analysis", EC_PROCESS, EF_UNUSED, 0} + /* PPME_CPU_ANALYSIS_X */{"cpu_analysis", EC_PROCESS, EF_UNUSED, 0}, + /* PPME_GRPC_HEADER_ENCODE_E */ {"grpc_header_encoder", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 8, {{"streamid", PT_UINT32, PF_DEC}, {"fd", PT_INT32, PF_DEC}, {"end_stream", PT_UINT32, PF_DEC}, {"status", PT_CHARBUF, PF_NA}, {"grpc_status", PT_CHARBUF, PF_NA}, {"scheme", PT_CHARBUF, PF_NA}, {"authority", PT_CHARBUF, PF_NA}, {"path", PT_CHARBUF, PF_NA}}}, + /* PPME_GRPC_HEADER_ENCODE_X */ {"grpc_header_encoder", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 0}, + /* PPME_GRPC_HEADER_SERVER_RECV_E */ {"grpc_header_server_recv", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 6, {{"streamid", PT_UINT32, PF_DEC}, {"fd", PT_INT32, PF_DEC}, {"end_stream", PT_UINT32, PF_DEC}, {"scheme", PT_CHARBUF, PF_NA}, {"authority", PT_CHARBUF, PF_NA}, {"path", PT_CHARBUF, PF_NA}}}, + /* PPME_GRPC_HEADER_SERVER_RECV_X */ {"grpc_header_server_recv", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 0}, + /* PPME_GRPC_HEADER_CLIENT_RECV_E */ {"grpc_header_client_recv", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 5, {{"streamid", PT_UINT32, PF_DEC}, {"fd", PT_INT32, PF_DEC}, {"end_stream", PT_UINT32, PF_DEC}, {"status", PT_CHARBUF, PF_NA}, {"grpc_status", PT_CHARBUF, PF_NA}}}, + /* PPME_GRPC_HEADER_CLIENT_RECV_X */ {"grpc_header_client_recv", EC_NET, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_SIMPLE_CONS, 0} /* NB: Starting from scap version 1.2, event types will no longer be changed when an event is modified, and the only kind of change permitted for pre-existent events is adding parameters. * New event types are allowed only for new syscalls or new internal events. * The number of parameters can be used to differentiate between event versions. diff --git a/driver/ppm_events_public.h b/driver/ppm_events_public.h index 3f86018c85..fb5a2af6c8 100644 --- a/driver/ppm_events_public.h +++ b/driver/ppm_events_public.h @@ -1001,7 +1001,13 @@ enum ppm_event_type { PPME_TCP_SEND_RESET_X = 341, PPME_CPU_ANALYSIS_E = 342, PPME_CPU_ANALYSIS_X = 343, - PPM_EVENT_MAX = 344 + PPME_GRPC_HEADER_ENCODE_E = 344, + PPME_GRPC_HEADER_ENCODE_X = 345, + PPME_GRPC_HEADER_SERVER_RECV_E = 346, + PPME_GRPC_HEADER_SERVER_RECV_X = 347, + PPME_GRPC_HEADER_CLIENT_RECV_E = 348, + PPME_GRPC_HEADER_CLIENT_RECV_X = 349, + PPM_EVENT_MAX = 350 }; /*@}*/ diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index e359d02ebd..5e5d876709 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -54,6 +54,7 @@ list(APPEND targetfiles scap_savefile.c scap_procs.c scap_userlist.c + scap_func_symbol.c syscall_info_table.c ../../driver/dynamic_params_table.c ../../driver/event_table.c diff --git a/userspace/libscap/scap-int.h b/userspace/libscap/scap-int.h index b7ca10c6f5..0be4aa8a6d 100644 --- a/userspace/libscap/scap-int.h +++ b/userspace/libscap/scap-int.h @@ -156,11 +156,18 @@ struct scap // Anonymous struct with bpf stuff struct { + // save BPF prog fds used for close int m_bpf_prog_fds[BPF_PROGS_MAX]; int m_bpf_prog_cnt; bool m_bpf_fillers[BPF_PROGS_MAX]; int m_bpf_event_fd[BPF_PROGS_MAX]; int m_bpf_map_fds[BPF_MAPS_MAX]; + + int m_uprobe_prog_fds[4 * BPF_PROGS_MAX]; + int m_uprobe_event_fd[4 * BPF_PROGS_MAX]; + bool m_uprobe_array_idx_is_used[4 * BPF_PROGS_MAX]; + int m_uprobe_prog_cnt; + int m_bpf_prog_array_map_idx; }; @@ -183,6 +190,8 @@ struct scap uint64_t m_proc_scan_timeout_ms; uint64_t m_proc_scan_log_interval_ms; + bool enable_uprobe; + // Function which may be called to log a debug event void(*m_debug_log_fn)(const char* msg); }; diff --git a/userspace/libscap/scap.c b/userspace/libscap/scap.c index bbb746fe05..493f800cfe 100644 --- a/userspace/libscap/scap.c +++ b/userspace/libscap/scap.c @@ -141,6 +141,10 @@ static uint32_t get_max_consumers() return 0; } +bool load_uprobe(scap_t *handle, const char *path, bool is_uprobe, const char *target_file_path) { + return __load_uprobe(handle, path, is_uprobe, target_file_path); +} + #ifndef _WIN32 scap_t* scap_open_live_int(char *error, int32_t *rc, proc_entry_callback proc_callback, @@ -326,6 +330,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc, // // Open and initialize all the devices // + // if support bpf if(handle->m_bpf) { if((*rc = scap_bpf_load(handle, bpf_probe)) != SCAP_SUCCESS) diff --git a/userspace/libscap/scap.h b/userspace/libscap/scap.h index 2a685a982a..4a080c0a8a 100644 --- a/userspace/libscap/scap.h +++ b/userspace/libscap/scap.h @@ -72,6 +72,8 @@ struct iovec; #define SCAP_FAILURE 1 #define SCAP_TIMEOUT -1 #define SCAP_UNKNOWN_KPROBE -2 +#define SCAP_UPROBE_SKIP -3 +#define SCAP_UPROBE_ARRAY_FULL -4 #define SCAP_ILLEGAL_INPUT 3 #define SCAP_NOTFOUND 4 #define SCAP_INPUT_TOO_SMALL 5 @@ -642,6 +644,8 @@ scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc); */ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc); +bool load_uprobe(scap_t *handle, const char *path, bool is_uprobe, const char *user_probe_path); + /*! \brief Close a capture handle. diff --git a/userspace/libscap/scap_bpf.c b/userspace/libscap/scap_bpf.c index 89d825eb39..0ca67c60c1 100644 --- a/userspace/libscap/scap_bpf.c +++ b/userspace/libscap/scap_bpf.c @@ -38,12 +38,15 @@ limitations under the License. #include "scap.h" #include "scap-int.h" #include "scap_bpf.h" +#include "scap_func_symbol.h" #include "driver_config.h" #include "../../driver/bpf/types.h" #include "../../driver/bpf/maps.h" #include "compat/misc.h" #include "compat/bpf.h" - +// #define NONE "\033[m" +// #define RED "\033[0;32;31m" +// #define GREEN "\033[0;32;32m" #ifdef MINIMAL_BUILD #undef MINIMAL_BUILD @@ -57,7 +60,8 @@ limitations under the License. // subset of features needed. // -struct bpf_map_data { +struct bpf_map_data +{ int fd; size_t elf_offset; struct bpf_map_def def; @@ -67,12 +71,11 @@ static const int BUF_SIZE_PAGES = 2048; static const int BPF_LOG_SIZE = 1 << 18; -static char* license; +static char *license; #define FILLER_NAME_FN(x) #x, static const char *g_filler_names[PPM_FILLER_MAX] = { - FILLER_LIST_MAPPER(FILLER_NAME_FN) -}; + FILLER_LIST_MAPPER(FILLER_NAME_FN)}; #undef FILLER_NAME_FN static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) @@ -109,8 +112,8 @@ static int bpf_map_update_elem(int fd, const void *key, const void *value, uint6 bzero(&attr, sizeof(attr)); attr.map_fd = fd; - attr.key = (unsigned long) key; - attr.value = (unsigned long) value; + attr.key = (unsigned long)key; + attr.value = (unsigned long)value; attr.flags = flags; return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); @@ -123,8 +126,8 @@ static int bpf_map_lookup_elem(int fd, const void *key, void *value) bzero(&attr, sizeof(attr)); attr.map_fd = fd; - attr.key = (unsigned long) key; - attr.value = (unsigned long) value; + attr.key = (unsigned long)key; + attr.value = (unsigned long)value; return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } @@ -147,35 +150,40 @@ static int bpf_map_create(enum bpf_map_type map_type, } static uint32_t find_vdso_code() { - char *vdso = (char *) getauxval(AT_SYSINFO_EHDR); - if (vdso == NULL) { - return 0; - } - if (memcmp(vdso, ELFMAG, 4)) { - return 0; - } - const ElfW(Ehdr) *ehdr = (const ElfW(Ehdr) *) vdso; - int i; - for (i = 0; i < ehdr->e_shnum; i++) { - const ElfW(Shdr) *shdr = (const ElfW(Shdr) *)(vdso + ehdr->e_shoff + (i * ehdr->e_shentsize)); - if (shdr->sh_type == SHT_NOTE) { - const char *ptr = (const char *)(vdso + shdr->sh_offset); - const char *end = ptr + shdr->sh_size; - while (ptr < end) { - const ElfW(Nhdr) *nhdr = (const ElfW(Nhdr) *) ptr; - ptr += sizeof(*nhdr); - const char *name = ptr; - ptr += (nhdr->n_namesz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); - const char *desc = ptr; - ptr += (nhdr->n_descsz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); - if ((nhdr->n_namesz > 5 && !memcmp(name, "Linux", 5)) && nhdr->n_descsz == 4 && !nhdr->n_type) - { - return *(uint32_t *) desc; - } - } - } - } - return 0; + char *vdso = (char *)getauxval(AT_SYSINFO_EHDR); + if(vdso == NULL) + { + return 0; + } + if(memcmp(vdso, ELFMAG, 4)) + { + return 0; + } + const ElfW(Ehdr) *ehdr = (const ElfW(Ehdr) *)vdso; + int i; + for(i = 0; i < ehdr->e_shnum; i++) + { + const ElfW(Shdr) *shdr = (const ElfW(Shdr) *)(vdso + ehdr->e_shoff + (i * ehdr->e_shentsize)); + if(shdr->sh_type == SHT_NOTE) + { + const char *ptr = (const char *)(vdso + shdr->sh_offset); + const char *end = ptr + shdr->sh_size; + while(ptr < end) + { + const ElfW(Nhdr) *nhdr = (const ElfW(Nhdr) *)ptr; + ptr += sizeof(*nhdr); + const char *name = ptr; + ptr += (nhdr->n_namesz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); + const char *desc = ptr; + ptr += (nhdr->n_descsz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); + if((nhdr->n_namesz > 5 && !memcmp(name, "Linux", 5)) && nhdr->n_descsz == 4 && !nhdr->n_type) + { + return *(uint32_t *)desc; + } + } + } + } + return 0; } static uint32_t get_kernel_version() { @@ -183,68 +191,68 @@ static uint32_t get_kernel_version() char filename[256]; unsigned x, y, z; int i = 0; - for (i = 0; i < 4; i++) + for(i = 0; i < 4; i++) { switch(i) { - case 0: - { - return find_vdso_code(); - } - case 1: - { - // KERNEL_VERSION_CODE, as environment variable - // KERNEL_VERSION_CODE = (VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL - // same to KERNEL_VERSION - char *kernel_version_c = getenv("KERNEL_VERSION_CODE"); - if (kernel_version_c != NULL) - return atoi(kernel_version_c); + case 0: + { + return find_vdso_code(); + } + case 1: + { + // KERNEL_VERSION_CODE, as environment variable + // KERNEL_VERSION_CODE = (VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL + // same to KERNEL_VERSION + char *kernel_version_c = getenv("KERNEL_VERSION_CODE"); + if(kernel_version_c != NULL) + return atoi(kernel_version_c); + break; + } + case 2: + { + // ubuntu + // check /proc/version_signature + int t; + snprintf(filename, sizeof(filename), "%s/proc/version_signature", scap_get_host_root()); + int fd = open(filename, O_RDONLY, 0); + int res = read(fd, buf, sizeof(buf)); + if(sscanf(buf, "Ubuntu %d.%d.%d-%d.%d-generic %d.%d.%d", &t, &t, &t, &t, &t, &x, &y, &z) != 8) break; - } - case 2: - { - // ubuntu - // check /proc/version_signature - int t; - snprintf(filename, sizeof(filename), "%s/proc/version_signature", scap_get_host_root()); - int fd = open(filename, O_RDONLY, 0); - int res = read(fd, buf, sizeof(buf)); - if (sscanf(buf, "Ubuntu %d.%d.%d-%d.%d-generic %d.%d.%d", &t, &t, &t, &t, &t, &x, &y, &z) != 8) - break; - return KERNEL_VERSION(x, y, z); - } - case 3: - { - // debian - // check /proc/sys/kernel/version - snprintf(filename, sizeof(filename), "%s/proc/sys/kernel/version", scap_get_host_root()); - int fd = open(filename, O_RDONLY, 0); - if (fd < 0) - break; - int res = read(fd, buf, sizeof(buf)); - if (sscanf(buf, "#1 SMP Debian %d.%d.%d-x (xxxx-xx-xx)", &x, &y, &z) != 3) - break; - return KERNEL_VERSION(x, y, z); - } - case 4: - { - // uname - struct utsname utsn; - uname(&utsn); - if (sscanf(utsn.release, "%d.%d.%d", &x, &y, &z) != 3) - return 0; - return KERNEL_VERSION(x, y, z); - } - default: + return KERNEL_VERSION(x, y, z); + } + case 3: + { + // debian + // check /proc/sys/kernel/version + snprintf(filename, sizeof(filename), "%s/proc/sys/kernel/version", scap_get_host_root()); + int fd = open(filename, O_RDONLY, 0); + if(fd < 0) + break; + int res = read(fd, buf, sizeof(buf)); + if(sscanf(buf, "#1 SMP Debian %d.%d.%d-x (xxxx-xx-xx)", &x, &y, &z) != 3) + break; + return KERNEL_VERSION(x, y, z); + } + case 4: + { + // uname + struct utsname utsn; + uname(&utsn); + if(sscanf(utsn.release, "%d.%d.%d", &x, &y, &z) != 3) return 0; + return KERNEL_VERSION(x, y, z); + } + default: + return 0; } } } static uint32_t bpf_load_program(const struct bpf_insn *insns, - enum bpf_prog_type type, - size_t insns_cnt, - char *log_buf, - size_t log_buf_sz) + enum bpf_prog_type type, + size_t insns_cnt, + char *log_buf, + size_t log_buf_sz) { union bpf_attr attr; int fd; @@ -252,14 +260,14 @@ static uint32_t bpf_load_program(const struct bpf_insn *insns, bzero(&attr, sizeof(attr)); attr.prog_type = type; - attr.insn_cnt = (uint32_t) insns_cnt; - attr.insns = (unsigned long) insns; - attr.license = (unsigned long) license; - attr.log_buf = (unsigned long) NULL; + attr.insn_cnt = (uint32_t)insns_cnt; + attr.insns = (unsigned long)insns; + attr.license = (unsigned long)license; + attr.log_buf = (unsigned long)NULL; attr.log_size = 0; attr.log_level = 0; - if (type == BPF_PROG_TYPE_KPROBE) + if(type == BPF_PROG_TYPE_KPROBE) { attr.kern_version = get_kernel_version(); } @@ -270,7 +278,7 @@ static uint32_t bpf_load_program(const struct bpf_insn *insns, return fd; } - attr.log_buf = (unsigned long) log_buf; + attr.log_buf = (unsigned long)log_buf; attr.log_size = log_buf_sz; attr.log_level = 1; log_buf[0] = 0; @@ -283,7 +291,7 @@ static int bpf_raw_tracepoint_open(const char *name, int prog_fd) union bpf_attr attr; bzero(&attr, sizeof(attr)); - attr.raw_tracepoint.name = (unsigned long) name; + attr.raw_tracepoint.name = (unsigned long)name; attr.raw_tracepoint.prog_fd = prog_fd; return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr)); @@ -496,7 +504,22 @@ static int write_kprobe_events(const char *val) if(val == NULL) return -1; - fd = open("/sys/kernel/debug/tracing/kprobe_events", O_APPEND | O_WRONLY); + fd = open("/sys/kernel/debug/tracing/kprobe_events", O_APPEND | O_WRONLY); + + ret = write(fd, val, strlen(val)); + close(fd); + + return ret; +} + +static int write_uprobe_events(char *val) +{ + int fd, ret; + + if(val == NULL) + return -1; + + fd = open("/sys/kernel/debug/tracing/uprobe_events", O_APPEND | O_WRONLY); ret = write(fd, val, strlen(val)); close(fd); @@ -504,7 +527,7 @@ static int write_kprobe_events(const char *val) return ret; } -static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_insn *prog, int size) +static int32_t load_and_attach(scap_t *handle, const char *event, struct bpf_insn *prog, int size, const char *target_file_path) { struct perf_event_attr attr = {}; enum bpf_prog_type program_type = BPF_PROG_TYPE_UNSPEC; @@ -518,6 +541,8 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins bool is_kprobe = strncmp(event, "kprobe/", 7) == 0; bool is_kretprobe = strncmp(event, "kretprobe/", 10) == 0; + bool is_uprobe = strncmp(event, "uprobe/", 7) == 0; + bool is_uretprobe = strncmp(event, "uretprobe/", 10) == 0; bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0; bool is_raw_tracepoint = strncmp(event, "raw_tracepoint/", 15) == 0; @@ -575,6 +600,57 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins strcat(buf, "/id"); } } + else if(is_uprobe || is_uretprobe) + { + /* + * https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt + * uprobe use type BPF_PROG_TYPE_KPROBE + */ + program_type = BPF_PROG_TYPE_KPROBE; + if(is_uprobe) + event += 7; + else + event += 10; + + if(memcmp(event, "filler/", sizeof("filler/") - 1) != 0) + { + uint64_t addr; + + char *func_symbol = event; + int i = 0; + while(true) + { + if(func_symbol[i] == ':') + break; + i++; + } + func_symbol += (i + 1); + char str[200]; + sscanf(event, "%[^:]", str); + event = str; + err = bcc_resolve_symname(target_file_path, func_symbol, &addr); + if(err < 0) + { + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "failed to resolve symbol name '%s' error '%s'\n", func_symbol, strerror(errno)); + return SCAP_UPROBE_SKIP; + } + // printf(GREEN"%s:%s symbol exist\n"NONE, target_file_path, func_symbol); + char *identifier = generate_identifier(target_file_path); + snprintf(buf, sizeof(buf), "%s%s%s %s:0x%" PRIx64 "", + is_uprobe ? "p:" : "r:", event, identifier, target_file_path, addr); + err = write_uprobe_events(buf); + if(err < 0 && errno != 17) + { + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "failed to create uprobe '%s' error '%s'\n", event, strerror(errno)); + return SCAP_FAILURE; + } + + strcpy(buf, "/sys/kernel/debug/tracing/events/uprobes/"); + strcat(buf, event); + strcat(buf, identifier); + strcat(buf, "/id"); + } + } if(*event == 0) { @@ -583,17 +659,46 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins } fd = bpf_load_program(prog, program_type, insns_cnt, error, BPF_LOG_SIZE); + if(fd < 0) { - fprintf(stderr, "%s", error); - snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "bpf_load_program() err=%d event=%s message=%s", errno, event, error); + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "bpf_load_program() err=%d event=%s message=%s\n", errno, event, error); + // fprintf(stderr, "%s\n", handle->m_lasterr); free(error); return SCAP_FAILURE; } free(error); + if(target_file_path != NULL) + { + int cnt = 0; + // find an array space to store uprobe fd and pmc fd + if(handle->m_uprobe_prog_cnt == 0) + { + handle->m_uprobe_prog_cnt++; + } + while(handle->m_uprobe_array_idx_is_used[handle->m_uprobe_prog_cnt] == true) + { + handle->m_uprobe_prog_cnt = (handle->m_uprobe_prog_cnt + 1) % BPF_PROGS_MAX; + cnt++; + if(cnt > BPF_PROGS_MAX + 10) + { + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "handle->m_uprobe_prog_fds[] is full, please enlarge the size of m_uprobe_prog_fds[] and m_uprobe_event_fd[]"); + return SCAP_FAILURE; + } + if(handle->m_uprobe_prog_cnt == 0) + { + handle->m_uprobe_prog_cnt++; + } + } - handle->m_bpf_prog_fds[handle->m_bpf_prog_cnt++] = fd; + handle->m_uprobe_prog_fds[handle->m_uprobe_prog_cnt] = fd; + handle->m_uprobe_array_idx_is_used[handle->m_uprobe_prog_cnt] = true; + } + else + { + handle->m_bpf_prog_fds[handle->m_bpf_prog_cnt++] = fd; + } if(memcmp(event, "filler/", sizeof("filler/") - 1) == 0) { @@ -612,7 +717,7 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid filler name: %s", event); return SCAP_FAILURE; } - + // used for tail_call only for traccepoint, kprobe and uprobe use subfunction instead of tail_call err = bpf_map_update_elem(handle->m_bpf_map_fds[handle->m_bpf_prog_array_map_idx], &prog_id, &fd, BPF_ANY); if(err < 0) { @@ -640,7 +745,7 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins if(efd < 0) { if(strcmp(event, "exceptions/page_fault_user") == 0 || - strcmp(event, "exceptions/page_fault_kernel") == 0) + strcmp(event, "exceptions/page_fault_kernel") == 0) { return SCAP_SUCCESS; } @@ -649,7 +754,8 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins if(is_kprobe || is_kretprobe) { return SCAP_UNKNOWN_KPROBE; - }else + } + else { return SCAP_FAILURE; } @@ -675,11 +781,13 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "event %d fd %d err %s", id, efd, scap_strerror(handle, errno)); return SCAP_FAILURE; } + if(ioctl(efd, PERF_EVENT_IOC_ENABLE, 0)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "PERF_EVENT_IOC_ENABLE: %s", scap_strerror(handle, errno)); return SCAP_FAILURE; } + if(ioctl(efd, PERF_EVENT_IOC_SET_BPF, fd)) { close(efd); @@ -687,14 +795,25 @@ static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_ins return SCAP_FAILURE; } } - - handle->m_bpf_event_fd[handle->m_bpf_prog_cnt - 1] = efd; + if(target_file_path != NULL) + { + handle->m_uprobe_event_fd[handle->m_uprobe_prog_cnt] = efd; + // puts("==> add uprobe successfully:"); + // printf("event id %d\n",id); + // printf("event efd %d\n",efd); + // printf("prog fd %d\n",fd); + // printf("m_uprobe_prog_cnt %d\n", handle->m_uprobe_prog_cnt); + } + else + { + handle->m_bpf_event_fd[handle->m_bpf_prog_cnt - 1] = efd; + } return SCAP_SUCCESS; } #ifndef MINIMAL_BUILD -static int32_t load_bpf_file(scap_t *handle, const char *path) +static int32_t load_bpf_file(scap_t *handle, const char *path, bool is_uprobe, const char *target_file_path) { int j; int maps_shndx = 0; @@ -711,6 +830,14 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) struct utsname osname; int32_t res = SCAP_FAILURE; + static int program_fd = 0; + static Elf *elf = NULL; + + if(is_uprobe) + { + goto load_prog; + } + if(uname(&osname)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't call uname()"); @@ -723,21 +850,21 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) return SCAP_FAILURE; } - int program_fd = open(path, O_RDONLY, 0); + program_fd = open(path, O_RDONLY, 0); if(program_fd < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't open BPF probe '%s': %s", path, scap_strerror(handle, errno)); return SCAP_FAILURE; } - Elf *elf = elf_begin(program_fd, ELF_C_READ_MMAP_PRIVATE, NULL); + elf = elf_begin(program_fd, ELF_C_READ_MMAP_PRIVATE, NULL); if(!elf) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't read ELF format"); goto cleanup; } - GElf_Ehdr ehdr; + static GElf_Ehdr ehdr; if(gelf_getehdr(elf, &ehdr) != &ehdr) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't read ELF header"); @@ -760,19 +887,21 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) strtabidx = shdr.sh_link; symbols = data; } - else if(strcmp(shname, "kernel_version") == 0) { + else if(strcmp(shname, "kernel_version") == 0) + { if(strcmp(osname.release, data->d_buf)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe is compiled for %s, but running version is %s", - (char *) data->d_buf, osname.release); + (char *)data->d_buf, osname.release); goto cleanup; } } - else if(strcmp(shname, "probe_version") == 0) { + else if(strcmp(shname, "probe_version") == 0) + { if(strcmp(PROBE_VERSION, data->d_buf)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe version is %s, but running version is %s", - (char *) data->d_buf, PROBE_VERSION); + (char *)data->d_buf, PROBE_VERSION); goto cleanup; } } @@ -796,7 +925,14 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) goto cleanup; } - if(load_maps(handle, maps, nr_maps) != SCAP_SUCCESS) + if(is_uprobe) + { + for(j = 0; j < nr_maps; ++j) + { + maps[j].fd = handle->m_bpf_map_fds[j]; + } + } + else if(load_maps(handle, maps, nr_maps) != SCAP_SUCCESS) { goto cleanup; } @@ -818,7 +954,7 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) continue; } - insns = (struct bpf_insn *) data_prog->d_buf; + insns = (struct bpf_insn *)data_prog->d_buf; if(parse_relocations(handle, data, symbols, &shdr, insns, maps, nr_maps)) { @@ -827,26 +963,40 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) } } +load_prog: for(j = 0; j < ehdr.e_shnum; ++j) { if(get_elf_section(elf, j, &ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) { continue; } + if(is_uprobe) + { + if(memcmp(shname, "uprobe/", sizeof("uprobe/") - 1) == 0 || + memcmp(shname, "uretprobe/", sizeof("uretprobe/") - 1) == 0) + { + res = load_and_attach(handle, shname, data->d_buf, data->d_size, target_file_path); + if(res != SCAP_SUCCESS && res != SCAP_UPROBE_SKIP) + { + goto cleanup; + } + } + continue; + } if(memcmp(shname, "tracepoint/", sizeof("tracepoint/") - 1) == 0 || memcmp(shname, "raw_tracepoint/", sizeof("raw_tracepoint/") - 1) == 0 || memcmp(shname, "kprobe/", sizeof("kprobe/") - 1) == 0 || memcmp(shname, "kretprobe/", sizeof("kretprobe/") - 1) == 0) { - int load_result = load_tracepoint(handle, shname, data->d_buf, data->d_size); + res = load_and_attach(handle, shname, data->d_buf, data->d_size, NULL); if((memcmp(shname, "kprobe/", sizeof("kprobe/") - 1) == 0 || memcmp(shname, "kretprobe/", sizeof("kretprobe/") - 1) == 0) && - load_result == SCAP_UNKNOWN_KPROBE) + res == SCAP_UNKNOWN_KPROBE) { - continue ; + continue; } - if(load_result != SCAP_SUCCESS) + if(res != SCAP_SUCCESS) { goto cleanup; } @@ -855,10 +1005,21 @@ static int32_t load_bpf_file(scap_t *handle, const char *path) res = SCAP_SUCCESS; cleanup: - elf_end(elf); - close(program_fd); + if(res != SCAP_SUCCESS && res != SCAP_UPROBE_SKIP) + { + elf_end(elf); + close(program_fd); + } return res; } + +bool __load_uprobe(scap_t *handle, const char *path, bool is_uprobe, const char *target_file_path) +{ + int pre_idx = handle->m_uprobe_prog_cnt; + load_bpf_file(handle, path, is_uprobe, target_file_path); + return handle->m_uprobe_prog_cnt > pre_idx; +} + #endif // MINIMAL_BUILD static void *perf_event_mmap(scap_t *handle, int fd) @@ -1048,7 +1209,7 @@ int32_t scap_bpf_stop_capture(scap_t *handle) return SCAP_SUCCESS; } -int32_t scap_bpf_set_snaplen(scap_t* handle, uint32_t snaplen) +int32_t scap_bpf_set_snaplen(scap_t *handle, uint32_t snaplen) { struct sysdig_bpf_settings settings; int k = 0; @@ -1075,7 +1236,7 @@ int32_t scap_bpf_set_snaplen(scap_t* handle, uint32_t snaplen) return SCAP_SUCCESS; } -int32_t scap_bpf_set_fullcapture_port_range(scap_t* handle, uint16_t range_start, uint16_t range_end) +int32_t scap_bpf_set_fullcapture_port_range(scap_t *handle, uint16_t range_start, uint16_t range_end) { struct sysdig_bpf_settings settings; int k = 0; @@ -1097,7 +1258,7 @@ int32_t scap_bpf_set_fullcapture_port_range(scap_t* handle, uint16_t range_start return SCAP_SUCCESS; } -int32_t scap_bpf_set_statsd_port(scap_t* const handle, const uint16_t port) +int32_t scap_bpf_set_statsd_port(scap_t *const handle, const uint16_t port) { struct sysdig_bpf_settings settings = {}; int k = 0; @@ -1119,7 +1280,7 @@ int32_t scap_bpf_set_statsd_port(scap_t* const handle, const uint16_t port) return SCAP_SUCCESS; } -int32_t scap_bpf_disable_dynamic_snaplen(scap_t* handle) +int32_t scap_bpf_disable_dynamic_snaplen(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; @@ -1140,22 +1301,22 @@ int32_t scap_bpf_disable_dynamic_snaplen(scap_t* handle) return SCAP_SUCCESS; } -int32_t scap_bpf_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio) +int32_t scap_bpf_start_dropping_mode(scap_t *handle, uint32_t sampling_ratio) { switch(sampling_ratio) { - case 1: - case 2: - case 4: - case 8: - case 16: - case 32: - case 64: - case 128: - break; - default: - snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid sampling ratio size"); - return SCAP_FAILURE; + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + break; + default: + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid sampling ratio size"); + return SCAP_FAILURE; } struct sysdig_bpf_settings settings; @@ -1178,7 +1339,7 @@ int32_t scap_bpf_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio) return SCAP_SUCCESS; } -int32_t scap_bpf_stop_dropping_mode(scap_t* handle) +int32_t scap_bpf_stop_dropping_mode(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; @@ -1200,7 +1361,7 @@ int32_t scap_bpf_stop_dropping_mode(scap_t* handle) return SCAP_SUCCESS; } -int32_t scap_bpf_enable_dynamic_snaplen(scap_t* handle) +int32_t scap_bpf_enable_dynamic_snaplen(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; @@ -1221,7 +1382,7 @@ int32_t scap_bpf_enable_dynamic_snaplen(scap_t* handle) return SCAP_SUCCESS; } -int32_t scap_bpf_enable_page_faults(scap_t* handle) +int32_t scap_bpf_enable_page_faults(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; @@ -1242,7 +1403,7 @@ int32_t scap_bpf_enable_page_faults(scap_t* handle) return SCAP_SUCCESS; } -int32_t scap_bpf_enable_tracers_capture(scap_t* handle) +int32_t scap_bpf_enable_tracers_capture(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; @@ -1290,7 +1451,7 @@ int32_t scap_bpf_close(scap_t *handle) close(handle->m_devs[j].m_fd); } } - + // clear tracepoint and kprobe pmc fd for(j = 0; j < sizeof(handle->m_bpf_event_fd) / sizeof(handle->m_bpf_event_fd[0]); ++j) { if(handle->m_bpf_event_fd[j] > 0) @@ -1299,7 +1460,7 @@ int32_t scap_bpf_close(scap_t *handle) handle->m_bpf_event_fd[j] = 0; } } - + // clear tracepoint and kprobe bpf prog fd for(j = 0; j < sizeof(handle->m_bpf_prog_fds) / sizeof(handle->m_bpf_prog_fds[0]); ++j) { if(handle->m_bpf_prog_fds[j] > 0) @@ -1308,6 +1469,33 @@ int32_t scap_bpf_close(scap_t *handle) handle->m_bpf_prog_fds[j] = 0; } } + // clear uprobe pmc fd + for(j = 0; j < sizeof(handle->m_uprobe_event_fd) / sizeof(handle->m_uprobe_event_fd[0]); ++j) + { + if(handle->m_uprobe_event_fd[j] > 0) + { + close(handle->m_uprobe_event_fd[j]); + handle->m_uprobe_event_fd[j] = 0; + } + } + // clear uprobe bpf prog fd + for(j = 0; j < sizeof(handle->m_uprobe_prog_fds) / sizeof(handle->m_uprobe_prog_fds[0]); ++j) + { + if(handle->m_uprobe_prog_fds[j] > 0) + { + close(handle->m_uprobe_prog_fds[j]); + handle->m_uprobe_prog_fds[j] = 0; + } + } + // clear uprobe array idx map + for(j = 0; j < sizeof(handle->m_uprobe_array_idx_is_used) / sizeof(handle->m_uprobe_array_idx_is_used[0]); ++j) + { + if(handle->m_uprobe_array_idx_is_used[j] > 0) + { + close(handle->m_uprobe_array_idx_is_used[j]); + handle->m_uprobe_array_idx_is_used[j] = false; + } + } for(j = 0; j < sizeof(handle->m_bpf_map_fds) / sizeof(handle->m_bpf_map_fds[0]); ++j) { @@ -1319,6 +1507,7 @@ int32_t scap_bpf_close(scap_t *handle) } handle->m_bpf_prog_cnt = 0; + handle->m_uprobe_prog_cnt = 0; handle->m_bpf_prog_array_map_idx = -1; return SCAP_SUCCESS; @@ -1341,7 +1530,7 @@ static int32_t set_boot_time(scap_t *handle, uint64_t *boot_time) return SCAP_FAILURE; } - now = tv_now.tv_sec * (uint64_t) 1000000000 + tv_now.tv_usec * 1000; + now = tv_now.tv_sec * (uint64_t)1000000000 + tv_now.tv_usec * 1000; if(clock_gettime(CLOCK_BOOTTIME, &ts_uptime)) { @@ -1349,7 +1538,7 @@ static int32_t set_boot_time(scap_t *handle, uint64_t *boot_time) return SCAP_FAILURE; } - uptime = ts_uptime.tv_sec * (uint64_t) 1000000000 + ts_uptime.tv_nsec; + uptime = ts_uptime.tv_sec * (uint64_t)1000000000 + ts_uptime.tv_nsec; *boot_time = now - uptime; @@ -1409,7 +1598,7 @@ static int32_t set_runtime_params(scap_t *handle) // snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't open /proc/sys/net/core/bpf_jit_kallsyms"); // return SCAP_FAILURE; // compatibility for centos 7.6 - return SCAP_SUCCESS; + return SCAP_SUCCESS; } if(fprintf(f, "1") != 1) @@ -1456,8 +1645,9 @@ static int32_t set_default_settings(scap_t *handle) } memset(settings.if_name, 0, 16); int i = 0; - for (i = 0; i < PPM_EVENT_MAX; i++) { - settings.events_mask[i] = true; + for(i = 0; i < PPM_EVENT_MAX; i++) + { + settings.events_mask[i] = true; } int k = 0; @@ -1492,7 +1682,7 @@ int32_t scap_bpf_load(scap_t *handle, const char *bpf_probe) return SCAP_FAILURE; } - if(load_bpf_file(handle, bpf_probe) != SCAP_SUCCESS) + if(load_bpf_file(handle, bpf_probe, false, NULL) != SCAP_SUCCESS) { return SCAP_FAILURE; } @@ -1566,7 +1756,7 @@ int32_t scap_bpf_load(scap_t *handle, const char *bpf_probe) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "processors online: %d, expected: %d", online_cpu, handle->m_ndevs); return SCAP_FAILURE; } - + // open pmc for bpf perf event pmu_fd = sys_perf_event_open(&attr, -1, j, -1, 0); if(pmu_fd < 0) { @@ -1615,7 +1805,7 @@ int32_t scap_bpf_load(scap_t *handle, const char *bpf_probe) #endif // MINIMAL_BUILD } -int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats) +int32_t scap_bpf_get_stats(scap_t *handle, OUT scap_stats *stats) { int j; @@ -1641,7 +1831,7 @@ int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats) return SCAP_SUCCESS; } -int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret) +int32_t scap_bpf_get_n_tracepoint_hit(scap_t *handle, long *ret) { int j; @@ -1704,9 +1894,9 @@ int32_t scap_bpf_disable_skb_capture(scap_t *handle) return SCAP_SUCCESS; } -int32_t scap_bpf_handle_eventmask(scap_t* handle, uint32_t op, uint32_t event_id) +int32_t scap_bpf_handle_eventmask(scap_t *handle, uint32_t op, uint32_t event_id) { - if (event_id >= PPM_EVENT_MAX || event_id < 0) + if(event_id >= PPM_EVENT_MAX || event_id < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "illegal event type"); return SCAP_FAILURE; diff --git a/userspace/libscap/scap_bpf.h b/userspace/libscap/scap_bpf.h index af2e248bf4..fa581d35b6 100644 --- a/userspace/libscap/scap_bpf.h +++ b/userspace/libscap/scap_bpf.h @@ -49,6 +49,7 @@ int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret); int32_t scap_bpf_enable_skb_capture(scap_t *handle, const char *ifname); int32_t scap_bpf_disable_skb_capture(scap_t *handle); int32_t scap_bpf_handle_eventmask(scap_t* handle, uint32_t op, uint32_t event_id); +bool __load_uprobe(scap_t *handle, const char *path, bool is_uprobe, const char *user_probe_path); static inline scap_evt *scap_bpf_evt_from_perf_sample(void *evt) { diff --git a/userspace/libscap/scap_func_symbol.c b/userspace/libscap/scap_func_symbol.c new file mode 100644 index 0000000000..1603ebf2d1 --- /dev/null +++ b/userspace/libscap/scap_func_symbol.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2016 GitHub, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "scap_func_symbol.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// Constants are the integer part of the sines of integers (in radians) * 2^32. +static const uint32_t k[64] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee , + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 , + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be , + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 , + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa , + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 , + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed , + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a , + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c , + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 , + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 , + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 , + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 , + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; + +// r specifies the per-round shift amounts +static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +// leftrotate function definition +#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) + +#define ELF_ST_TYPE(x) (((uint32_t) x) & 0xf) + +static int _find_sym(const char *symbol_name, uint64_t addr, void *payload) { + struct symbol *sym = (struct symbol *)payload; + if (!strcmp(sym->name, symbol_name)) { + sym->offset = addr; + return -1; + } + return 0; +} + +static int _find_load(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset, + void *payload) { + struct load_addr_t *addr = (struct load_addr_t *)(payload); + if (addr->target_addr >= v_addr && addr->target_addr < (v_addr + mem_sz)) { + addr->binary_addr = addr->target_addr - v_addr + file_offset; + return -1; + } + return 0; +} + +// The CRC algorithm used by GNU debuglink. Taken from: +// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html +static unsigned int gnu_debuglink_crc32(unsigned int crc, + char *buf, size_t len) { + static const unsigned int crc32_table[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; + char *end; + + crc = ~crc & 0xffffffff; + for (end = buf + len; buf < end; ++buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc & 0xffffffff; +} + +static int verify_checksum(const char *file, unsigned int crc) { + struct stat st; + int fd; + void *buf; + unsigned int actual; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + + if (fstat(fd, &st) < 0) { + close(fd); + return 0; + } + + buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (!buf) { + close(fd); + return 0; + } + + actual = gnu_debuglink_crc32(0, buf, st.st_size); + + munmap(buf, st.st_size); + close(fd); + return actual == crc; +} + +static Elf_Scn * get_section(Elf *e, const char *section_name, + GElf_Shdr *section_hdr, size_t *section_idx) { + Elf_Scn *section = NULL; + GElf_Shdr header; + char *name; + + size_t stridx; + if (elf_getshdrstrndx(e, &stridx) != 0) + return NULL; + + size_t index; + for (index = 1; (section = elf_nextscn(e, section)) != 0; index++) { + if (!gelf_getshdr(section, &header)) + continue; + + name = elf_strptr(e, stridx, header.sh_name); + if (name && !strcmp(name, section_name)) { + if (section_hdr) + *section_hdr = header; + if (section_idx) + *section_idx = index; + return section; + } + } + + return NULL; +} + +static Elf_Data * get_section_elf_data(Elf *e, const char *section_name) { + Elf_Scn *section = get_section(e, section_name, NULL, NULL); + if (section) + return elf_getdata(section, NULL); + return NULL; +} + +static int find_debuglink(Elf *e, char **debug_file, unsigned int *crc) { + Elf_Data *data = NULL; + + *debug_file = NULL; + *crc = 0; + + data = get_section_elf_data(e, ".gnu_debuglink"); + if (!data || data->d_size <= 5) + return 0; + + *debug_file = (char *)data->d_buf; + *crc = *(unsigned int*)((char *)data->d_buf + data->d_size - 4); + + return *debug_file ? 1 : 0; +} + +// Check if two filenames point to the same file, including hard or soft links. +static bool same_file(char *a, const char *b) +{ + struct stat stat_a, stat_b; + + if (stat(a, &stat_a) || stat(b, &stat_b)) + return false; + + if ((stat_a.st_dev == stat_b.st_dev) && + (stat_a.st_ino == stat_b.st_ino)) + return true; + else + return false; +} + +static char *find_debug_via_debuglink(Elf *e, const char *binpath, + int check_crc) { + char fullpath[PATH_MAX]; + char *tmppath; + char *bindir = NULL; + char *res = NULL; + unsigned int crc; + char *name; // the name of the debuginfo file + + if (!find_debuglink(e, &name, &crc)) + return NULL; + + tmppath = strdup(binpath); + bindir = dirname(tmppath); + + // Search for the file in 'binpath', but ignore the file we find if it + // matches the binary itself: the binary will always be probed later on, + // and it might contain poorer symbols (e.g. stripped or partial symbols) + // than the external debuginfo that might be available elsewhere. + snprintf(fullpath, sizeof(fullpath),"%s/%s", bindir, name); + if (same_file(fullpath, binpath) != true && access(fullpath, F_OK) != -1) { + res = strdup(fullpath); + goto DONE; + } + + // Search for the file in 'binpath'/.debug + snprintf(fullpath, sizeof(fullpath), "%s/.debug/%s", bindir, name); + if (access(fullpath, F_OK) != -1) { + res = strdup(fullpath); + goto DONE; + } + + // Search for the file in the global debug directory /usr/lib/debug/'binpath' + snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug%s/%s", bindir, name); + if (access(fullpath, F_OK) != -1) { + res = strdup(fullpath); + goto DONE; + } + +DONE: + free(tmppath); + if (res && check_crc && !verify_checksum(res, crc)) { + free(res); + return NULL; + } + return res; +} + +static int find_buildid(Elf *e, char *buildid) { + Elf_Data *data = get_section_elf_data(e, ".note.gnu.build-id"); + if (!data || data->d_size <= 16 || strcmp((char *)data->d_buf + 12, "GNU")) + return 0; + + char *buf = (char *)data->d_buf + 16; + size_t length = data->d_size - 16; + size_t i = 0; + for (i = 0; i < length; ++i) { + sprintf(buildid + (i * 2), "%02hhx", buf[i]); + } + + return 1; +} + +static char *find_debug_via_buildid(Elf *e) { + char fullpath[PATH_MAX]; + char buildid[128]; // currently 40 seems to be default, let's be safe + + if (!find_buildid(e, buildid)) + return NULL; + + // Search for the file in the global debug directory with a sub-path: + // mm/nnnnnn...nnnn.debug + // Where mm are the first two characters of the buildid, and nnnn are the + // rest of the build id, followed by .debug. + snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug/.build-id/%c%c/%s.debug", + buildid[0], buildid[1], buildid + 2); + if (access(fullpath, F_OK) != -1) { + return strdup(fullpath); + } + + return NULL; +} + +static int openelf_fd(int fd, Elf **elf_out) { + if (elf_version(EV_CURRENT) == EV_NONE) + return -1; + + *elf_out = elf_begin(fd, ELF_C_READ, 0); + if (*elf_out == NULL) + return -1; + + return 0; +} + + +static int openelf(const char *path, Elf **elf_out, int *fd_out) { + *fd_out = open(path, O_RDONLY); + if (*fd_out < 0) + return -1; + + if (openelf_fd(*fd_out, elf_out) == -1) { + close(*fd_out); + return -1; + } + + return 0; +} + +static char *find_debug_via_symfs(Elf *e, const char* path) { + char fullpath[PATH_MAX]; + char buildid[128]; + char symfs_buildid[128]; + int check_build_id; + char *symfs; + Elf *symfs_e = NULL; + int symfs_fd = -1; + char *result = NULL; + + symfs = getenv("BCC_SYMFS"); + if (!symfs || !*symfs) + goto out; + + check_build_id = find_buildid(e, buildid); + + int ns_prefix_length = 0; + sscanf(path, "/proc/%*u/root/%n", &ns_prefix_length); + path += ns_prefix_length; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", symfs, path); + if (access(fullpath, F_OK) == -1) + goto out; + + if (openelf(fullpath, &symfs_e, &symfs_fd) < 0) { + symfs_e = NULL; + symfs_fd = -1; + goto out; + } + + if (check_build_id) { + if (!find_buildid(symfs_e, symfs_buildid)) + goto out; + + if (strncmp(buildid, symfs_buildid, sizeof(buildid))) + goto out; + } + + result = strdup(fullpath); + +out: + if (symfs_e) { + elf_end(symfs_e); + } + + if (symfs_fd != -1) { + close(symfs_fd); + } + + return result; +} + +static char *find_debug_file(Elf* e, const char* path, int check_crc) { + char *debug_file = NULL; + + // If there is a separate debuginfo file, try to locate and read it, first + // using symfs, then using the build-id section, finally using the debuglink + // section. These rules are what perf and gdb follow. + // See: + // - https://github.com/torvalds/linux/blob/v5.2/tools/perf/Documentation/perf-report.txt#L325 + // - https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + debug_file = find_debug_via_symfs(e, path); + if (!debug_file) + debug_file = find_debug_via_buildid(e); + if (!debug_file) + debug_file = find_debug_via_debuglink(e, path, check_crc); + + return debug_file; +} + +static int list_in_scn(Elf *e, Elf_Scn *section, size_t stridx, size_t symsize, + struct symbol_option *option, elf_symcb callback, + void *payload, bool debugfile) { + Elf_Data *data = NULL; + + while ((data = elf_getdata(section, data)) != 0) { + size_t i, symcount = data->d_size / symsize; + + if (data->d_size % symsize) + return -1; + + for (i = 0; i < symcount; ++i) { + GElf_Sym sym; + const char *name; + + if (!gelf_getsym(data, (int)i, &sym)) + continue; + + if ((name = elf_strptr(e, stridx, sym.st_name)) == NULL) + continue; + if (name[0] == 0) + continue; + + if (sym.st_value == 0) + continue; + + uint32_t st_type = ELF_ST_TYPE(sym.st_info); + if (!(option->use_symbol_type & (1 << st_type))) + continue; + + int ret; + ret = callback(name, sym.st_value, payload); + if (ret < 0) + return 1; // signal termination to caller + } + } + + return 0; +} + +static int listsymbols(Elf *e, elf_symcb callback, void *payload, + struct symbol_option *option, bool debugfile) { + Elf_Scn *section = NULL; + + while ((section = elf_nextscn(e, section)) != 0) { + GElf_Shdr header; + + if (!gelf_getshdr(section, &header)) + continue; + + if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM) + continue; + + int rc = list_in_scn(e, section, header.sh_link, header.sh_entsize, + option, callback, payload, debugfile); + if (rc == 1) + break; // callback signaled termination + + if (rc < 0) + return rc; + } + + return 0; +} + +static int foreach_sym_core(const char *path, elf_symcb callback, + struct symbol_option *option, void *payload, + int is_debug_file) { + Elf *e; + int fd, res; + char *debug_file; + + if (!option) + return -1; + + if (openelf(path, &e, &fd) < 0) + return -1; + + if (option->use_debug_file && !is_debug_file) { + // The is_debug_file argument helps avoid infinitely resolving debuginfo + // files for debuginfo files and so on. + debug_file = find_debug_file(e, path, + option->check_debug_file_crc); + if (debug_file) { + foreach_sym_core(debug_file, callback, option, payload, 1); + free(debug_file); + } + } + + res = listsymbols(e, callback, payload, option, is_debug_file); + elf_end(e); + close(fd); + return res; +} + +int bcc_resolve_symname(const char *module, const char *symbol_name, uint64_t *res_addr) { + int module_type; + + static struct symbol_option default_option = { + .use_debug_file = 1, + .check_debug_file_crc = 1, + .use_symbol_type = 65535, + }; + + if (module == NULL || symbol_name == NULL) + return -1; + + struct symbol sym; + memset(&sym, 0, sizeof(struct symbol)); + + sym.module = strdup(module); + sym.name = symbol_name; + + if (bcc_elf_foreach_sym(module, _find_sym, &default_option, &sym) < 0) + return -1; + if (sym.offset == 0x0) + return -1; + + // For executable (ET_EXEC) binaries and shared objects (ET_DYN), translate + // the virtual address to physical address in the binary file. + module_type = bcc_elf_get_type(sym.module); + if (module_type == ET_EXEC || module_type == ET_DYN) { + struct load_addr_t addr = { + .target_addr = sym.offset, + .binary_addr = 0x0, + }; + if (bcc_elf_foreach_load_section(sym.module, &_find_load, &addr) < 0) + return -1; + if (!addr.binary_addr) + return -1; + *res_addr = addr.binary_addr; + } + return 0; +} + +int bcc_elf_foreach_sym(const char *path, elf_symcb callback, + void *option, void *payload) { + struct symbol_option *o = option; + return foreach_sym_core(path, callback, o, payload, 0); +} + +int bcc_elf_get_type(const char *path) { + Elf *e; + GElf_Ehdr hdr; + int fd; + void* res = NULL; + + if (openelf(path, &e, &fd) < 0) + return -1; + + res = (void*)gelf_getehdr(e, &hdr); + elf_end(e); + close(fd); + + if (!res) + return -1; + else + return hdr.e_type; +} + +int bcc_elf_foreach_load_section(const char *path, + elf_load_sectioncb callback, + void *payload) { + Elf *e = NULL; + int fd = -1, err = -1, res; + size_t nhdrs, i; + + if (openelf(path, &e, &fd) < 0) + goto exit; + + if (elf_getphdrnum(e, &nhdrs) != 0) + goto exit; + + GElf_Phdr header; + for (i = 0; i < nhdrs; i++) { + if (!gelf_getphdr(e, (int)i, &header)) + continue; + if (header.p_type != PT_LOAD || !(header.p_flags & PF_X)) + continue; + res = callback(header.p_vaddr, header.p_memsz, header.p_offset, payload); + if (res < 0) { + err = 1; + goto exit; + } + } + err = 0; + +exit: + if (e) + elf_end(e); + if (fd >= 0) + close(fd); + return err; +} + +void to_bytes(uint32_t val, uint8_t *bytes) +{ + bytes[0] = (uint8_t) val; + bytes[1] = (uint8_t) (val >> 8); + bytes[2] = (uint8_t) (val >> 16); + bytes[3] = (uint8_t) (val >> 24); +} + +uint32_t to_int32(const uint8_t *bytes) +{ + return (uint32_t) bytes[0] + | ((uint32_t) bytes[1] << 8) + | ((uint32_t) bytes[2] << 16) + | ((uint32_t) bytes[3] << 24); +} + +void encode(const uint8_t *initial_msg, size_t initial_len, uint8_t *digest) { + + // These vars will contain the hash + uint32_t h0, h1, h2, h3; + + // Message (to prepare) + uint8_t *msg = NULL; + + size_t new_len, offset; + uint32_t w[16]; + uint32_t a, b, c, d, i, f, g, temp; + + // Initialize variables - simple count in nibbles: + h0 = 0x67452301; + h1 = 0xefcdab89; + h2 = 0x98badcfe; + h3 = 0x10325476; + + //Pre-processing: + //append "1" bit to message + //append "0" bits until message length in bits ≡ 448 (mod 512) + //append length mod (2^64) to message + + for (new_len = initial_len + 1; new_len % (512/8) != 448/8; new_len++) + ; + + msg = (uint8_t*)malloc(new_len + 8); + memcpy(msg, initial_msg, initial_len); + msg[initial_len] = 0x80; // append the "1" bit; most significant bit is "first" + for (offset = initial_len + 1; offset < new_len; offset++) + msg[offset] = 0; // append "0" bits + + // append the len in bits at the end of the buffer. + to_bytes(initial_len*8, msg + new_len); + // initial_len>>29 == initial_len*8>>32, but avoids overflow. + to_bytes(initial_len>>29, msg + new_len + 4); + + // Process the message in successive 512-bit chunks: + //for each 512-bit chunk of message: + for(offset=0; offset +#include +#include +#include +#include + +struct symbol { + const char *name; + const char *module; + uint64_t offset; +}; + +struct symbol_option { + int use_debug_file; + int check_debug_file_crc; + // Bitmask flags indicating what types of ELF symbols to use + uint32_t use_symbol_type; +}; + +struct load_addr_t { + uint64_t target_addr; + uint64_t binary_addr; +}; + +// Symbol name, start address, length, payload +// Callback returning a negative value indicates to stop the iteration +typedef int (*elf_symcb)(const char *, uint64_t, void *); + +// Segment virtual address, memory size, file offset, payload +// Callback returning a negative value indicates to stop the iteration +typedef int (*elf_load_sectioncb)(uint64_t, uint64_t, uint64_t, void *); + +int bcc_resolve_symname(const char *module, const char *symbol_name, uint64_t *res_addr); + +// Iterate over symbol table of a binary module +// Parameter "option" points to a symbol_option struct to indicate whether +// and how to use debuginfo file, and what types of symbols to load. +// Returns -1 on error, and 0 on success or stopped by callback +int bcc_elf_foreach_sym(const char *path, elf_symcb callback, void *option, void *payload); + +int bcc_elf_get_type(const char *path); + +// Iterate over all executable load sections of an ELF +// Returns -1 on error, 1 if stopped by callback, and 0 on success +int bcc_elf_foreach_load_section(const char *path, + elf_load_sectioncb callback, + void *payload); + +void to_bytes(uint32_t val, uint8_t *bytes); + +uint32_t to_int32(const uint8_t *bytes); + +void encode(const uint8_t *initial_msg, size_t initial_len, uint8_t *digest); + +char* generate_identifier(char *msg); +#endif // AGENT_LIBS_SCAP_FUNC_SYMBOL_H diff --git a/userspace/libscap/scap_procs.c b/userspace/libscap/scap_procs.c index d7cd835f0f..d495919615 100644 --- a/userspace/libscap/scap_procs.c +++ b/userspace/libscap/scap_procs.c @@ -956,6 +956,16 @@ static int32_t _scap_proc_scan_proc_dir_impl(scap_t* handle, char* procdirname, uint64_t last_tid_processed = 0; struct scap_ns_socket_list* sockets_by_ns = NULL; + char *uprobe_mode = getenv("enable_uprobe"); + if (uprobe_mode != NULL && strncmp("true", uprobe_mode, sizeof(uprobe_mode)) == 0) + { + handle->enable_uprobe = true; + } + else + { + handle->enable_uprobe = false; + } + dir_p = opendir(procdirname); if(dir_p == NULL) diff --git a/userspace/libscap/settings.h b/userspace/libscap/settings.h index c12824b4c8..0d5bdc44bf 100644 --- a/userspace/libscap/settings.h +++ b/userspace/libscap/settings.h @@ -24,4 +24,4 @@ limitations under the License. #define USE_ZLIB #endif // MINIMAL_BUILD -#define SCAP_NODRIVER_MAX_FD_LOOKUP 20 +#define SCAP_NODRIVER_MAX_FD_LOOKUP 20 \ No newline at end of file diff --git a/userspace/libsinsp/examples/test.cpp b/userspace/libsinsp/examples/test.cpp index 6f380400a9..122aaf9b19 100644 --- a/userspace/libsinsp/examples/test.cpp +++ b/userspace/libsinsp/examples/test.cpp @@ -82,7 +82,6 @@ int main(int argc, char **argv) signal(SIGPIPE, sigint_handler); inspector.open(); - if(!filter_string.empty()) { try @@ -122,7 +121,7 @@ int main(int argc, char **argv) sinsp_utils::ts_to_iso_8601(ev->get_ts(), &date_time); bool is_host_proc = thread->m_container_id.empty(); - cout << "[" << date_time << "]:[" + cout << "[" << date_time << "]:[" << (is_host_proc ? "HOST" : thread->m_container_id) << "]:"; cout << "[CAT="; diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index 9df895de78..40f3b54a9e 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -19,6 +19,7 @@ limitations under the License. #define NOMINMAX #include #else +#include #include #include #include @@ -38,6 +39,11 @@ limitations under the License. #include "filter.h" #include "filterchecks.h" #include "protodecoder.h" +using namespace std; +#define NONE "\033[m" +#define RED "\033[0;32;31m" +#define GREEN "\033[0;32;32m" + #ifdef SIMULATE_DROP_MODE bool should_drop(sinsp_evt *evt); #endif @@ -586,6 +592,7 @@ bool sinsp_parser::reset(sinsp_evt *evt) // If we're exiting a clone or if we have a scheduler event // (many kernel thread), we don't look for /proc // + bool query_os; if(etype == PPME_SYSCALL_CLONE_11_X || etype == PPME_SYSCALL_CLONE_16_X || @@ -767,7 +774,6 @@ bool sinsp_parser::reset(sinsp_evt *evt) } } } - return true; } diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index 120b9c5384..5d30c56cd2 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -874,6 +874,7 @@ void sinsp::on_new_entry_from_proc(void* context, { thread_added = m_thread_manager->add_thread(newti, true); } + if (!thread_added) { delete newti; } @@ -886,7 +887,6 @@ void sinsp::on_new_entry_from_proc(void* context, { sinsp_threadinfo* newti = build_threadinfo(); newti->init(tinfo); - if (!m_thread_manager->add_thread(newti, true)) { ASSERT(false); delete newti; diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 6c2edddfa8..85f8f26ca2 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -14,6 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include +#include +#include +#include +#include +#include #ifndef _WIN32 #define __STDC_FORMAT_MACROS @@ -22,7 +28,13 @@ limitations under the License. #endif #include #include +#include +#include +#include +#include #include "sinsp.h" +#include "scap.h" +#include "scap-int.h" #include "sinsp_int.h" #include "protodecoder.h" #include "tracers.h" @@ -31,7 +43,19 @@ limitations under the License. #include "tracer_emitter.h" #endif +// agent-libs running in container in production environment +// the elf path differs in host and container +#define NONE "\033[m" +#define GREEN "\033[0;32;32m" extern sinsp_evttables g_infotables; +static const char *bpf_probe; +// 1 running on host, 2 running in container +static int running_mode; + +unordered_map inodemap; +unordered_map inode_to_prog_idx; +// tranform container path to host path +unordered_map hostpath; static void copy_ipv6_address(uint32_t* dest, uint32_t* src) { @@ -1256,6 +1280,144 @@ void sinsp_thread_manager::increment_mainthread_childcount(sinsp_threadinfo* thr } } +// void get_container_path(char *container_path, char *container_id){ +// // TODO: may have better solution +// static char command[100] = "docker inspect --format=\'{{.GraphDriver.Data.MergedDir}}\' "; +// // len{ "docker inspect --format=\'{{.GraphDriver.Data.MergedDir}}\' " } = 58 +// command[58] = '\0'; +// strcat(command, container_id); + +// FILE *pp = popen(command, "r"); // build pipe +// if (!pp) +// return; + +// // collect cmd execute result +// while (fgets(container_path, 1024, pp) != NULL){} + +// pclose(pp); +// container_path[strlen(container_path) - 1] = '\0'; +// } + +void get_container_path(char* container_path, int pid) { + // 生成/proc/${pid}/mountinfo文件路径 + std::stringstream ss; + ss << "/host/proc/" << pid << "/mountinfo"; + std::string mountinfo_file = ss.str(); + + // 读取文件 + std::ifstream fin(mountinfo_file.c_str()); + if (!fin.is_open()) { + // std::cerr << "Failed to open file: " << mountinfo_file << std::endl; + container_path[0] = '\0'; + return; + } + + // 提取container_path + std::string line; + while (std::getline(fin, line)) { + std::istringstream iss(line); + std::vector tokens; + std::string token; + while (std::getline(iss, token, ',')) { + tokens.push_back(token); + } + for (int i = 0; i < tokens.size(); ++i) { + std::size_t pos = tokens[i].find("upperdir="); + if (pos != std::string::npos) { + std::string value = tokens[i].substr(pos + 9); + value.replace(value.size() - 4, 4, "merged"); + strncpy(container_path, value.c_str(), 1024 - 1); + return; + } + } + } + + // 未找到upperdir + // std::cerr << "Failed to find upperdir" << std::endl; + container_path[0] = '\0'; +} + +void to_host_path(char* target_file_path, sinsp_threadinfo *threadinfo, char* file_path_from_proc){ + static char container_path[1024]; + static char container_id[20]; + + if(!threadinfo->m_container_id.empty()) + { + /* + cout << "container_id: " << container_id << endl; + cout << "thread: " << threadinfo->m_pid << ' ' << threadinfo->m_tid << ' ' + << threadinfo->get_comm() << ' ' + << threadinfo->get_cwd() << ' ' << threadinfo->get_exepath() << ' ' + << threadinfo->get_exe() + << endl; + */ + + get_container_path(container_path, threadinfo->m_pid); + strcat(target_file_path, container_path); + } + strcat(target_file_path, file_path_from_proc); +} + +static void handle_uprobe(scap_t* handle, sinsp_threadinfo *threadinfo){ + if(handle->enable_uprobe == false) + { + return; + } + + if(!bpf_probe) + { + bpf_probe = scap_get_bpf_probe_from_env(); + } + + if(threadinfo->get_exepath().empty()) + { + return; + } + + // /host popen(/var/lib/..../merged) file_path(/home/a.out) + static char proc_path[20] = {0}; + static char file_path_from_proc[2014] = {0}; + static char target_file_path[1024] = {0}; + struct stat file; + + sprintf(proc_path, "/host/proc/%ld/exe", threadinfo->m_pid); + + static long buf_len; + if((buf_len = readlink(proc_path, file_path_from_proc,1024)) <=0) + { + return; + } + file_path_from_proc[buf_len] = '\0'; + + if(strlen(file_path_from_proc) == 0) + { + return; + } + + // runing in container + target_file_path[0] = '/', target_file_path[1] = 'h', target_file_path[2] = 'o', + target_file_path[3] = 's', target_file_path[4] = 't', target_file_path[5] = '\0'; + + + to_host_path(target_file_path, threadinfo, file_path_from_proc); + + if(stat(target_file_path, &file) == -1) + { + // cout << "[add_thread: handle_uprobe] stat error file_path: " << target_file_path << endl; + return; + } + if(inodemap[file.st_ino] == 0) + { + //TODO: if handle_uprobe return false, the file does not have any our hook func, can be marked as -1 + if(load_uprobe(handle, bpf_probe, true, target_file_path)) + { + inode_to_prog_idx[file.st_ino] = handle->m_uprobe_prog_cnt; + // cout << target_file_path << " [inode]" << file.st_ino << " -> [prog_idx]" << handle->m_uprobe_prog_cnt << endl; + } + } + inodemap[file.st_ino]++; +} + bool sinsp_thread_manager::add_thread(sinsp_threadinfo *threadinfo, bool from_scap_proctable) { #ifdef GATHER_INTERNAL_STATS @@ -1289,13 +1451,64 @@ bool sinsp_thread_manager::add_thread(sinsp_threadinfo *threadinfo, bool from_sc threadinfo->allocate_private_state(); m_threadtable.put(threadinfo); - return true; + if(threadinfo->is_main_thread()) + { + handle_uprobe(m_inspector->m_h, threadinfo); + } + return true; } void sinsp_thread_manager::remove_thread(int64_t tid, bool force) { uint64_t nchilds; sinsp_threadinfo* tinfo = m_threadtable.get(tid); + static struct stat file; + + /* + if(tinfo != nullptr && tinfo->is_main_thread()) + { + static char target_file_path[1024] = {0}; + + if(running_mode == 0) + { + char *mode = getenv("HOST_MODE"); + if (mode != nullptr && strncmp("true", mode, sizeof(mode)) == 0) + { + running_mode = 1; + } + else + running_mode = 2; + } + + if(running_mode == 1) + { + // runing in host mode; + target_file_path[0] = '\0'; + } + else + { + // runing in container + target_file_path[0] = '/', target_file_path[1] = 'h', target_file_path[2] = 'o', + target_file_path[3] = 's', target_file_path[4] = 't', target_file_path[5] = '\0'; + } + + to_host_path(target_file_path, tinfo, (char*)tinfo->get_exepath().c_str()); + + if(stat(target_file_path, &file) == -1){} + else if(inodemap[file.st_ino] > 0) + { + // TODO + // inodemap[file.st_ino]--; + if(inodemap[file.st_ino] == 0 && inode_to_prog_idx[file.st_ino] != 0) + { + // cout << GREEN << "inodemap(remove): " << target_file_path << " [inode]" << file.st_ino << " [prog_idx]"<< inode_to_prog_idx[file.st_ino] << NONE << endl; + m_inspector->m_h->m_uprobe_array_idx_is_used[inode_to_prog_idx[file.st_ino]] = false; + close(m_inspector->m_h->m_uprobe_event_fd[inode_to_prog_idx[file.st_ino]]); + close(m_inspector->m_h->m_uprobe_prog_fds[inode_to_prog_idx[file.st_ino]]); + } + } + } + */ if(tinfo == nullptr) { @@ -1374,9 +1587,7 @@ void sinsp_thread_manager::remove_thread(int64_t tid, bool force) #ifdef GATHER_INTERNAL_STATS m_removed_threads->increment(); #endif - m_threadtable.erase(tid); - // // If the thread has a nonzero refcount, it means that we are forcing the removal // of a main process or program that some child refer to. @@ -1388,6 +1599,7 @@ void sinsp_thread_manager::remove_thread(int64_t tid, bool force) recreate_child_dependencies(); } } + } void sinsp_thread_manager::fix_sockets_coming_from_proc()