From a73268fa48d702c0c3f2ff21d9545523472ee3a2 Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Thu, 21 Nov 2024 14:25:41 +0100 Subject: [PATCH 1/2] new(scap): create some helpers method for the converter Signed-off-by: Andrea Terzolo --- .../test_suites/userspace/scap_event.cpp | 55 ++++ userspace/libscap/CMakeLists.txt | 1 + .../libscap/examples/01-open/scap_open.c | 236 +------------- userspace/libscap/scap.h | 20 ++ userspace/libscap/scap_event.c | 194 ++++++++++++ userspace/libscap/scap_print_event.c | 287 ++++++++++++++++++ 6 files changed, 558 insertions(+), 235 deletions(-) create mode 100644 userspace/libscap/scap_print_event.c diff --git a/test/libscap/test_suites/userspace/scap_event.cpp b/test/libscap/test_suites/userspace/scap_event.cpp index 863ff357a2..d0d1a9f33d 100644 --- a/test/libscap/test_suites/userspace/scap_event.cpp +++ b/test/libscap/test_suites/userspace/scap_event.cpp @@ -167,3 +167,58 @@ TEST(scap_event, empty_buffers) { EXPECT_EQ(decoded_params[0].size, sizeof(uint64_t)); EXPECT_EQ(decoded_params[1].size, 0); } + +TEST(scap_event, test_scap_create_event) { + char error[SCAP_LASTERR_SIZE]; + uint64_t ts = 12; + int64_t tid = 25; + int64_t fd = 6; + const char *name = "/etc/passwd"; + uint32_t flags = 0; + uint32_t mode = 37; + uint32_t dev = 0; + uint64_t ino = 0; + scap_evt *evt = scap_create_event(error, + ts, + tid, + PPME_SYSCALL_OPEN_X, + 6, + fd, + name, + flags, + mode, + dev, + ino); + if(evt == NULL) { + FAIL() << "Error creating event: " << error; + } + uint16_t total_evt_len = 78; + // Assert header + ASSERT_EQ(evt->ts, ts); + ASSERT_EQ(evt->tid, tid); + ASSERT_EQ(evt->type, PPME_SYSCALL_OPEN_X); + ASSERT_EQ(evt->len, total_evt_len); + ASSERT_EQ(evt->nparams, 6); + // Assert len array + uint16_t *lens16 = (uint16_t *)((char *)evt + sizeof(scap_evt)); + ASSERT_EQ(lens16[0], 8); + ASSERT_EQ(lens16[1], 12); + ASSERT_EQ(lens16[2], 4); + ASSERT_EQ(lens16[3], 4); + ASSERT_EQ(lens16[4], 4); + ASSERT_EQ(lens16[5], 8); + // Assert parameters + char *val = ((char *)evt + sizeof(scap_evt) + 6 * sizeof(uint16_t)); + ASSERT_EQ(*(int32_t *)val, fd); + val += 8; + ASSERT_STREQ(val, name); + val += 12; + ASSERT_EQ(*(uint32_t *)val, flags); + val += 4; + ASSERT_EQ(*(uint32_t *)val, mode); + val += 4; + ASSERT_EQ(*(uint32_t *)val, dev); + val += 4; + ASSERT_EQ(*(uint64_t *)val, ino); + free(evt); +} diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index 466a24864a..f7057f83c4 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -94,6 +94,7 @@ add_library( scap_event_schema STATIC scap_event.c ppm_sc_names.c + scap_print_event.c ${LIBS_DIR}/driver/dynamic_params_table.c ${LIBS_DIR}/driver/event_table.c ${LIBS_DIR}/driver/flags_table.c diff --git a/userspace/libscap/examples/01-open/scap_open.c b/userspace/libscap/examples/01-open/scap_open.c index 9794606ebd..0024747bfa 100644 --- a/userspace/libscap/examples/01-open/scap_open.c +++ b/userspace/libscap/examples/01-open/scap_open.c @@ -21,7 +21,6 @@ limitations under the License. #include #include #include -#include #include #include #include @@ -159,10 +158,6 @@ static const struct scap_vtable* vtable = NULL; static uint64_t g_nevts = 0; /* total number of events captured. */ static uint64_t g_total_number_of_bytes = 0; /* total dimension of events in bytes. */ static scap_t* g_h = NULL; /* global scap handler. */ -static uint16_t* lens16 = NULL; /* pointer used to print the length of event params. */ -static char* valptr = NULL; -/* pointer used to print the value of event params. */ /* pointer used to print the value of - event params. */ static struct timeval tval_start, tval_end, tval_result; static unsigned long number_of_timeouts; /* Times in which there were no events in the buffer. */ static unsigned long number_of_scap_next; /* Times in which the 'scap-next' method is called. */ @@ -266,235 +261,6 @@ void enable_simple_set() { /*=============================== SYSCALLS/TRACEPOINTS ===========================*/ -/*=============================== PRINT EVENT PARAMS ===========================*/ - -void print_ipv4(int starting_index) { - char ipv4_string[50]; - uint8_t* ipv4 = (uint8_t*)(valptr + starting_index); - snprintf(ipv4_string, sizeof(ipv4_string), "%d.%d.%d.%d", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); - printf("- ipv4: %s\n", ipv4_string); -} - -void print_ipv6(int starting_index) { - uint32_t ipv6[4] = {0, 0, 0, 0}; - ipv6[0] = *(uint32_t*)(valptr + starting_index); - ipv6[1] = *(uint32_t*)(valptr + starting_index + 4); - ipv6[2] = *(uint32_t*)(valptr + starting_index + 8); - ipv6[3] = *(uint32_t*)(valptr + starting_index + 12); - - char ipv6_string[150]; - inet_ntop(AF_INET6, ipv6, ipv6_string, 150); - printf("- ipv6: %s\n", ipv6_string); -} - -void print_unix_path(int starting_index) { - printf("- unix path: %s\n", (char*)(valptr + starting_index)); -} - -void print_port(int starting_index) { - printf("- port: %d\n", *(uint16_t*)(valptr + starting_index)); -} - -void print_parameter(int16_t num_param) { - int16_t param_type = g_event_info[evt_type].params[num_param].type; - int16_t len = lens16[num_param]; - - if(len == 0) { - printf("PARAM %d: is empty\n", num_param); - return; - } - - switch(param_type) { - case PT_FLAGS8: - printf("PARAM %d: %X\n", num_param, *(uint8_t*)(valptr)); - break; - - case PT_FLAGS16: - printf("PARAM %d: %X\n", num_param, *(uint16_t*)(valptr)); - break; - - case PT_FLAGS32: - printf("PARAM %d: %X\n", num_param, *(uint32_t*)(valptr)); - break; - - case PT_INT8: - printf("PARAM %d: %d\n", num_param, *(int8_t*)(valptr)); - break; - - case PT_INT16: - printf("PARAM %d: %d\n", num_param, *(int16_t*)(valptr)); - break; - - case PT_INT32: - printf("PARAM %d: %d\n", num_param, *(int32_t*)(valptr)); - break; - - case PT_INT64: - case PT_ERRNO: - case PT_PID: - printf("PARAM %d: %ld\n", num_param, *(int64_t*)(valptr)); - break; - - case PT_UINT8: - case PT_SIGTYPE: - case PT_ENUMFLAGS8: - printf("PARAM %d: %d\n", num_param, *(uint8_t*)(valptr)); - break; - - case PT_UINT16: - case PT_SYSCALLID: - case PT_ENUMFLAGS16: - printf("PARAM %d: %d\n", num_param, *(uint16_t*)(valptr)); - break; - - case PT_UINT32: - case PT_UID: - case PT_GID: - case PT_SIGSET: - case PT_MODE: - case PT_ENUMFLAGS32: - printf("PARAM %d: %d\n", num_param, *(uint32_t*)(valptr)); - break; - - case PT_UINT64: - case PT_RELTIME: - case PT_ABSTIME: - printf("PARAM %d: %lu\n", num_param, *(uint64_t*)(valptr)); - break; - - case PT_FD: - printf("PARAM %d: %d\n", num_param, *(int32_t*)(valptr)); - break; - - case PT_SOCKADDR: { - printf("PARAM %d:\n", num_param); - uint8_t sock_family = *(uint8_t*)(valptr); - printf("- sock_family: %d\n", sock_family); - switch(sock_family) { - case PPM_AF_INET: - /* ipv4 dest. */ - print_ipv4(1); - - /* port dest. */ - print_port(5); - break; - - case PPM_AF_INET6: - /* ipv6 dest. */ - print_ipv6(1); - - /* port dest. */ - print_port(17); - break; - - case PPM_AF_UNIX: - /* unix_path. */ - print_unix_path(1); - break; - - default: - printf("- error\n"); - break; - } - break; - } - - case PT_SOCKTUPLE: { - printf("PARAM %d:\n", num_param); - uint8_t sock_family = *(uint8_t*)(valptr); - printf("- sock_family: %d\n", sock_family); - switch(sock_family) { - case PPM_AF_INET: - /* ipv4 src. */ - print_ipv4(1); - - /* ipv4 dest. */ - print_ipv4(5); - - /* port src. */ - print_port(9); - - /* port dest. */ - print_port(11); - break; - - case PPM_AF_INET6: - /* ipv6 src. */ - print_ipv6(1); - - /* ipv6 dest. */ - print_ipv6(17); - - /* port src. */ - print_port(33); - - /* port dest. */ - print_port(35); - break; - - case PPM_AF_UNIX: - /* Here there are also some kernel pointers but right - * now we are not interested in catching them. - * 8 + 8 = 16 bytes - */ - - /* unix_path. */ - print_unix_path(17); - break; - - default: - printf("- error\n"); - break; - } - break; - } - - case PT_CHARBUF: - case PT_BYTEBUF: - case PT_FSPATH: - case PT_CHARBUFARRAY: - case PT_FSRELPATH: - printf("PARAM %d: ", num_param); - for(int j = 0; j < len; j++) { - printf("%c", *(char*)(valptr + j)); - } - printf("\n"); - break; - - default: - printf("PARAM %d: TYPE NOT KNOWN\n", num_param); - break; - } - valptr += len; -} - -void print_event(scap_evt* ev) { - lens16 = (uint16_t*)((char*)ev + sizeof(struct ppm_evt_hdr)); - valptr = (char*)lens16 + ev->nparams * sizeof(uint16_t); - printf("\n------------------ EVENT: %d TID:%lu\n", evt_type, ev->tid); - - printf("------ HEADER\n"); - printf("timestamp: %lu\n", ev->ts); - printf("tid: %lu\n", ev->tid); - printf("len: %d\n", ev->len); - printf("type: %d\n", ev->type); - printf("num params: %d\n", ev->nparams); - printf("------\n"); - printf("------ PARAMS\n"); - - for(int i = 0; i < ev->nparams; i++) { - print_parameter(i); - } - if(ev->nparams == 0) { - printf("- This event has no parameter\n"); - } - - printf("------\n"); - printf("------------------\n"); -} - -/*=============================== PRINT EVENT PARAMS ===========================*/ - /*=============================== PRINT CAPTURE INFO ===========================*/ void print_help() { @@ -988,7 +754,7 @@ int main(int argc, char** argv) { } if(ev->type == evt_type) { - print_event(ev); + scap_print_event(ev, PRINT_FULL); } count_syscalls(ev); g_total_number_of_bytes += ev->len; diff --git a/userspace/libscap/scap.h b/userspace/libscap/scap.h index 119e209290..4927c5d123 100644 --- a/userspace/libscap/scap.h +++ b/userspace/libscap/scap.h @@ -856,6 +856,26 @@ int32_t scap_event_encode_params_v(struct scap_sized_buffer event_buf, uint32_t n, va_list args); uint8_t scap_get_size_bytes_from_type(enum ppm_param_type t); +char* scap_get_default_value_from_type(enum ppm_param_type t); +bool scap_compare_events(scap_evt* curr, scap_evt* expected, char* error); +scap_evt* scap_create_event_v(char* error, + uint64_t ts, + uint64_t tid, + ppm_event_code event_type, + uint32_t n, + va_list args); +scap_evt* scap_create_event(char* error, + uint64_t ts, + uint64_t tid, + ppm_event_code event_type, + uint32_t n, + ...); +typedef enum scap_print_info { + PRINT_HEADER = 0, + PRINT_HEADER_LENGTHS, + PRINT_FULL, +} scap_print_info; +void scap_print_event(scap_evt* ev, scap_print_info i); /*@}*/ diff --git a/userspace/libscap/scap_event.c b/userspace/libscap/scap_event.c index e039be173a..b67f139ac4 100644 --- a/userspace/libscap/scap_event.c +++ b/userspace/libscap/scap_event.c @@ -409,3 +409,197 @@ uint8_t scap_get_size_bytes_from_type(enum ppm_param_type t) { ASSERT(false); return 0; } + +char *scap_get_default_value_from_type(enum ppm_param_type t) { + static uint8_t default_uint8 = 0; + static uint16_t default_uint16 = 0; + static uint32_t default_uint32 = 0; + static uint64_t default_uint64 = 0; + switch(t) { + case PT_INT8: + case PT_UINT8: + case PT_FLAGS8: + case PT_ENUMFLAGS8: + return (char *)&default_uint8; + case PT_INT16: + case PT_UINT16: + case PT_FLAGS16: + case PT_ENUMFLAGS16: + case PT_SYSCALLID: + return (char *)&default_uint16; + case PT_INT32: + case PT_UINT32: + case PT_FLAGS32: + case PT_ENUMFLAGS32: + case PT_UID: + case PT_GID: + case PT_MODE: + return (char *)&default_uint32; + case PT_INT64: + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + case PT_ERRNO: + case PT_FD: + case PT_PID: + return (char *)&default_uint64; + // Should be ok to return NULL since the len will be 0 + case PT_BYTEBUF: + case PT_CHARBUF: + case PT_SOCKADDR: + case PT_SOCKTUPLE: + case PT_FDLIST: + case PT_FSPATH: + case PT_CHARBUFARRAY: + case PT_CHARBUF_PAIR_ARRAY: + case PT_FSRELPATH: + case PT_DYN: + return NULL; + default: + // We forgot to handle something + ASSERT(false); + break; + } + return 0; +} + +// This should be only used for testing purposes in production we should use directly `memcmp` for +// the whole event +bool scap_compare_events(scap_evt *curr, scap_evt *expected, char *error) { + ////////////////////////////// + // Start comparing the header + ////////////////////////////// + // `UINT64_MAX - 1` can be used to skip the comparison + if(expected->ts != (UINT64_MAX - 1) && curr->ts != expected->ts) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event timestamp mismatch. Current (%ld) != expected (%ld)", + curr->ts, + expected->ts); + return false; + } + if(curr->tid != expected->tid) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event tid mismatch. Current (%ld) != expected (%ld)", + curr->tid, + expected->tid); + return false; + } + if(curr->type != expected->type) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event type mismatch. Current (%d) != expected (%d)", + curr->type, + expected->type); + return false; + } + if(curr->nparams != expected->nparams) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event nparams mismatch. Current (%d) != expected (%d)", + curr->nparams, + expected->nparams); + return false; + } + if(curr->len != expected->len) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event len mismatch. Current (%d) != expected (%d)", + curr->len, + expected->len); + return false; + } + ////////////////////////////// + // Comparing the length of the parameters + ////////////////////////////// + for(int i = 0; i < curr->nparams; i++) { + uint16_t curr_param_len = 0; + uint16_t expected_param_len = 0; + memcpy(&curr_param_len, + (char *)curr + sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * i, + sizeof(uint16_t)); + memcpy(&expected_param_len, + (char *)expected + sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * i, + sizeof(uint16_t)); + if(curr_param_len != expected_param_len) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Param %d length mismatch. Current (%d) != expected (%d)", + i, + curr_param_len, + expected_param_len); + return false; + } + } + ////////////////////////////// + // Comparing each parameter + ////////////////////////////// + char *curr_pos = (char *)curr + sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * curr->nparams; + char *expected_pos = + (char *)expected + sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * curr->nparams; + for(int i = 0; i < curr->nparams; i++) { + uint16_t curr_param_len = 0; + memcpy(&curr_param_len, + (char *)curr + sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * i, + sizeof(uint16_t)); + // todo!: we can improve this by printing the parameter for the specific type. + if(memcmp(curr_pos, expected_pos, curr_param_len) != 0) { + snprintf(error, SCAP_LASTERR_SIZE, "Param %d mismatch. Current != expected", i); + return false; + } + curr_pos += curr_param_len; + expected_pos += curr_param_len; + } + return true; +} + +scap_evt *scap_create_event_v(char *error, + uint64_t ts, + uint64_t tid, + ppm_event_code event_type, + uint32_t n, + va_list args) { + struct scap_sized_buffer event_buf = {NULL, 0}; + size_t event_size = 0; + va_list args2; + va_copy(args2, args); + int32_t ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args); + if(ret != SCAP_INPUT_TOO_SMALL) { + goto error; + } + event_buf.buf = malloc(event_size); + event_buf.size = event_size; + if(event_buf.buf == NULL) { + snprintf(error, SCAP_LASTERR_SIZE, "cannot alloc %ld bytes.", event_size); + goto error; + } + ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args2); + if(ret != SCAP_SUCCESS) { + event_buf.size = 0; + free(event_buf.buf); + goto error; + } + scap_evt *event = (scap_evt *)(event_buf.buf); + event->ts = ts; + event->tid = tid; + va_end(args2); + return event; +error: + va_end(args2); + return NULL; +} + +// If this returns NULL, check the error message. +scap_evt *scap_create_event(char *error, + uint64_t ts, + uint64_t tid, + ppm_event_code event_type, + uint32_t n, + ...) { + va_list args; + va_start(args, n); + scap_evt *ret = scap_create_event_v(error, ts, tid, event_type, n, args); + va_end(args); + return ret; +} diff --git a/userspace/libscap/scap_print_event.c b/userspace/libscap/scap_print_event.c new file mode 100644 index 0000000000..a1bb5190a9 --- /dev/null +++ b/userspace/libscap/scap_print_event.c @@ -0,0 +1,287 @@ + +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. +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 +#include +#include +#if defined(_WIN32) +/* This prevents including when including Ws2tcpip.h */ +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#else +#include +#endif + +static void print_ipv4(int starting_index, char *valptr) { + char ipv4_string[50]; + uint8_t *ipv4 = (uint8_t *)(valptr + starting_index); + snprintf(ipv4_string, sizeof(ipv4_string), "%d.%d.%d.%d", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); + printf("- ipv4: %s\n", ipv4_string); +} + +static void print_ipv6(int starting_index, char *valptr) { + uint32_t ipv6[4] = {0, 0, 0, 0}; + ipv6[0] = *(uint32_t *)(valptr + starting_index); + ipv6[1] = *(uint32_t *)(valptr + starting_index + 4); + ipv6[2] = *(uint32_t *)(valptr + starting_index + 8); + ipv6[3] = *(uint32_t *)(valptr + starting_index + 12); + + char ipv6_string[150]; + inet_ntop(AF_INET6, ipv6, ipv6_string, 150); + printf("- ipv6: %s\n", ipv6_string); +} + +static void print_unix_path(int starting_index, char *valptr) { + printf("- unix path: %s\n", (char *)(valptr + starting_index)); +} + +static void print_port(int starting_index, char *valptr) { + printf("- port: %d\n", *(uint16_t *)(valptr + starting_index)); +} + +static void print_parameter(int16_t num_param, scap_evt *ev, uint16_t offset) { + uint16_t len = + *(uint16_t *)((char *)ev + sizeof(struct ppm_evt_hdr) + num_param * sizeof(uint16_t)); + char *valptr = (char *)ev + offset; + + if(len == 0) { + printf("PARAM %d: is empty\n", num_param); + return; + } + + switch(g_event_info[ev->type].params[num_param].type) { + case PT_FLAGS8: + printf("PARAM %d: %X\n", num_param, *(uint8_t *)(valptr)); + break; + + case PT_FLAGS16: + printf("PARAM %d: %X\n", num_param, *(uint16_t *)(valptr)); + break; + + case PT_FLAGS32: + printf("PARAM %d: %X\n", num_param, *(uint32_t *)(valptr)); + break; + + case PT_INT8: + printf("PARAM %d: %d\n", num_param, *(int8_t *)(valptr)); + break; + + case PT_INT16: + printf("PARAM %d: %d\n", num_param, *(int16_t *)(valptr)); + break; + + case PT_INT32: + printf("PARAM %d: %d\n", num_param, *(int32_t *)(valptr)); + break; + + case PT_INT64: + case PT_ERRNO: + case PT_PID: + printf("PARAM %d: %ld\n", num_param, *(int64_t *)(valptr)); + break; + + case PT_UINT8: + case PT_SIGTYPE: + case PT_ENUMFLAGS8: + printf("PARAM %d: %d\n", num_param, *(uint8_t *)(valptr)); + break; + + case PT_UINT16: + case PT_SYSCALLID: + case PT_ENUMFLAGS16: + printf("PARAM %d: %d\n", num_param, *(uint16_t *)(valptr)); + break; + + case PT_UINT32: + case PT_UID: + case PT_GID: + case PT_SIGSET: + case PT_MODE: + case PT_ENUMFLAGS32: + printf("PARAM %d: %d\n", num_param, *(uint32_t *)(valptr)); + break; + + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + printf("PARAM %d: %lu\n", num_param, *(uint64_t *)(valptr)); + break; + + case PT_FD: + printf("PARAM %d: %d\n", num_param, *(int32_t *)(valptr)); + break; + + case PT_SOCKADDR: { + printf("PARAM %d:\n", num_param); + uint8_t sock_family = *(uint8_t *)(valptr); + printf("- sock_family: %d\n", sock_family); + switch(sock_family) { + case PPM_AF_INET: + /* ipv4 dest. */ + print_ipv4(1, valptr); + + /* port dest. */ + print_port(5, valptr); + break; + + case PPM_AF_INET6: + /* ipv6 dest. */ + print_ipv6(1, valptr); + + /* port dest. */ + print_port(17, valptr); + break; + + case PPM_AF_UNIX: + /* unix_path. */ + print_unix_path(1, valptr); + break; + + default: + printf("- error\n"); + break; + } + break; + } + + case PT_SOCKTUPLE: { + printf("PARAM %d:\n", num_param); + uint8_t sock_family = *(uint8_t *)(valptr); + printf("- sock_family: %d\n", sock_family); + switch(sock_family) { + case PPM_AF_INET: + /* ipv4 src. */ + print_ipv4(1, valptr); + + /* ipv4 dest. */ + print_ipv4(5, valptr); + + /* port src. */ + print_port(9, valptr); + + /* port dest. */ + print_port(11, valptr); + break; + + case PPM_AF_INET6: + /* ipv6 src. */ + print_ipv6(1, valptr); + + /* ipv6 dest. */ + print_ipv6(17, valptr); + + /* port src. */ + print_port(33, valptr); + + /* port dest. */ + print_port(35, valptr); + break; + + case PPM_AF_UNIX: + /* Here there are also some kernel pointers but right + * now we are not interested in catching them. + * 8 + 8 = 16 bytes + */ + + /* unix_path. */ + print_unix_path(17, valptr); + break; + + default: + printf("- error\n"); + break; + } + break; + } + + case PT_CHARBUF: + case PT_FSPATH: + printf("PARAM %d: %s\n", num_param, valptr); + break; + + case PT_BYTEBUF: + case PT_CHARBUFARRAY: + case PT_FSRELPATH: + printf("PARAM %d: ", num_param); + for(int j = 0; j < len; j++) { + printf("%c", *(char *)(valptr + j)); + } + printf("\n"); + break; + + default: + printf("PARAM %d: TYPE NOT KNOWN\n", num_param); + break; + } +} + +static void scap_print_event_header(scap_evt *ev) { + printf("----------------------- HEADER\n"); + printf("timestamp: %lu\n", ev->ts); + printf("tid: %lu\n", ev->tid); + printf("len: %d\n", ev->len); + printf("type: %d\n", ev->type); + printf("num params: %d\n", ev->nparams); + printf("----------------------- \n"); +} + +static void scap_print_lengths(scap_evt *ev) { + printf("----------------------- LEN ARRAY\n"); + uint16_t *lens16 = (uint16_t *)((char *)ev + sizeof(struct ppm_evt_hdr)); + for(int i = 0; i < ev->nparams; i++) { + printf("param %d len: %d\n", i, lens16[i]); + } + if(ev->nparams == 0) { + printf("- This event has no parameter\n"); + } + printf("----------------------- \n"); +} + +static void scap_print_params(scap_evt *ev) { + printf("----------------------- PARAMS\n"); + uint16_t offsets[PPM_MAX_EVENT_PARAMS + 1] = {0}; + offsets[0] = sizeof(struct ppm_evt_hdr) + sizeof(uint16_t) * ev->nparams; + uint16_t *lens16 = (uint16_t *)((char *)ev + sizeof(struct ppm_evt_hdr)); + for(int i = 0; i < ev->nparams; i++) { + print_parameter(i, ev, offsets[i]); + offsets[i + 1] = offsets[i] + lens16[i]; + } + if(ev->nparams == 0) { + printf("- This event has no parameter\n"); + } + printf("-----------------------\n"); +} + +void scap_print_event(scap_evt *ev, scap_print_info i) { + switch(i) { + case PRINT_HEADER: + scap_print_event_header(ev); + break; + case PRINT_HEADER_LENGTHS: + scap_print_event_header(ev); + scap_print_lengths(ev); + break; + case PRINT_FULL: + scap_print_event_header(ev); + scap_print_lengths(ev); + scap_print_params(ev); + break; + + default: + break; + } +} From 1775efd05deb29b634576c31c95fe2c6a2fe8096 Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Fri, 22 Nov 2024 17:11:11 +0100 Subject: [PATCH 2/2] new(converter): add the scap file converter Signed-off-by: Andrea Terzolo --- driver/ppm_events_public.h | 2 + test/libscap/CMakeLists.txt | 5 + .../engines/savefile/convert_event_test.h | 139 ++++++ .../engines/savefile/converter.cpp | 23 + .../libscap/engine/savefile/CMakeLists.txt | 8 +- .../engine/savefile/converter/CMakeLists.txt | 24 ++ .../engine/savefile/converter/converter.cpp | 404 ++++++++++++++++++ .../engine/savefile/converter/converter.h | 42 ++ .../engine/savefile/converter/debug_macro.h | 33 ++ .../engine/savefile/converter/results.h | 25 ++ .../libscap/engine/savefile/converter/table.h | 26 ++ .../libscap/engine/savefile/converter/types.h | 68 +++ 12 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 test/libscap/test_suites/engines/savefile/convert_event_test.h create mode 100644 test/libscap/test_suites/engines/savefile/converter.cpp create mode 100644 userspace/libscap/engine/savefile/converter/CMakeLists.txt create mode 100644 userspace/libscap/engine/savefile/converter/converter.cpp create mode 100644 userspace/libscap/engine/savefile/converter/converter.h create mode 100644 userspace/libscap/engine/savefile/converter/debug_macro.h create mode 100644 userspace/libscap/engine/savefile/converter/results.h create mode 100644 userspace/libscap/engine/savefile/converter/table.h create mode 100644 userspace/libscap/engine/savefile/converter/types.h diff --git a/driver/ppm_events_public.h b/driver/ppm_events_public.h index 2a6d9fca43..647856bfae 100644 --- a/driver/ppm_events_public.h +++ b/driver/ppm_events_public.h @@ -2056,6 +2056,8 @@ enum ppm_event_flags { // overhead to full event capture */ SUPPORT DROPPED EF_LARGE_PAYLOAD = (1 << 11), /* This event has a large payload, ie: up to UINT32_MAX bytes. DO NOT USE ON syscalls-driven events!!! */ + EF_TMP_CONVERTER_MANAGED = (1 << 12), /* todo!: this must be removed when we will mark ENTER + events as OLD_VERSION */ }; /* diff --git a/test/libscap/CMakeLists.txt b/test/libscap/CMakeLists.txt index cdcc76d182..ebac7e968b 100644 --- a/test/libscap/CMakeLists.txt +++ b/test/libscap/CMakeLists.txt @@ -92,6 +92,11 @@ if(BUILD_LIBSCAP_GVISOR) )# Used for includes endif() +file(GLOB_RECURSE SAVEFILE_TEST_SUITE + "${CMAKE_CURRENT_SOURCE_DIR}/test_suites/engines/savefile/*.cpp" +) +list(APPEND LIBSCAP_TESTS_SOURCES ${SAVEFILE_TEST_SUITE}) + # Summary logs set(LIBSCAP_UNIT_TESTS_PREFIX "[LIBSCAP UNIT TESTS]") message(STATUS "${LIBSCAP_UNIT_TESTS_PREFIX} LIBSCAP_TESTS_SOURCES: ${LIBSCAP_TESTS_SOURCES}") diff --git a/test/libscap/test_suites/engines/savefile/convert_event_test.h b/test/libscap/test_suites/engines/savefile/convert_event_test.h new file mode 100644 index 0000000000..99cc22b014 --- /dev/null +++ b/test/libscap/test_suites/engines/savefile/convert_event_test.h @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. +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. +*/ +#pragma once +#include +#include +#include +#include +#include +#include + +typedef std::shared_ptr safe_scap_evt_t; +safe_scap_evt_t new_safe_scap_evt(scap_evt *evt) { + return safe_scap_evt_t{evt, free}; +} +class convert_event_test : public testing::Test { + static constexpr uint16_t safe_margin = 100; + +protected: + virtual void TearDown() { + // At every iteration we want to clear the storage in the converter + scap_clear_converter_storage(); + } + safe_scap_evt_t create_safe_scap_event(uint64_t ts, + uint64_t tid, + ppm_event_code event_type, + uint32_t n, + ...) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + va_list args; + va_start(args, n); + scap_evt *evt = scap_create_event_v(error, ts, tid, event_type, n, args); + va_end(args); + if(evt == NULL) { + throw std::runtime_error("Error creating event: " + std::string(error)); + } + return new_safe_scap_evt(evt); + } + // The expected result can be either CONVERSION_CONTINUE or CONVERSION_COMPLETED + void assert_single_conversion_success(enum conversion_result expected_res, + safe_scap_evt_t evt_to_convert, + safe_scap_evt_t expected_evt) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + // We assume it's okay to create a new event with the same size as the expected event + auto storage = new_safe_scap_evt((scap_evt *)calloc(1, expected_evt->len)); + // First we check the conversion result matches the expected result + ASSERT_EQ(scap_convert_event(storage.get(), evt_to_convert.get(), error), expected_res) + << "Different conversion results: " << error; + if(!scap_compare_events(storage.get(), expected_evt.get(), error)) { + printf("\nExpected event:\n"); + scap_print_event(expected_evt.get(), PRINT_FULL); + printf("\nConverted event:\n"); + scap_print_event(storage.get(), PRINT_FULL); + FAIL() << error; + } + } + void assert_single_conversion_failure(safe_scap_evt_t evt_to_convert) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + // We assume it's okay to create a new event with the same size as the expected event + auto storage = new_safe_scap_evt((scap_evt *)calloc(1, evt_to_convert->len)); + // First we check the conversion result matches the expected result + ASSERT_EQ(scap_convert_event(storage.get(), evt_to_convert.get(), error), CONVERSION_ERROR) + << "The conversion is not failed: " << error; + } + void assert_single_conversion_skip(safe_scap_evt_t evt_to_convert) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + // We assume it's okay to create a new event with the same size as the expected event + auto storage = new_safe_scap_evt((scap_evt *)calloc(1, evt_to_convert->len)); + // First we check the conversion result matches the expected result + ASSERT_EQ(scap_convert_event(storage.get(), evt_to_convert.get(), error), CONVERSION_SKIP) + << "The conversion is not skipped: " << error; + } + void assert_full_conversion(safe_scap_evt_t evt_to_convert, safe_scap_evt_t expected_evt) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + // Here we need to allocate more space than the expected event because in the middle we + // could have larger events. We could also use `MAX_EVENT_SIZE` but probably it will just + // slowdown tests. + auto to_convert_evt = new_safe_scap_evt( + (scap_evt *)calloc(1, expected_evt->len + convert_event_test::safe_margin)); + auto new_evt = new_safe_scap_evt( + (scap_evt *)calloc(1, expected_evt->len + convert_event_test::safe_margin)); + // We copy the event to convert into the new larger storage since during the conversions it + // could contain larger events than the initial one. + // We copy it in the new event to match the for loop logic. + memcpy(new_evt.get(), evt_to_convert.get(), evt_to_convert->len); + int conv_num = 0; + conversion_result conv_res = CONVERSION_CONTINUE; + for(conv_num = 0; conv_num < MAX_CONVERSION_BOUNDARY && conv_res == CONVERSION_CONTINUE; + conv_num++) { + // Copy the new event into the one to convert for the next conversion. + memcpy(to_convert_evt.get(), new_evt.get(), new_evt->len); + conv_res = scap_convert_event((scap_evt *)new_evt.get(), + (scap_evt *)to_convert_evt.get(), + error); + } + switch(conv_res) { + case CONVERSION_ERROR: + FAIL() << "Unexpected CONVERSION_ERROR: " << error; + case CONVERSION_SKIP: + FAIL() << "Unexpected CONVERSION_SKIP"; + case CONVERSION_CONTINUE: + if(conv_num < MAX_CONVERSION_BOUNDARY) { + FAIL() << "Unexpected CONVERSION_CONTINUE without reaching max boundary"; + } else { + FAIL() << "Unexpected CONVERSION_CONTINUE reaching max boundary"; + } + default: + break; + } + if(!scap_compare_events(new_evt.get(), expected_evt.get(), error)) { + printf("\nExpected event:\n"); + scap_print_event(expected_evt.get(), PRINT_FULL); + printf("\nConverted event:\n"); + scap_print_event(new_evt.get(), PRINT_FULL); + FAIL() << error; + } + } + void assert_event_storage_presence(safe_scap_evt_t expected_evt) { + char error[SCAP_LASTERR_SIZE] = {'\0'}; + int64_t tid = expected_evt.get()->tid; + auto event = scap_retrieve_evt_from_converter_storage(tid); + if(!event) { + FAIL() << "Event with tid " << tid << " not found in the storage"; + } + if(!scap_compare_events(event, expected_evt.get(), error)) { + FAIL() << "Different events: " << error; + } + } +}; diff --git a/test/libscap/test_suites/engines/savefile/converter.cpp b/test/libscap/test_suites/engines/savefile/converter.cpp new file mode 100644 index 0000000000..2b46b80781 --- /dev/null +++ b/test/libscap/test_suites/engines/savefile/converter.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. +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 "convert_event_test.h" + +// todo!: remove it when we will add the first example. +TEST_F(convert_event_test, tmp_example) { + uint64_t ts = 12; + int64_t tid = 25; + + // At the moment we don't have event managed by the converter so this should fail. + assert_single_conversion_failure(create_safe_scap_event(ts, tid, PPME_SYSCALL_OPEN_E, 0)); +} diff --git a/userspace/libscap/engine/savefile/CMakeLists.txt b/userspace/libscap/engine/savefile/CMakeLists.txt index 66c03778c5..22418d3260 100644 --- a/userspace/libscap/engine/savefile/CMakeLists.txt +++ b/userspace/libscap/engine/savefile/CMakeLists.txt @@ -14,7 +14,11 @@ # # Since we have circular dependencies between libscap and the savefile engine, make this library # always static (directly linked into libscap) +add_subdirectory(converter) add_library(scap_engine_savefile STATIC scap_savefile.c scap_reader_gzfile.c scap_reader_buffered.c) -add_dependencies(scap_engine_savefile zlib) -target_link_libraries(scap_engine_savefile PRIVATE scap_engine_noop scap_platform_util ${ZLIB_LIB}) +add_dependencies(scap_engine_savefile zlib scap_savefile_converter) +target_link_libraries( + scap_engine_savefile PRIVATE scap_engine_noop scap_platform_util scap_savefile_converter + ${ZLIB_LIB} +) diff --git a/userspace/libscap/engine/savefile/converter/CMakeLists.txt b/userspace/libscap/engine/savefile/converter/CMakeLists.txt new file mode 100644 index 0000000000..05e9f2ecb2 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/CMakeLists.txt @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2024 The Falco Authors. +# +# 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. +# + +# Since we have circular dependencies between libscap and the savefile engine, make this library +# always static (directly linked into libscap) +add_library(scap_savefile_converter STATIC converter.cpp) + +add_dependencies(scap_savefile_converter uthash) +target_include_directories( + scap_savefile_converter PRIVATE ${LIBS_DIR} ${LIBS_DIR}/userspace + ${LIBS_DIR}/userspace/libscap/engine/savefile +) diff --git a/userspace/libscap/engine/savefile/converter/converter.cpp b/userspace/libscap/engine/savefile/converter/converter.cpp new file mode 100644 index 0000000000..4a051b1eb0 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/converter.cpp @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef std::shared_ptr safe_scap_evt_t; + +static inline safe_scap_evt_t safe_scap_evt(scap_evt *evt) { + return safe_scap_evt_t{evt, free}; +} + +// use a shared pointer to store the events +static std::unordered_map evt_storage = {}; + +extern const struct ppm_event_info g_event_info[]; + +static const char *get_event_name(ppm_event_code event_type) { + const struct ppm_event_info *event_info = &g_event_info[event_type]; + return event_info->name; +} + +static char get_direction_char(ppm_event_code event_type) { + if(PPME_IS_ENTER(event_type)) { + return 'E'; + } else { + return 'X'; + } +} + +static void clear_evt(uint64_t tid) { + if(evt_storage.find(tid) != evt_storage.end()) { + evt_storage[tid].reset(); + } +} + +static void store_evt(uint64_t tid, scap_evt *evt) { + // if there was a previous event for this tid, we can overwrite the pointer because it means we + // don't need it anymore. We need to keep the enter event until we retrieve it in the + // corresponding exit event, but if the same thread is doing another enter event it means the + // previous syscall is already completed. + + clear_evt(tid); + + scap_evt *tmp_evt = (scap_evt *)malloc(evt->len); + if(!tmp_evt) { + throw std::runtime_error("Cannot allocate memory for the enter event."); + } + memcpy(tmp_evt, evt, evt->len); + evt_storage[tid] = safe_scap_evt(tmp_evt); +} + +static scap_evt *retrieve_evt(uint64_t tid) { + if(evt_storage.find(tid) != evt_storage.end()) { + return evt_storage[tid].get(); + } + return nullptr; +} + +static uint16_t get_param_len(scap_evt *evt, uint8_t num_param) { + if(evt->nparams <= num_param) { + std::string error = "Try to access len of param num '" + std::to_string(num_param) + + "' for event " + get_event_name((ppm_event_code)evt->type) + "_" + + get_direction_char((ppm_event_code)evt->type) + + " (num parameters: " + std::to_string(evt->type) + ")."; + throw std::runtime_error(error); + } + + // todo!: we need to manage LARGE_PAYLOAD events + uint16_t off_len = sizeof(scap_evt) + sizeof(uint16_t) * num_param; + uint16_t len = 0; + memcpy(&len, (char *)evt + off_len, sizeof(uint16_t)); + return (uint32_t)len; +} + +static char *get_param_ptr(scap_evt *evt, uint8_t num_param) { + if(evt->nparams <= num_param) { + std::string error = "Try to access param num '" + std::to_string(num_param) + + "' for event " + get_event_name((ppm_event_code)evt->type) + "_" + + get_direction_char((ppm_event_code)evt->type) + + " (num parameters: " + std::to_string(evt->type) + ")."; + throw std::runtime_error(error); + } + + char *ptr = (char *)evt + sizeof(scap_evt) + sizeof(uint16_t) * evt->nparams; + uint16_t ptr_off = 0; + for(auto i = 0; i < num_param; i++) { + uint16_t len = 0; + memcpy(&len, (char *)evt + sizeof(scap_evt) + sizeof(uint16_t) * i, sizeof(uint16_t)); + ptr_off += len; + } + + return ptr + ptr_off; +} + +// This writes len + the param +static void push_default_parameter(scap_evt *evt, uint16_t *params_offset, uint8_t param_num) { + // Please ensure that `new_evt->type` is already the final type you want to obtain. + // Otherwise we will access the wrong entry in the event table. + const struct ppm_event_info *event_info = &(g_event_info[evt->type]); + uint16_t len = scap_get_size_bytes_from_type(event_info->params[param_num].type); + char *ptr = scap_get_default_value_from_type(event_info->params[param_num].type); + uint16_t lens_offset = sizeof(scap_evt) + param_num * sizeof(uint16_t); + + PRINT_MESSAGE( + "push default param (%d, type: %d) with len (%d) at {params_offest (%d), " + "lens_offset (%d)}\n", + param_num, + event_info->params[param_num].type, + len, + *params_offset, + lens_offset); + + // If value is NULL, the len should be 0 + memcpy((char *)evt + *params_offset, ptr, len); + *params_offset += len; + memcpy((char *)evt + lens_offset, &len, sizeof(uint16_t)); +} + +// This writes len + the param +static void push_parameter(scap_evt *new_evt, + scap_evt *tmp_evt, + uint16_t *params_offset, + uint8_t new_evt_param_num, + uint8_t tmp_evt_param_num) { + // we need to write the len into the event. + uint16_t lens_offset = sizeof(scap_evt) + new_evt_param_num * sizeof(uint16_t); + uint16_t len = get_param_len(tmp_evt, tmp_evt_param_num); + char *ptr = get_param_ptr(tmp_evt, tmp_evt_param_num); + + PRINT_MESSAGE( + "push param (%d, type: %d) with len (%d) at {params_offest: %d, " + "lens_offset: %d} from event type '%d', param '%d'\n", + new_evt_param_num, + g_event_info[tmp_evt->type].params[tmp_evt_param_num].type, + len, + *params_offset, + lens_offset, + tmp_evt->type, + tmp_evt_param_num); + + memcpy((char *)new_evt + *params_offset, ptr, len); + *params_offset += len; + memcpy((char *)new_evt + lens_offset, &len, sizeof(uint16_t)); +} + +static uint16_t copy_old_params(scap_evt *new_evt, scap_evt *evt_to_convert) { + // Copy the lengths array + uint16_t new_evt_offset = sizeof(scap_evt); + uint16_t old_evt_offset = sizeof(scap_evt); + uint16_t size_to_copy = evt_to_convert->nparams * sizeof(uint16_t); + memcpy((char *)new_evt + new_evt_offset, (char *)evt_to_convert + old_evt_offset, size_to_copy); + + PRINT_MESSAGE( + "Copy lengths array (size %d) from old event offset '%d' to new event " + "offset '%d'\n", + size_to_copy, + old_evt_offset, + new_evt_offset); + + // Copy the parameters (we left some space for the missing lengths) + new_evt_offset = sizeof(scap_evt) + new_evt->nparams * sizeof(uint16_t); + old_evt_offset = sizeof(scap_evt) + evt_to_convert->nparams * sizeof(uint16_t); + size_to_copy = + evt_to_convert->len - (sizeof(scap_evt) + evt_to_convert->nparams * sizeof(uint16_t)); + memcpy((char *)new_evt + new_evt_offset, (char *)evt_to_convert + old_evt_offset, size_to_copy); + + PRINT_MESSAGE( + "Copy parameters (size %d) from old event offset '%d' to new event " + "offset '%d'\n", + size_to_copy, + old_evt_offset, + new_evt_offset); + + return new_evt_offset + size_to_copy; +} + +extern "C" bool is_conversion_needed(scap_evt *evt_to_convert) { + assert(evt_to_convert->type < PPM_EVENT_MAX); + const struct ppm_event_info *event_info = &(g_event_info[evt_to_convert->type]); + + // todo!: we need to cleanup this logic when we can mark enter events as `EF_OLD_VERSION` + + // If the event is not yet managed by the converter we never need a conversion + if((event_info->flags & EF_TMP_CONVERTER_MANAGED) == 0) { + return false; + } + + // If the event is managed by the converter and it is an enter event it will always need a + // conversion. + if(PPME_IS_ENTER(evt_to_convert->type)) { + return true; + } + + // If it is an exit event it needs a conversion when: + // - it is an `EF_OLD_VERSION` + // - the number of parameters is different from the one in the event table + + // If we are a new event type we need to check the number of parameters. + assert(evt_to_convert->nparams <= event_info->nparams); + + // If the number of parameters is different from the one in the event table we need a + // conversion. + if((event_info->flags & EF_OLD_VERSION) || (evt_to_convert->nparams != event_info->nparams)) { + return true; + } + return false; +} + +extern "C" scap_evt *scap_retrieve_evt_from_converter_storage(uint64_t tid) { + return retrieve_evt(tid); +} + +extern "C" void scap_clear_converter_storage() { + evt_storage.clear(); +} + +static conversion_result convert_event(scap_evt *new_evt, + scap_evt *evt_to_convert, + const conversion_info *ci, + char *error) { + ///////////////////////////// + // Dispatch the action + ///////////////////////////// + + uint16_t params_offset = 0; + int param_to_populate = 0; + + switch(ci->action) { + case C_ACTION_SKIP: + return CONVERSION_SKIP; + + case C_ACTION_STORE: + store_evt(evt_to_convert->tid, evt_to_convert); + return CONVERSION_SKIP; + + case C_ACTION_ADD_PARAMS: + memcpy(new_evt, evt_to_convert, sizeof(scap_evt)); + // The new number of params is the previous one plus the number of conversion instructions. + new_evt->nparams = evt_to_convert->nparams + ci->instr.size(); + params_offset = copy_old_params(new_evt, evt_to_convert); + param_to_populate = evt_to_convert->nparams; + break; + + case C_ACTION_CHANGE_TYPE: + memcpy(new_evt, evt_to_convert, sizeof(scap_evt)); + // The new number of params is the number of conversion instructions. + new_evt->nparams = ci->instr.size(); + new_evt->type = ci->desired_type; + params_offset = sizeof(scap_evt) + new_evt->nparams * sizeof(uint16_t); + param_to_populate = 0; + break; + + default: + snprintf(error, SCAP_LASTERR_SIZE, "Unhandled conversion action '%d'.", ci->action); + return CONVERSION_ERROR; + } + + ///////////////////////////// + // Fill the event to its most recent version + ///////////////////////////// + + PRINT_MESSAGE("New event header (the len is still the old one):\n"); + PRINT_EVENT(new_evt, PRINT_HEADER); + + scap_evt *tmp_evt = NULL; + // If this is true at the end of the for loop we will free its memory. + bool used_enter_event = false; + + // We iterate over the instructions + for(int i = 0; i < ci->instr.size(); i++, param_to_populate++) { + PRINT_MESSAGE("Instruction n° %d. Param to populate: %d\n", i, param_to_populate); + + switch(ci->instr[i].flags) { + case C_INSTR_FROM_DEFAULT: + tmp_evt = NULL; + break; + + case C_INSTR_FROM_ENTER: + tmp_evt = retrieve_evt(evt_to_convert->tid); + if(!tmp_evt) { + // It could be due to different reasons: + // - we dropped the enter event in the capture + // - we jump here from a previous conversion. For example, we jump from `BRK_1_X` to + // `BRK_4_X` but in this case we don't have the enter event BRK_4_E because we don't + // convert `BRK_1_E` to `BRK_4_E`. It would be meaningless, they would't bring the + // same info. + break; + } + + // todo!: undestand if we can pretend this is an error or it is a normal situation. + if(tmp_evt->type != evt_to_convert->type - 1) { + snprintf(error, + SCAP_LASTERR_SIZE, + "The enter event for '%s_%c' is not the right one! Event found '%s_%c'.", + get_event_name((ppm_event_code)evt_to_convert->type), + get_direction_char((ppm_event_code)evt_to_convert->type), + get_event_name((ppm_event_code)tmp_evt->type), + get_direction_char((ppm_event_code)tmp_evt->type)); + return CONVERSION_ERROR; + } + used_enter_event = true; + break; + + case C_INSTR_FROM_OLD: + tmp_evt = evt_to_convert; + if(tmp_evt->nparams <= ci->instr[i].param_num) { + // todo!: this sounds like an error but let's see in the future. At the moment we + // fail + snprintf(error, + SCAP_LASTERR_SIZE, + "We want to take parameter '%d' from event '%d' but this event has only " + "'%d' parameters!", + ci->instr[i].param_num, + tmp_evt->type, + tmp_evt->nparams); + return CONVERSION_ERROR; + } + break; + + default: + snprintf(error, + SCAP_LASTERR_SIZE, + "Unknown instruction (flags: %d, param_num: %d).", + ci->instr[i].flags, + ci->instr[i].param_num); + return CONVERSION_ERROR; + } + + if(!tmp_evt) { + push_default_parameter(new_evt, ¶ms_offset, param_to_populate); + } else { + push_parameter(new_evt, + tmp_evt, + ¶ms_offset, + param_to_populate, + ci->instr[i].param_num); + } + } + + if(used_enter_event) { + // We can free the enter event because we don't need it anymore. + clear_evt(evt_to_convert->tid); + } + + new_evt->len = params_offset; + + PRINT_MESSAGE("Final event:\n"); + PRINT_EVENT(new_evt, PRINT_FULL); + return is_conversion_needed(new_evt) ? CONVERSION_CONTINUE : CONVERSION_COMPLETED; +} + +extern "C" conversion_result scap_convert_event(scap_evt *new_evt, + scap_evt *evt_to_convert, + char *error) { + // This should be checked by the caller but just double check here + if(!is_conversion_needed(evt_to_convert)) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Conversion not need for event type '%d' nparams '%d'. Please double check", + evt_to_convert->type, + evt_to_convert->nparams); + return CONVERSION_ERROR; + } + + // If we need a conversion but we don't have an entry in the table we have an error. + auto conv_key = conversion_key{evt_to_convert->type, (uint8_t)evt_to_convert->nparams}; + if(g_conversion_table.find(conv_key) == g_conversion_table.end()) { + snprintf(error, + SCAP_LASTERR_SIZE, + "Event '%d' has '%d' parameters, but we don't handle it in the table.", + evt_to_convert->type, + evt_to_convert->nparams); + return CONVERSION_ERROR; + } + + // If we reached this point we have for sure an entry in the conversion table. + return convert_event(new_evt, evt_to_convert, &g_conversion_table[conv_key], error); +} diff --git a/userspace/libscap/engine/savefile/converter/converter.h b/userspace/libscap/engine/savefile/converter/converter.h new file mode 100644 index 0000000000..338486b103 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/converter.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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. + +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct ppm_evt_hdr scap_evt; + +// 50 consecutive conversions on the same event should be more than enough +#define MAX_CONVERSION_BOUNDARY 50 + +conversion_result scap_convert_event(scap_evt* new_evt, scap_evt* evt_to_convert, char* error); + +bool is_conversion_needed(scap_evt* evt_to_convert); + +// Only for testing purposes +scap_evt* scap_retrieve_evt_from_converter_storage(uint64_t tid); +void scap_clear_converter_storage(); + +#ifdef __cplusplus +}; +#endif diff --git a/userspace/libscap/engine/savefile/converter/debug_macro.h b/userspace/libscap/engine/savefile/converter/debug_macro.h new file mode 100644 index 0000000000..fbceca9376 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/debug_macro.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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. + +*/ +#pragma once + +#include + +#define CONVERSION_DEBUGGING 0 + +#if CONVERSION_DEBUGGING +#define PRINT_MESSAGE(...) printf("[DEBUG]: " __VA_ARGS__) +#define PRINT_EVENT(ev, i) \ + printf("\n"); \ + scap_print_event(ev, i); \ + printf("\n"); +#else +#define PRINT_MESSAGE(...) +#define PRINT_EVENT(ev, i) +#endif diff --git a/userspace/libscap/engine/savefile/converter/results.h b/userspace/libscap/engine/savefile/converter/results.h new file mode 100644 index 0000000000..d573b2062b --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/results.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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. + +*/ +#pragma once + +typedef enum conversion_result { + CONVERSION_ERROR, + CONVERSION_CONTINUE, + CONVERSION_COMPLETED, + CONVERSION_SKIP, +} conversion_result; diff --git a/userspace/libscap/engine/savefile/converter/table.h b/userspace/libscap/engine/savefile/converter/table.h new file mode 100644 index 0000000000..15047d7b53 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/table.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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. + +*/ +#pragma once + +#include +#include +#include + +#include + +static std::unordered_map g_conversion_table = {}; diff --git a/userspace/libscap/engine/savefile/converter/types.h b/userspace/libscap/engine/savefile/converter/types.h new file mode 100644 index 0000000000..1548701649 --- /dev/null +++ b/userspace/libscap/engine/savefile/converter/types.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +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. + +*/ +#pragma once + +#include +#include + +enum conversion_instruction_flags { + C_NO_INSTR = 0, // This should be never called + C_INSTR_FROM_OLD, // Take the parameter from the old event + C_INSTR_FROM_ENTER, // Take the parameter from the enter event + C_INSTR_FROM_DEFAULT, // Generate the default parameter +}; + +// Conversion actions +enum conversion_action { + C_ACTION_UNKNOWN = 0, + C_ACTION_SKIP, + C_ACTION_STORE, + C_ACTION_ADD_PARAMS, + C_ACTION_CHANGE_TYPE, +}; + +struct conversion_instruction { + uint8_t flags = 0; + uint8_t param_num = 0; +}; + +struct conversion_key { + uint16_t event_code = 0; + uint8_t param_num = 0; + + // Comparison operator for equality (needed by std::unordered_map) + bool operator==(const conversion_key& other) const { + return event_code == other.event_code && param_num == other.param_num; + } +}; + +namespace std { +template<> +struct hash { + size_t operator()(const conversion_key& key) const { + // Combine the hash of event_code and param_num + return std::hash()(key.event_code) ^ (std::hash()(key.param_num) << 1); + } +}; +} // namespace std + +struct conversion_info { + uint8_t action = 0; + uint16_t desired_type = 0; // Needed only when action is `C_ACTION_CHANGE_TYPE` + std::vector instr = {}; +};