From fe0076fb6fc2194c30079b6f2844fdf50a3d1b18 Mon Sep 17 00:00:00 2001 From: Donn Date: Tue, 11 Apr 2023 16:50:23 -0500 Subject: [PATCH 01/25] Initial support. Scope all processes in a docker container. Debug and hard-coded values exist. --- src/fn.c | 1 + src/fn.h | 1 + src/gocontext_arm.S | 2 ++ src/wrap_go.c | 72 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/fn.c b/src/fn.c index 19af7331c..8160f0fdb 100644 --- a/src/fn.c +++ b/src/fn.c @@ -333,6 +333,7 @@ initFn(void) GETADDR(g_fn.closedir, "closedir"); GETADDR(g_fn.readdir, "readdir"); GETADDR(g_fn.setrlimit, "setrlimit"); + GETADDR(g_fn.mount, "mount"); #ifdef __STATX__ GETADDR(g_fn.statx, "statx"); #endif diff --git a/src/fn.h b/src/fn.h index de5ddce13..a25ba9644 100644 --- a/src/fn.h +++ b/src/fn.h @@ -216,6 +216,7 @@ typedef struct { void *(*__memcpy_chk)(void *, const void *, size_t, size_t); int (*__sprintf_chk)(char *, int, size_t, const char *, ...); long int (*__fdelt_chk)(long int); + int (*mount)(const char *, const char *, const char *, unsigned long, const void *); #ifdef __linux__ // Couldn't easily get struct definitions for these on mac int (*statvfs64)(const char *, struct statvfs64 *); diff --git a/src/gocontext_arm.S b/src/gocontext_arm.S index ef09f9386..89e20c89d 100644 --- a/src/gocontext_arm.S +++ b/src/gocontext_arm.S @@ -121,6 +121,8 @@ b.eq 2f cmp x8, #SYS_close b.eq 2f + cmp x8, #SYS_mount + b.eq 2f ldr x17, =\retptr ldr x17, [x17] diff --git a/src/wrap_go.c b/src/wrap_go.c index 903d0c38e..e0c18f2b2 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -367,7 +367,7 @@ adjustGoStructOffsetsForVersion() scopeLogWarn("Architecture not supported. Not adjusting schema offsets."); return; } - tap_entry(tap_syscall)->func_name = "runtime/internal/syscall.Syscall6"; + tap_entry(tap_syscall)->func_name = "runtime/internal/syscall.Syscall6.abi0"; tap_entry(tap_rawsyscall)->func_name = ""; tap_entry(tap_syscall6)->func_name = ""; } @@ -793,7 +793,6 @@ patch_addrs(funchook_t *funchook, } else if (tap->assembly_fn == go_hook_reg_syscall6) { g_syscall6_return = (uint64_t)tap->return_addr; } - break; // Done patching } @@ -1242,6 +1241,8 @@ c_syscall(char *sys_stack, char *g_stack) uint64_t syscall_num = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_num); int64_t rc = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_rc); if(rc < 0) rc = -1; // kernel syscalls can return values < -1 + static bool once = FALSE; + static int count = 0; switch(syscall_num) { case SYS_write: @@ -1330,6 +1331,73 @@ c_syscall(char *sys_stack, char *g_stack) doCloseAndReportFailures(fd, (rc != -1), "go_close"); // If net, deletes a net object } break; + case SYS_mount: + { + char *src = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p1); + char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); + char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); + + count++; + funcprint("Scope: mount of %s %s %s\n", src, target, fstype); + + // scope_strstr(g_proc.procname, "dockerd") && + // (once == FALSE) && + // (count == 3) && + // (scope_strstr(target, "init") == NULL) && + if ((src != NULL) && (target != NULL) && + (count == 3) && + scope_strstr(src, "overlay") && + scope_strstr(target, "merged")) { + int ldfd; + char *filterdir; + size_t targetlen = scope_strlen(target); + + once = TRUE; + scopeLogError("Interposed Mount overlay: %s %d %d\n", g_proc.procname, once, count); + + if ((filterdir = scope_malloc(targetlen + 128)) == NULL) break; + scope_strcpy(filterdir, target); + scope_strcat(filterdir, "/opt/scope"); + + // make a dir in the merged dir + scopeLogError("%s:%d mkdir %s", __FUNCTION__, __LINE__, filterdir); + if (scope_mkdir(filterdir, 0666) != 0) { + scopeLogError("ERROR: mkdir %s:%d", __FUNCTION__, __LINE__); + scope_free(filterdir); + break; + } + + // mount the merged dir addition into the host FS + scopeLogError("%s:%d mount 1 %s", __FUNCTION__, __LINE__, filterdir); + if (g_fn.mount("/opt/scope", filterdir, "overlay", MS_BIND, NULL) != 0) { + scope_free(filterdir); + scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); + break; + } + + filterdir[targetlen + 1] = '\0'; + scope_strcat(filterdir, "etc/ld.so.preload"); + scopeLogError("%s:%d open %s", __FUNCTION__, __LINE__, filterdir); + if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT, 0666)) == -1) { + scopeLogError("ERROR: open %s:%d", __FUNCTION__, __LINE__); + scope_free(filterdir); + break; + } + + scope_close(ldfd); + + // mount merged/etc/ld.so.preload from the filter dir + scopeLogError("%s:%d mount 2 %s", __FUNCTION__, __LINE__, filterdir); + if (g_fn.mount("/opt/scope/ld.so.preload", filterdir, "overlay", MS_BIND, NULL) != 0) { + scope_free(filterdir); + scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); + break; + } + scope_free(filterdir); + break; + } + } + break; default: break; } From 9aebf88bfdcde6d4821f23de477f2324fa1e1a6a Mon Sep 17 00:00:00 2001 From: Donn Date: Wed, 12 Apr 2023 17:20:50 -0500 Subject: [PATCH 02/25] Use the defined libdir. Create intermediate dirs before a mount. Write the preload file, not mount. --- src/dbg.c | 1 + src/dbg.h | 1 + src/scopetypes.h | 2 ++ src/utils.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 2 ++ src/wrap.c | 18 +++++++++++++ src/wrap_go.c | 53 +++++++++++++++++++----------------- 7 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/dbg.c b/src/dbg.c index 3f5b9c22d..d11a88a5e 100644 --- a/src/dbg.c +++ b/src/dbg.c @@ -39,6 +39,7 @@ bool g_ismusl = FALSE; bool g_isstatic = FALSE; bool g_isgo = FALSE; bool g_issighandler = FALSE; +char g_libpath[PATH_MAX] = {}; void dbgInit() diff --git a/src/dbg.h b/src/dbg.h index a8ecac027..53985a5e2 100644 --- a/src/dbg.h +++ b/src/dbg.h @@ -79,6 +79,7 @@ extern bool g_ismusl; extern bool g_isstatic; extern bool g_isgo; extern bool g_issighandler; +extern char g_libpath[]; void scopeLog(cfg_log_level_t, const char *, ...) PRINTF_FORMAT(2,3); void scopeLogHex(cfg_log_level_t, const void *, size_t, const char *, ...) PRINTF_FORMAT(4,5); diff --git a/src/scopetypes.h b/src/scopetypes.h index 7972523ce..662d756b0 100644 --- a/src/scopetypes.h +++ b/src/scopetypes.h @@ -50,6 +50,8 @@ typedef enum {CFG_MTC_FS, #define SM_NAME "scope_anon" #define SCOPE_FILTER_USR_PATH ("/usr/lib/appscope/scope_filter") #define SCOPE_FILTER_TMP_PATH ("/tmp/appscope/scope_filter") +#define SCOPE_SYS_PATH "/usr/lib/appscope/" +#define SCOPE_TMP_PATH "/tmp/appscope/" typedef unsigned int bool; #define TRUE 1 diff --git a/src/utils.c b/src/utils.c index 5d8852625..75b79dfdf 100644 --- a/src/utils.c +++ b/src/utils.c @@ -494,3 +494,73 @@ sigSafeWriteNumber(int fd, long val, int base) { return scope_write(fd, buf ,msgLen); } +/* + * Returns normalized version string + * - "dev" for unofficial release + * - "%d.%d.%d" for official release (e.g. "1.3.0" for "v1.3.0") + * - "%d.%d.%d-%s%d" for candidate release (e.g. "1.3.0-rc0" for "v1.3.0-rc0") + */ +const char * +libVersion(const char *version) { + + if ((version == NULL) || (*version != 'v')) { + return "dev"; + } + + ++version; + size_t versionSize = scope_strlen(version); + + for (int i = 0; i < versionSize; ++i) { + // Only digit and "." are accepted + if ((scope_isdigit(version[i]) == 0) && version[i] != '.' && + version[i] != '-' && version[i] != 't' && version[i] != 'c' && + version[i] != 'r') { + return "dev"; + } + if (i == 0 || i == versionSize) { + // First and last character must be number + if (scope_isdigit(version[i]) == 0) { + return "dev"; + } + } + } + return version; +} + +/* + * The mkdir system call does not support the creation of intermediate dirs. + * This will walk the path and create all dirs one at a time. + */ +int +makeIntermediateDirs(const char *path, mode_t mode) { + char *dup_path = scope_strdup(path); + if (!dup_path) { + return -1; + } + + char *curr = dup_path; + char *slash; + + while ((slash = scope_strrchr(curr, '/'))) { + *slash = '\0'; + if (scope_mkdir(dup_path, mode) == -1) { + if (scope_errno != EEXIST) { + scope_free(dup_path); + return -1; + } + } + + *slash = '/'; + curr = slash + 1; + } + + if (scope_mkdir(dup_path, mode) == -1) { + if (scope_errno != EEXIST) { + scope_free(dup_path); + return -1; + } + } + + scope_free(dup_path); + return 0; +} diff --git a/src/utils.h b/src/utils.h index b8da28aa9..4b3fc31fc 100644 --- a/src/utils.h +++ b/src/utils.h @@ -28,5 +28,7 @@ int sigSafeNanosleep(const struct timespec *); void sigSafeUtoa(unsigned long, char *, int, int *); bool sigSafeMkdirRecursive(const char *); ssize_t sigSafeWriteNumber(int , long, int); +const char *libVersion(const char *); +int makeIntermediateDirs(const char *, mode_t); #endif // __UTILS_H__ diff --git a/src/wrap.c b/src/wrap.c index 02256442c..47abdc905 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -1718,6 +1718,24 @@ init(void) bool scopedFlag = FALSE; bool skipReadCfg = FALSE; + /* + * This might be able to merge with the filter file check below. + * However, we dont want to check a specific file. We may update + * how we define the filter file. So, for now, leaving them + * distinct. + * + * Would be nice to use an env var without the need to access + * a file. However, we don't rely on something like SCOPE_LIB_PATH + * when LD_PRELOAD alone is used. + */ + if (!scope_access(SCOPE_SYS_PATH, R_OK)) { + scope_strncpy(g_libpath, SCOPE_SYS_PATH, sizeof(SCOPE_SYS_PATH)); + } else if (!scope_access(SCOPE_TMP_PATH, R_OK)) { + scope_strncpy(g_libpath, SCOPE_TMP_PATH, sizeof(SCOPE_TMP_PATH)); + } + + scope_strncat(g_libpath, libVersion(SCOPE_VER), sizeof(SCOPE_VER)); + if (attachedFlag) { scopedFlag = TRUE; } else { diff --git a/src/wrap_go.c b/src/wrap_go.c index e0c18f2b2..8d700f1e6 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1241,8 +1241,7 @@ c_syscall(char *sys_stack, char *g_stack) uint64_t syscall_num = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_num); int64_t rc = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_rc); if(rc < 0) rc = -1; // kernel syscalls can return values < -1 - static bool once = FALSE; - static int count = 0; + static int once = 0; switch(syscall_num) { case SYS_write: @@ -1337,39 +1336,40 @@ c_syscall(char *sys_stack, char *g_stack) char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); - count++; + once++; funcprint("Scope: mount of %s %s %s\n", src, target, fstype); - // scope_strstr(g_proc.procname, "dockerd") && - // (once == FALSE) && - // (count == 3) && - // (scope_strstr(target, "init") == NULL) && + /* + * Checks: + * only applies to the dockerd proc + * ensure the constructor populated a path + * proceed after container mounts are done + */ if ((src != NULL) && (target != NULL) && - (count == 3) && - scope_strstr(src, "overlay") && - scope_strstr(target, "merged")) { + scope_strstr(g_proc.procname, "dockerd") && + (scope_strstr(g_libpath, "/")) && + (once == 3)) { int ldfd; char *filterdir; size_t targetlen = scope_strlen(target); - once = TRUE; - scopeLogError("Interposed Mount overlay: %s %d %d\n", g_proc.procname, once, count); + scopeLogError("Interposed Mount overlay: %s %d\n", g_proc.procname, once); if ((filterdir = scope_malloc(targetlen + 128)) == NULL) break; scope_strcpy(filterdir, target); - scope_strcat(filterdir, "/opt/scope"); + scope_strcat(filterdir, g_libpath); // make a dir in the merged dir scopeLogError("%s:%d mkdir %s", __FUNCTION__, __LINE__, filterdir); - if (scope_mkdir(filterdir, 0666) != 0) { - scopeLogError("ERROR: mkdir %s:%d", __FUNCTION__, __LINE__); + if (makeIntermediateDirs((const char *)filterdir, 0666) != 0) { + scopeLogError("ERROR: mkdir %s:%d %d", __FUNCTION__, __LINE__, scope_errno); scope_free(filterdir); break; } // mount the merged dir addition into the host FS scopeLogError("%s:%d mount 1 %s", __FUNCTION__, __LINE__, filterdir); - if (g_fn.mount("/opt/scope", filterdir, "overlay", MS_BIND, NULL) != 0) { + if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { scope_free(filterdir); scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); break; @@ -1378,21 +1378,24 @@ c_syscall(char *sys_stack, char *g_stack) filterdir[targetlen + 1] = '\0'; scope_strcat(filterdir, "etc/ld.so.preload"); scopeLogError("%s:%d open %s", __FUNCTION__, __LINE__, filterdir); - if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT, 0666)) == -1) { + if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { scopeLogError("ERROR: open %s:%d", __FUNCTION__, __LINE__); scope_free(filterdir); break; } - scope_close(ldfd); - - // mount merged/etc/ld.so.preload from the filter dir - scopeLogError("%s:%d mount 2 %s", __FUNCTION__, __LINE__, filterdir); - if (g_fn.mount("/opt/scope/ld.so.preload", filterdir, "overlay", MS_BIND, NULL) != 0) { - scope_free(filterdir); - scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); - break; + size_t liblen = strlen(g_libpath); + char libpath[liblen + sizeof("/libscope.so")]; + scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); + scope_strncpy(libpath, g_libpath, liblen); + scopeLogError("%s:%d %ld %s %s", __FUNCTION__, __LINE__, liblen, g_libpath, libpath); + scope_strcat(libpath, "/libscope.so"); + scopeLogError("%s:%d %s", __FUNCTION__, __LINE__, libpath); + if (scope_write(ldfd, libpath, sizeof(libpath)) <= 0) { + scopeLogError("ERROR: write %s:%d", __FUNCTION__, __LINE__); } + + scope_close(ldfd); scope_free(filterdir); break; } From d3828c5c1830522ca7fc7e0a7067be73cb97303b Mon Sep 17 00:00:00 2001 From: Donn Date: Thu, 13 Apr 2023 16:25:01 -0500 Subject: [PATCH 03/25] Resolve an issue where subsequent docker run commands did not mount. Still a WIP. --- src/wrap_go.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index 8d700f1e6..fd6846d3f 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1336,8 +1336,8 @@ c_syscall(char *sys_stack, char *g_stack) char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); - once++; - funcprint("Scope: mount of %s %s %s\n", src, target, fstype); + if (scope_strstr(fstype, "overlay")) once++; + scopeLogError("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); /* * Checks: @@ -1354,13 +1354,13 @@ c_syscall(char *sys_stack, char *g_stack) size_t targetlen = scope_strlen(target); scopeLogError("Interposed Mount overlay: %s %d\n", g_proc.procname, once); + once = 0; if ((filterdir = scope_malloc(targetlen + 128)) == NULL) break; scope_strcpy(filterdir, target); scope_strcat(filterdir, g_libpath); // make a dir in the merged dir - scopeLogError("%s:%d mkdir %s", __FUNCTION__, __LINE__, filterdir); if (makeIntermediateDirs((const char *)filterdir, 0666) != 0) { scopeLogError("ERROR: mkdir %s:%d %d", __FUNCTION__, __LINE__, scope_errno); scope_free(filterdir); @@ -1368,7 +1368,6 @@ c_syscall(char *sys_stack, char *g_stack) } // mount the merged dir addition into the host FS - scopeLogError("%s:%d mount 1 %s", __FUNCTION__, __LINE__, filterdir); if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { scope_free(filterdir); scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); @@ -1377,7 +1376,7 @@ c_syscall(char *sys_stack, char *g_stack) filterdir[targetlen + 1] = '\0'; scope_strcat(filterdir, "etc/ld.so.preload"); - scopeLogError("%s:%d open %s", __FUNCTION__, __LINE__, filterdir); + if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { scopeLogError("ERROR: open %s:%d", __FUNCTION__, __LINE__); scope_free(filterdir); @@ -1386,11 +1385,11 @@ c_syscall(char *sys_stack, char *g_stack) size_t liblen = strlen(g_libpath); char libpath[liblen + sizeof("/libscope.so")]; + scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); scope_strncpy(libpath, g_libpath, liblen); - scopeLogError("%s:%d %ld %s %s", __FUNCTION__, __LINE__, liblen, g_libpath, libpath); scope_strcat(libpath, "/libscope.so"); - scopeLogError("%s:%d %s", __FUNCTION__, __LINE__, libpath); + if (scope_write(ldfd, libpath, sizeof(libpath)) <= 0) { scopeLogError("ERROR: write %s:%d", __FUNCTION__, __LINE__); } From e5bc5223a6f1056146bcc5f3cf738f51596e39bf Mon Sep 17 00:00:00 2001 From: Donn Date: Tue, 18 Apr 2023 15:53:01 -0500 Subject: [PATCH 04/25] Mount the Edge socket from within a container. --- src/transport.c | 32 +-------- src/utils.c | 32 +++++++++ src/utils.h | 6 +- src/wrap_go.c | 172 ++++++++++++++++++++++++++++++------------------ 4 files changed, 145 insertions(+), 97 deletions(-) diff --git a/src/transport.c b/src/transport.c index 746992967..3913a0646 100644 --- a/src/transport.c +++ b/src/transport.c @@ -21,6 +21,7 @@ #include "scopestdlib.h" #include "fn.h" #include "transport.h" +#include "utils.h" // Yuck. Avoids naming conflict between our src/wrap.c and libssl.a #define SSL_read SCOPE_SSL_read @@ -1018,37 +1019,6 @@ transportConnectFile(transport_t *t) return 1; } -#define EDGE_PATH_DOCKER "/var/run/appscope/appscope.sock" -#define EDGE_PATH_DEFAULT "/opt/cribl/state/appscope.sock" -#define READ_AND_WRITE (R_OK|W_OK) -static char* -edgePath(void){ - // 1) If EDGE_PATH_DOCKER can be accessed, return that. - if (scope_access(EDGE_PATH_DOCKER, READ_AND_WRITE) == 0) { - return scope_strdup(EDGE_PATH_DOCKER); - } - - // 2) If CRIBL_HOME is defined and can be accessed, - // return $CRIBL_HOME/state/appscope.sock - const char *cribl_home = getenv("CRIBL_HOME"); - if (cribl_home) { - char *new_path = NULL; - if (scope_asprintf(&new_path, "%s/%s", cribl_home, "state/appscope.sock") > 0) { - if (scope_access(new_path, READ_AND_WRITE) == 0) { - return new_path; - } - scope_free(new_path); - } - } - - // 3) If EDGE_PATH_DEFAULT can be accessed, return it - if (scope_access(EDGE_PATH_DEFAULT, READ_AND_WRITE) == 0) { - return scope_strdup(EDGE_PATH_DEFAULT); - } - - return NULL; -} - int transportConnect(transport_t *trans) { diff --git a/src/utils.c b/src/utils.c index 75b79dfdf..2c595c092 100644 --- a/src/utils.c +++ b/src/utils.c @@ -564,3 +564,35 @@ makeIntermediateDirs(const char *path, mode_t mode) { scope_free(dup_path); return 0; } + +#define EDGE_PATH_DOCKER "/var/run/appscope/appscope.sock" +#define EDGE_PATH_DEFAULT "/opt/cribl/state/appscope.sock" +#define READ_AND_WRITE (R_OK|W_OK) + +char * +edgePath(void) { + // 1) If EDGE_PATH_DOCKER can be accessed, return that. + if (scope_access(EDGE_PATH_DOCKER, READ_AND_WRITE) == 0) { + return scope_strdup(EDGE_PATH_DOCKER); + } + + // 2) If CRIBL_HOME is defined and can be accessed, + // return $CRIBL_HOME/state/appscope.sock + const char *cribl_home = getenv("CRIBL_HOME"); + if (cribl_home) { + char *new_path = NULL; + if (scope_asprintf(&new_path, "%s/%s", cribl_home, "state/appscope.sock") > 0) { + if (scope_access(new_path, READ_AND_WRITE) == 0) { + return new_path; + } + scope_free(new_path); + } + } + + // 3) If EDGE_PATH_DEFAULT can be accessed, return it + if (scope_access(EDGE_PATH_DEFAULT, READ_AND_WRITE) == 0) { + return scope_strdup(EDGE_PATH_DEFAULT); + } + + return NULL; +} diff --git a/src/utils.h b/src/utils.h index 4b3fc31fc..6b17019f7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -23,12 +23,14 @@ int endsWith(const char *, const char *); void setUUID(char *); void setMachineID(char *); +char *edgePath(void); +int makeIntermediateDirs(const char *, mode_t); +const char *libVersion(const char *); + // Signal Safe API int sigSafeNanosleep(const struct timespec *); void sigSafeUtoa(unsigned long, char *, int, int *); bool sigSafeMkdirRecursive(const char *); ssize_t sigSafeWriteNumber(int , long, int); -const char *libVersion(const char *); -int makeIntermediateDirs(const char *, mode_t); #endif // __UTILS_H__ diff --git a/src/wrap_go.c b/src/wrap_go.c index fd6846d3f..dcf611c33 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1234,6 +1234,111 @@ getFDFromConn(uint64_t tcpConn) { return -1; } +/* + * Create bind mounts in the overlay files within a container. + * Provides access to the library, filter, config, CLI and + * the Edge Unix socket in a container. + * + * Update ld.so.preload such that all procs load the library. + * Initailly intended for dockerd. + */ +bool +mountDirs(char *src, char *target, char *fstype) +{ + static int once = 0; + + if (scope_strstr(fstype, "overlay")) once++; + funcprint("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); + + /* + * Checks: + * only applies to the dockerd proc + * ensure the constructor populated a path + * proceed only after container mounts are done + */ + if ((src != NULL) && (target != NULL) && + scope_strstr(g_proc.procname, "dockerd") && + (scope_strstr(g_libpath, "/")) && + (once == 3)) { + int ldfd; + char *filterdir; + size_t targetlen = scope_strlen(target); + + funcprint("Interposed Mount overlay: %s %d\n", g_proc.procname, once); + once = 0; + + if ((filterdir = scope_malloc(targetlen + 128)) == NULL) return FALSE; + scope_strcpy(filterdir, target); + scope_strcat(filterdir, g_libpath); + + // make a dir in the merged dir + if (makeIntermediateDirs((const char *)filterdir, 0666) != 0) { + scopeLogWarn("Warn: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); + scope_free(filterdir); + return FALSE; + } + + // mount the merged dir addition into the host FS + if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { + scope_free(filterdir); + scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); + return FALSE; + } + + // mount the Edge socket path if it exists + char *sockpath = edgePath(); + if (sockpath) { + char *sockdir = scope_dirname(sockpath); + filterdir[targetlen + 1] = '\0'; + scope_strcat(filterdir, sockdir); + + // make the socket dir in the merged dir + if (makeIntermediateDirs((const char *)filterdir, 0666) == 0) { + // mount the Edge socket + if (g_fn.mount(sockdir, filterdir, "overlay", MS_BIND, NULL) != 0) { + scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); + } + } else { + scopeLogWarn("WARN: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); + } + + scope_free(sockpath); + } else { + scopeLogInfo("Note: can't resolve the Edge socket path %s:%d", __FUNCTION__, __LINE__); + } + + // write to /etc/ld.so.preload, not a mount + filterdir[targetlen + 1] = '\0'; + scope_strcat(filterdir, "etc/ld.so.preload"); + + if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { + scopeLogWarn("WARN: open %s:%d", __FUNCTION__, __LINE__); + scope_free(filterdir); + return FALSE; + } + + size_t liblen = strlen(g_libpath); + char libpath[liblen + sizeof("/libscope.so")]; + + scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); + scope_strncpy(libpath, g_libpath, liblen); + scope_strcat(libpath, "/libscope.so"); + + if (scope_write(ldfd, libpath, sizeof(libpath)) <= 0) { + scopeLogWarn("WARN: write %s:%d", __FUNCTION__, __LINE__); + scope_close(ldfd); + scope_free(filterdir); + return FALSE; + } + + scope_close(ldfd); + scope_free(filterdir); + } + + return TRUE; +} + + // Extract data from syscalls. Values are available in registers saved on sys_stack. static void c_syscall(char *sys_stack, char *g_stack) @@ -1241,7 +1346,6 @@ c_syscall(char *sys_stack, char *g_stack) uint64_t syscall_num = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_num); int64_t rc = *(int64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_rc); if(rc < 0) rc = -1; // kernel syscalls can return values < -1 - static int once = 0; switch(syscall_num) { case SYS_write: @@ -1336,70 +1440,10 @@ c_syscall(char *sys_stack, char *g_stack) char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); - if (scope_strstr(fstype, "overlay")) once++; - scopeLogError("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); - - /* - * Checks: - * only applies to the dockerd proc - * ensure the constructor populated a path - * proceed after container mounts are done - */ - if ((src != NULL) && (target != NULL) && - scope_strstr(g_proc.procname, "dockerd") && - (scope_strstr(g_libpath, "/")) && - (once == 3)) { - int ldfd; - char *filterdir; - size_t targetlen = scope_strlen(target); - - scopeLogError("Interposed Mount overlay: %s %d\n", g_proc.procname, once); - once = 0; - - if ((filterdir = scope_malloc(targetlen + 128)) == NULL) break; - scope_strcpy(filterdir, target); - scope_strcat(filterdir, g_libpath); - - // make a dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) != 0) { - scopeLogError("ERROR: mkdir %s:%d %d", __FUNCTION__, __LINE__, scope_errno); - scope_free(filterdir); - break; - } - - // mount the merged dir addition into the host FS - if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { - scope_free(filterdir); - scopeLogError("ERROR: mount %s:%d", __FUNCTION__, __LINE__); - break; - } - - filterdir[targetlen + 1] = '\0'; - scope_strcat(filterdir, "etc/ld.so.preload"); - - if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { - scopeLogError("ERROR: open %s:%d", __FUNCTION__, __LINE__); - scope_free(filterdir); - break; - } - - size_t liblen = strlen(g_libpath); - char libpath[liblen + sizeof("/libscope.so")]; - - scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); - scope_strncpy(libpath, g_libpath, liblen); - scope_strcat(libpath, "/libscope.so"); - - if (scope_write(ldfd, libpath, sizeof(libpath)) <= 0) { - scopeLogError("ERROR: write %s:%d", __FUNCTION__, __LINE__); - } - - scope_close(ldfd); - scope_free(filterdir); - break; - } - } + mountDirs(src, target, fstype); break; + } + default: break; } From c836ad949b6527ff2a4f502b4359c45a4c43f096 Mon Sep 17 00:00:00 2001 From: Donn Date: Wed, 19 Apr 2023 16:22:33 -0500 Subject: [PATCH 05/25] Support the case where some Go execs add the interface version, .abi0, to a symbol name. --- src/wrap_go.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index 5fc509172..c7fba4acc 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -367,7 +367,7 @@ adjustGoStructOffsetsForVersion() scopeLogWarn("Architecture not supported. Not adjusting schema offsets."); return; } - tap_entry(tap_syscall)->func_name = "runtime/internal/syscall.Syscall6.abi0"; + tap_entry(tap_syscall)->func_name = "runtime/internal/syscall.Syscall6"; tap_entry(tap_rawsyscall)->func_name = ""; tap_entry(tap_syscall6)->func_name = ""; } @@ -922,6 +922,39 @@ go_version_numbers(const char *go_runtime_version) g_go_maint_ver = maint_val; } +/* + * Some Go executables are built such that the .abi0 + * extension is used for internal runtime symbols. Others + * emit symbols without the version extension. So, if we + * can't resolve the symbol without a version extension, we + * try the symbol with extension. + */ +void * +tryAbi0(const char *buf, char *sname) +{ + if (!buf || !sname) return NULL; + + size_t slen = scope_strlen(sname); + if (slen == 0) return NULL; + + void *funcaddr = NULL; + char abi0name[slen + sizeof(".abi0 ")]; + + scope_memset(abi0name, 0, sizeof(abi0name)); + scope_strncpy(abi0name, sname, slen); + scope_strcat(abi0name, ".abi0"); + + funcprint("%s:%d %s (%ld) %s\n", __FUNCTION__, __LINE__, sname, slen, abi0name); + + // Look for the symbol in the elf strtab, then meta data; .gopclntab section + if (((funcaddr = getSymbol(buf, abi0name)) == 0) && + ((funcaddr = getGoSymbol(buf, abi0name, NULL, NULL)) == 0)) { + return NULL; + } + + return funcaddr; +} + void initGoHook(elf_buf_t *ebuf) { @@ -1062,8 +1095,10 @@ initGoHook(elf_buf_t *ebuf) void *orig_func; // Look for the symbol in the ELF symbol table if (((orig_func = getSymbol(ebuf->buf, tap->func_name)) == NULL) && - // Otherwise look in the .gopclntab section - ((orig_func = getGoSymbol(ebuf->buf, tap->func_name, NULL, NULL)) == NULL)) { + // look in the .gopclntab section + ((orig_func = getGoSymbol(ebuf->buf, tap->func_name, NULL, NULL)) == NULL) && + // is the symbol defined as an original API; with an abi0 extension + ((orig_func = tryAbi0(ebuf->buf, tap->func_name)) == NULL)) { sysprint("WARN: can't get the address for %s\n", tap->func_name); continue; } @@ -1232,7 +1267,7 @@ mountDirs(char *src, char *target, char *fstype) char *filterdir; size_t targetlen = scope_strlen(target); - funcprint("Interposed Mount overlay: %s %d\n", g_proc.procname, once); + sysprint("Interposed Mount overlay: %s %d\n", g_proc.procname, once); once = 0; if ((filterdir = scope_malloc(targetlen + 128)) == NULL) return FALSE; From 8262c1266170530e31c9ae10ba71b74d5d108b11 Mon Sep 17 00:00:00 2001 From: Donn Date: Wed, 19 Apr 2023 16:39:18 -0500 Subject: [PATCH 06/25] Applied review comments. --- src/utils.c | 10 +++++----- src/utils.h | 2 +- src/wrap_go.c | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/utils.c b/src/utils.c index 2c595c092..4be46f3c5 100644 --- a/src/utils.c +++ b/src/utils.c @@ -531,11 +531,11 @@ libVersion(const char *version) { * The mkdir system call does not support the creation of intermediate dirs. * This will walk the path and create all dirs one at a time. */ -int +bool makeIntermediateDirs(const char *path, mode_t mode) { char *dup_path = scope_strdup(path); if (!dup_path) { - return -1; + return FALSE; } char *curr = dup_path; @@ -546,7 +546,7 @@ makeIntermediateDirs(const char *path, mode_t mode) { if (scope_mkdir(dup_path, mode) == -1) { if (scope_errno != EEXIST) { scope_free(dup_path); - return -1; + return FALSE; } } @@ -557,12 +557,12 @@ makeIntermediateDirs(const char *path, mode_t mode) { if (scope_mkdir(dup_path, mode) == -1) { if (scope_errno != EEXIST) { scope_free(dup_path); - return -1; + return FALSE; } } scope_free(dup_path); - return 0; + return TRUE; } #define EDGE_PATH_DOCKER "/var/run/appscope/appscope.sock" diff --git a/src/utils.h b/src/utils.h index 6b17019f7..20d977621 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,7 +24,7 @@ void setUUID(char *); void setMachineID(char *); char *edgePath(void); -int makeIntermediateDirs(const char *, mode_t); +bool makeIntermediateDirs(const char *, mode_t); const char *libVersion(const char *); // Signal Safe API diff --git a/src/wrap_go.c b/src/wrap_go.c index c7fba4acc..1508bfaba 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1245,7 +1245,7 @@ getFDFromConn(uint64_t tcpConn) { * Update ld.so.preload such that all procs load the library. * Initailly intended for dockerd. */ -bool +static bool mountDirs(char *src, char *target, char *fstype) { static int once = 0; @@ -1275,7 +1275,7 @@ mountDirs(char *src, char *target, char *fstype) scope_strcat(filterdir, g_libpath); // make a dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) != 0) { + if (makeIntermediateDirs((const char *)filterdir, 0666) == FALSE) { scopeLogWarn("Warn: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); scope_free(filterdir); return FALSE; @@ -1283,8 +1283,8 @@ mountDirs(char *src, char *target, char *fstype) // mount the merged dir addition into the host FS if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { - scope_free(filterdir); scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); + scope_free(filterdir); return FALSE; } @@ -1296,7 +1296,7 @@ mountDirs(char *src, char *target, char *fstype) scope_strcat(filterdir, sockdir); // make the socket dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) == 0) { + if (makeIntermediateDirs((const char *)filterdir, 0666) == TRUE) { // mount the Edge socket if (g_fn.mount(sockdir, filterdir, "overlay", MS_BIND, NULL) != 0) { scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); @@ -1320,7 +1320,7 @@ mountDirs(char *src, char *target, char *fstype) return FALSE; } - size_t liblen = strlen(g_libpath); + size_t liblen = scope_strlen(g_libpath); char libpath[liblen + sizeof("/libscope.so")]; scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); From 84f58efee28ccebe2e33a6a03cbc5a9a894f0fb0 Mon Sep 17 00:00:00 2001 From: Donn Date: Thu, 20 Apr 2023 14:37:51 -0500 Subject: [PATCH 07/25] Use the internal libc for mount. --- contrib/redefine_syms.lst | 1 + src/fn.c | 1 - src/fn.h | 1 - src/gocontext.S | 2 ++ src/scopestdlib.c | 8 ++++++++ src/scopestdlib.h | 1 + src/wrap_go.c | 4 ++-- 7 files changed, 14 insertions(+), 4 deletions(-) diff --git a/contrib/redefine_syms.lst b/contrib/redefine_syms.lst index 78bb8d80a..d33a64692 100644 --- a/contrib/redefine_syms.lst +++ b/contrib/redefine_syms.lst @@ -117,3 +117,4 @@ usleep scopelibc_usleep vfprintf scopelibc_vfprintf vsnprintf scopelibc_vsnprintf write scopelibc_write +mount scopelibc_mount diff --git a/src/fn.c b/src/fn.c index 8160f0fdb..19af7331c 100644 --- a/src/fn.c +++ b/src/fn.c @@ -333,7 +333,6 @@ initFn(void) GETADDR(g_fn.closedir, "closedir"); GETADDR(g_fn.readdir, "readdir"); GETADDR(g_fn.setrlimit, "setrlimit"); - GETADDR(g_fn.mount, "mount"); #ifdef __STATX__ GETADDR(g_fn.statx, "statx"); #endif diff --git a/src/fn.h b/src/fn.h index a25ba9644..de5ddce13 100644 --- a/src/fn.h +++ b/src/fn.h @@ -216,7 +216,6 @@ typedef struct { void *(*__memcpy_chk)(void *, const void *, size_t, size_t); int (*__sprintf_chk)(char *, int, size_t, const char *, ...); long int (*__fdelt_chk)(long int); - int (*mount)(const char *, const char *, const char *, unsigned long, const void *); #ifdef __linux__ // Couldn't easily get struct definitions for these on mac int (*statvfs64)(const char *, struct statvfs64 *); diff --git a/src/gocontext.S b/src/gocontext.S index 6dbf52f41..2831b25f5 100644 --- a/src/gocontext.S +++ b/src/gocontext.S @@ -96,6 +96,8 @@ je 2f cmp $SYS_close, %eax je 2f + cmp $SYS_mount, %eax + je 2f lea \retptr@GOTPCREL(%rip), %r13 mov (%r13), %r13 diff --git a/src/scopestdlib.c b/src/scopestdlib.c index e61f7ca72..e8787066a 100644 --- a/src/scopestdlib.c +++ b/src/scopestdlib.c @@ -77,6 +77,7 @@ extern int scopelibc_rename(const char *, const char *); extern int scopelibc_remove(const char *); extern int scopelibc_pipe2(int [2], int); extern void scopelibc_setbuf(FILE *, char *); +extern int scopelibc_mount(const char *, const char *, const char *, unsigned long, const void *); // String handling operations extern char * scopelibc_realpath(const char *, char *); @@ -518,6 +519,13 @@ scope_mkdir(const char *pathname, mode_t mode) { return scopelibc_mkdir(pathname, mode); } +int +scope_mount(const char *source, const char *target, + const char *fstype, unsigned long flags, + const void *data) { + return scopelibc_mount(source, target, fstype, flags, data); +} + int scope_chdir(const char *path) { return scopelibc_chdir(path); diff --git a/src/scopestdlib.h b/src/scopestdlib.h index 5c58aa684..9193ec3ce 100644 --- a/src/scopestdlib.h +++ b/src/scopestdlib.h @@ -162,6 +162,7 @@ int scope_fileno(FILE *); int scope_flock(int, int); int scope_fstat(int, struct stat *); int scope_mkdir(const char *, mode_t); +int scope_mount(const char *, const char *, const char *, unsigned long, const void *); int scope_chdir(const char *); int scope_rmdir(const char *); char* scope_get_current_dir_name(void); diff --git a/src/wrap_go.c b/src/wrap_go.c index 1508bfaba..f2ee60c83 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1282,7 +1282,7 @@ mountDirs(char *src, char *target, char *fstype) } // mount the merged dir addition into the host FS - if (g_fn.mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { + if (scope_mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); scope_free(filterdir); return FALSE; @@ -1298,7 +1298,7 @@ mountDirs(char *src, char *target, char *fstype) // make the socket dir in the merged dir if (makeIntermediateDirs((const char *)filterdir, 0666) == TRUE) { // mount the Edge socket - if (g_fn.mount(sockdir, filterdir, "overlay", MS_BIND, NULL) != 0) { + if (scope_mount(sockdir, filterdir, "overlay", MS_BIND, NULL) != 0) { scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); } } else { From 365ab72a423bc61ebc7de0aad5f3a849c01b882e Mon Sep 17 00:00:00 2001 From: Donn Date: Mon, 8 May 2023 16:12:11 +0000 Subject: [PATCH 08/25] Resolved an issue with a unit test. --- test/unit/library/utilstest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/library/utilstest.c b/test/unit/library/utilstest.c index f1e6b2cba..32f23705a 100644 --- a/test/unit/library/utilstest.c +++ b/test/unit/library/utilstest.c @@ -2,9 +2,10 @@ #include #include #include +#include + #include "utils.h" #include "scopestdlib.h" - #include "fn.h" #include "test.h" From add7847c0dc2405f87cc9ecd60e828617024da7a Mon Sep 17 00:00:00 2001 From: Donn Date: Mon, 8 May 2023 19:31:20 +0000 Subject: [PATCH 09/25] Minor updates intended for clarity. --- src/wrap_go.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index 052c0ce18..87b42e709 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -31,6 +31,7 @@ #define PRI_STR_LEN sizeof(PRI_STR) #define UNDEF_OFFSET (-1) #define EXIT_STACK_SIZE (32 * 1024) +#define DOCKERD_AFTER_OVERLAY_MOUNTS 3 enum go_arch_t { X86_64, @@ -915,7 +916,7 @@ go_version_numbers(const char *go_runtime_version) * can't resolve the symbol without a version extension, we * try the symbol with extension. */ -void * +static void * tryAbi0(const char *buf, char *sname) { if (!buf || !sname) return NULL; @@ -1226,7 +1227,7 @@ getFDFromConn(uint64_t tcpConn) { * the Edge Unix socket in a container. * * Update ld.so.preload such that all procs load the library. - * Initailly intended for dockerd. + * Initially intended for dockerd. */ static bool mountDirs(char *src, char *target, char *fstype) @@ -1241,11 +1242,16 @@ mountDirs(char *src, char *target, char *fstype) * only applies to the dockerd proc * ensure the constructor populated a path * proceed only after container mounts are done + * + * We need to apply auto mounts after the overlay mounts are performed. + * Given that there are 3 mounts that encompass the overlays, we count + * the mounts and apply auto mounts at that point. The constant + * DOCKERD_AFTER_OVERLAY_MOUNTS defines the overlay activity. */ if ((src != NULL) && (target != NULL) && scope_strstr(g_proc.procname, "dockerd") && (scope_strstr(g_libpath, "/")) && - (once == 3)) { + (once == DOCKERD_AFTER_OVERLAY_MOUNTS)) { int ldfd; char *filterdir; size_t targetlen = scope_strlen(target); From 20a69f1f118fb48137aaad1a3023086bf56350e5 Mon Sep 17 00:00:00 2001 From: Donn Date: Tue, 9 May 2023 22:19:03 +0000 Subject: [PATCH 10/25] WIP: startAutoMount() works, debug needs to be removed and the function needs to be moved to the new filter command. --- cli/start/start.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/cli/start/start.go b/cli/start/start.go index fd93d929f..232d2cf13 100644 --- a/cli/start/start.go +++ b/cli/start/start.go @@ -1,12 +1,16 @@ package start import ( + "bufio" "errors" "fmt" "io" "os" "os/exec" + "path" "strconv" + "strings" + "syscall" "github.com/criblio/scope/libscope" "github.com/criblio/scope/loader" @@ -164,6 +168,70 @@ func startConfigureHost(filename string) error { return nil } +// for all containers +func startAutoMount(cPid int) error { + fmt.Println("startAutoMount: ", cPid) + mnt, err := os.Open(fmt.Sprintf("/proc/%d/mounts", cPid)) + if err != nil { + log.Warn(). + Err(err). + Str("pid", strconv.Itoa(cPid)). + Msgf("Can't read mount file for %v.", cPid) + return err + } + reader := bufio.NewReader(mnt) + for { + // Read a line per mount + mline, err := reader.ReadString('\n') + if err != nil { + log.Warn(). + Err(err). + Str("pid", strconv.Itoa(cPid)). + Msgf("Can't read mount file for %v.", cPid) + return err + } + + if strings.Contains(mline, "workdir") == true { + starti := strings.Index(mline, "workdir=") + starti += len("workdir=") + fmt.Println(mline[starti:]) + strstart := mline[starti:] + endi := strings.Index(strstart, "/work") + overlay := strstart[:endi] + fmt.Println(overlay) + + verdir, err := getAppScopeVerDir() + if err != nil { + log.Warn(). + Err(err). + Msgf("Can't get scope dir") + continue + } + + mpath := path.Join(overlay, "merged", verdir) + fmt.Println(mpath) + + if err = os.MkdirAll(mpath, 0755); err != nil { + log.Warn(). + Err(err). + Str("mpath", mpath). + Msgf("mkdir failed for %v.", mpath) + continue + } + + if err = syscall.Mount(verdir, mpath, "", syscall.MS_BIND, ""); err != nil { + log.Warn(). + Err(err). + Str("path", mpath). + Msgf("Bind mount failed for %v", mpath) + continue + } + break + } + } + return nil +} + // for all containers func startConfigureContainers(filename string, cPids []int) error { // Iterate over all containers @@ -284,6 +352,13 @@ func Start() error { return err } + // ############################ + cMypids := getContainersPids() + for _, cThisPid := range cMypids { + startAutoMount(cThisPid) + return nil + } + cfgData := getStartData() if len(cfgData) == 0 { log.Error(). From 2b62807ea867c01c56070d42b4c75df4704cf7af Mon Sep 17 00:00:00 2001 From: Donn Date: Fri, 12 May 2023 22:03:00 +0000 Subject: [PATCH 11/25] WIP: Auto mount from libscope. --- src/sysexec.c | 23 ------ src/utils.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 3 + src/wrap.c | 3 + 4 files changed, 217 insertions(+), 23 deletions(-) diff --git a/src/sysexec.c b/src/sysexec.c index e47c0951a..a9fd3abbb 100644 --- a/src/sysexec.c +++ b/src/sysexec.c @@ -24,8 +24,6 @@ #include "state.h" #include "gocontext.h" -#define SYSPRINT_CONSOLE 0 -#define PRINT_BUF_SIZE 1024 #define HEAP_SIZE (size_t)(500 * 1024) // 1Mb + an 8kb guard #define STACK_SIZE (size_t)(1024 * 1024) + (8 * 1024) @@ -40,27 +38,6 @@ #define EXPORTON __attribute__((visibility("default"))) -void -sysprint(const char* fmt, ...) -{ - // Create the string - char str[PRINT_BUF_SIZE]; - - if (fmt) { - va_list args; - va_start(args, fmt); - int rv = scope_vsnprintf(str, PRINT_BUF_SIZE, fmt, args); - va_end(args); - if (rv == -1) return; - } - - // Output the string -#if SYSPRINT_CONSOLE > 0 - scope_printf("%s", str); -#endif - scopeLog(CFG_LOG_DEBUG, "%s", str); -} - static int get_file_size(const char *path) { diff --git a/src/utils.c b/src/utils.c index 4be46f3c5..7ccac6904 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "scopestdlib.h" #include "utils.h" @@ -11,7 +12,10 @@ #include "dbg.h" #include "runtimecfg.h" #include "plattime.h" +#include "scopeelf.h" +#define SYSPRINT_CONSOLE 1 +#define PRINT_BUF_SIZE 1024 #define MAC_ADDR_LEN 17 #define ZERO_MACHINE_ID "00000000000000000000000000000000" @@ -29,6 +33,27 @@ static int getMacAddr(char *string); rtconfig g_cfg = {0}; +void +sysprint(const char* fmt, ...) +{ + // Create the string + char str[PRINT_BUF_SIZE]; + + if (fmt) { + va_list args; + va_start(args, fmt); + int rv = scope_vsnprintf(str, PRINT_BUF_SIZE, fmt, args); + va_end(args); + if (rv == -1) return; + } + + // Output the string +#if SYSPRINT_CONSOLE > 0 + scope_printf("%s", str); +#endif + scopeLog(CFG_LOG_DEBUG, "%s", str); +} + unsigned int strToVal(enum_map_t map[], const char *str) { @@ -596,3 +621,189 @@ edgePath(void) { return NULL; } + +bool +createLdsoPreload(char *path) +{ + int lfd; + size_t mlen; + elf_buf_t *ebuf = NULL; + struct stat fstat; + char bpath[PATH_MAX], rpath[PATH_MAX]; + + scope_strncpy(bpath, path, strlen(path) + 1); + scope_strcat(bpath, "/bin/cat"); + mlen = scope_strlen(path); + + if (lstat(bpath, &fstat) == -1) { + scopeLogWarn("lstat of %s in %s:%d", bpath, __FUNCTION__, __LINE__); + return FALSE; + } + + if (S_ISLNK(fstat.st_mode)) { + // musl based fs is a sym link to busybox + if (scope_readlink(bpath, rpath, PATH_MAX - 1) == -1) { + scopeLogWarn("sym link of %s in %s:%d", bpath, __FUNCTION__, __LINE__); + return FALSE; + } + bpath[mlen + 1] = '\0'; + scope_strcat(bpath, rpath); + } + + if ((ebuf = getElf(bpath)) == NULL) { + scopeLogWarn("get ELF of %s in %s:%d", bpath, __FUNCTION__, __LINE__); + return FALSE; + } + + // write to /etc/ld.so.preload + bpath[mlen + 1] = '\0'; + scope_strcat(bpath, "/etc/ld.so.preload"); + + if ((lfd = scope_open(bpath, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { + scopeLogWarn("open of %s in %s:%d", bpath, __FUNCTION__, __LINE__); + free(ebuf); + return FALSE; + } + + scope_memset(rpath, 0, PATH_MAX); + scope_strncpy(rpath, g_libpath, scope_strlen(g_libpath)); + if (is_musl(ebuf->buf)) { + scope_strcat(rpath, "/libscope.so.musl"); + } else { + scope_strcat(rpath, "/libscope.so.glibc"); + } + + if (scope_write(lfd, rpath, scope_strlen(rpath) + 1) <= 0) { + scopeLogWarn("write to %s in %s:%d", rpath, __FUNCTION__, __LINE__); + scope_close(lfd); + scope_free(ebuf); + return FALSE; + } + + scope_close(lfd); + scope_free(ebuf); + return TRUE; +} + +char * +getMountPath(pid_t pid) +{ + bool candidate = FALSE; + size_t len; + char *buf = NULL, *mount = NULL; + char path[PATH_MAX]; + + scope_snprintf(path, sizeof(path), "/proc/%d/mounts", pid); + FILE *fstream = scope_fopen(path, "r"); + if (fstream == NULL) return NULL; + + while (scope_getline(&buf, &len, fstream) != -1) { + // if a docker overlay mount and not already mounted; appscope + if ((scope_strstr(buf, "overlay")) && + (scope_strstr(buf, "docker"))) { + char *start, *end; + if (((start = scope_strstr(buf, "workdir="))) && + ((end = scope_strstr(buf, "/work")))) { + start += scope_strlen("workdir="); + *end = '\0'; + scope_strcat(start, "/merged"); + mount = scope_strdup(start); + candidate = TRUE; + } + } + + // no longer a candidate as we've already mounted this proc + if (scope_strstr(buf, "appscope")) candidate = FALSE; + + scope_free(buf); + buf = NULL; + len = 0; + } + + if (buf) scope_free(buf); + scope_fclose(fstream); + + if (candidate == TRUE) return mount; + return NULL; +} + +bool +mountCDirs(char *target, char *fstype) +{ + char *filterdir; + size_t targetlen = scope_strlen(target); + + if ((filterdir = scope_malloc(targetlen + 128)) == NULL) return FALSE; + scope_strcpy(filterdir, target); + scope_strcat(filterdir, g_libpath); + + // make a dir in the merged dir + if (makeIntermediateDirs((const char *)filterdir, 0666) == FALSE) { + scopeLogWarn("Warn: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); + scope_free(filterdir); + return FALSE; + } + + // mount the merged dir addition into the host FS + if (scope_mount(g_libpath, filterdir, fstype, MS_BIND, NULL) != 0) { + scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); + scope_free(filterdir); + return FALSE; + } +#if 0 // TBD + // mount the Edge socket path if it exists + char *sockpath = edgePath(); + if (sockpath) { + char *sockdir = scope_dirname(sockpath); + filterdir[targetlen + 1] = '\0'; + scope_strcat(filterdir, sockdir); + + // make the socket dir in the merged dir + if (makeIntermediateDirs((const char *)filterdir, 0666) == TRUE) { + // mount the Edge socket + if (scope_mount(sockdir, filterdir, fstype, MS_BIND, NULL) != 0) { + scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); + } + } else { + scopeLogWarn("WARN: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); + } + + scope_free(sockpath); + } else { + scopeLogInfo("Note: can't resolve the Edge socket path %s:%d", __FUNCTION__, __LINE__); + } +#endif + return TRUE; +} + +bool +autoMount() +{ + DIR *dirp; + struct dirent *entry; + char *mpath = NULL; + + dirp = scope_opendir("/proc"); + if (dirp == NULL) { + scopeLogError("opendir"); + return FALSE; + } + + // Iterate all procs + while ((entry = scope_readdir(dirp)) != NULL) { + // procs/tasks are a dir + if (entry->d_type == DT_DIR) { + pid_t pid = scope_atoi(entry->d_name); + if (pid > 0) { + // if pid is in a supported container, get the requisite mount path + if ((mpath = getMountPath(pid)) != NULL) { + sysprint("%s:%d mount %s\n", __FUNCTION__, __LINE__, mpath); + mountCDirs(mpath, NULL); + createLdsoPreload(mpath); + } + } + } + } + + return TRUE; +} diff --git a/src/utils.h b/src/utils.h index 20d977621..afdf8c549 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,6 +26,9 @@ void setMachineID(char *); char *edgePath(void); bool makeIntermediateDirs(const char *, mode_t); const char *libVersion(const char *); +void sysprint(const char *, ...); +bool mountCDirs(char *, char *); +bool autoMount(void); // Signal Safe API int sigSafeNanosleep(const struct timespec *); diff --git a/src/wrap.c b/src/wrap.c index 4a3709a7b..487005ddd 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -1167,6 +1167,9 @@ periodic(void *arg) perf = checkEnv(PRESERVE_PERF_REPORTING, "true"); + // TBD: where to run this? + autoMount(); + while (1) { // we are trying to exit, do nothing if (g_exitdone == TRUE) { From da4389355677c7758f4e64d6672813c74e205209 Mon Sep 17 00:00:00 2001 From: Donn Date: Tue, 23 May 2023 18:02:39 +0000 Subject: [PATCH 12/25] Added interposition of forkExec in wrap_go.c. Prepping for applying updates to config.json. --- src/gocontext.S | 5 ++- src/gocontext.h | 2 + src/scopeelf.c | 50 ++++++++++++++++++++++ src/scopeelf.h | 1 + src/wrap.c | 3 -- src/wrap_go.c | 109 +++++++++++++++++++++++++++++++++++++++++------- 6 files changed, 151 insertions(+), 19 deletions(-) diff --git a/src/gocontext.S b/src/gocontext.S index 2831b25f5..8cc3bce91 100644 --- a/src/gocontext.S +++ b/src/gocontext.S @@ -96,8 +96,8 @@ je 2f cmp $SYS_close, %eax je 2f - cmp $SYS_mount, %eax - je 2f + //cmp $SYS_mount, %eax + //je 2f lea \retptr@GOTPCREL(%rip), %r13 mov (%r13), %r13 @@ -221,6 +221,7 @@ c_syscall_handler: interpose_system_stack go_hook_exit go_exit c_handler interpose_system_stack go_hook_die go_die c_handler interpose_system_stack go_hook_sighandler go_sighandler c_handler + interpose_system_stack go_hook_forkExec go_forkExec c_handler syscall_filter go_hook_reg_syscall hook_syscall g_syscall_return syscall_filter go_hook_reg_rawsyscall hook_rawsyscall g_rawsyscall_return diff --git a/src/gocontext.h b/src/gocontext.h index 225040a4a..949bbd17c 100644 --- a/src/gocontext.h +++ b/src/gocontext.h @@ -76,6 +76,7 @@ enum tap_id { tap_exit, tap_die, tap_sighandler, + tap_forkExec, tap_end, }; @@ -132,5 +133,6 @@ extern void go_hook_reg_http2_client_write(void); extern void go_hook_exit(void); extern void go_hook_die(void); extern void go_hook_sighandler(void); +extern void go_hook_forkExec(void); #endif // __GOTCONTEXT_H__ diff --git a/src/scopeelf.c b/src/scopeelf.c index d339e4d27..9b2c60b7d 100644 --- a/src/scopeelf.c +++ b/src/scopeelf.c @@ -301,6 +301,56 @@ getElfEntries(struct link_map *lm, Elf64_Rela **rel, Elf64_Sym **sym, char **str return 0; } +void * +getDynSymbol(const char *buf, char *sname) +{ + int i, nsyms = 0; + Elf64_Addr symaddr = 0; + Elf64_Ehdr *ehdr; + Elf64_Shdr *sections; + Elf64_Sym *symtab = NULL; + const char *section_strtab = NULL; + const char *strtab = NULL; + const char *sec_name = NULL; + + if (!buf || !sname) return NULL; + + ehdr = (Elf64_Ehdr *)buf; + sections = (Elf64_Shdr *)((char *)buf + ehdr->e_shoff); + section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + + for (i = 0; i < ehdr->e_shnum; i++) { + sec_name = section_strtab + sections[i].sh_name; + + if (sections[i].sh_type == SHT_DYNSYM) { + symtab = (Elf64_Sym *)((char *)buf + sections[i].sh_offset); + nsyms = sections[i].sh_size / sections[i].sh_entsize; + } else if (sections[i].sh_type == SHT_STRTAB && scope_strcmp(sec_name, ".dynstr") == 0) { + strtab = (const char *)(buf + sections[i].sh_offset); + } + + if ((strtab != NULL) && (symtab != NULL)) break; + scopeLogDebug("section %s type = %d flags = 0x%lx addr = 0x%lx-0x%lx, size = 0x%lx off = 0x%lx\n", + sec_name, + sections[i].sh_type, + sections[i].sh_flags, + sections[i].sh_addr, + sections[i].sh_addr + sections[i].sh_size, + sections[i].sh_size, + sections[i].sh_offset); + } + + for (i=0; i < nsyms; i++) { + if (scope_strcmp(sname, strtab + symtab[i].st_name) == 0) { + symaddr = symtab[i].st_value; + sysprint("symbol found %s = 0x%08lx\n", strtab + symtab[i].st_name, symtab[i].st_value); + break; + } + } + + return (void *)symaddr; +} + void * getSymbol(const char *buf, char *sname) { diff --git a/src/scopeelf.h b/src/scopeelf.h index 947f934e8..04b790b40 100644 --- a/src/scopeelf.h +++ b/src/scopeelf.h @@ -26,6 +26,7 @@ int doGotcha(struct link_map *, got_list_t *, Elf64_Rela *, Elf64_Sym *, char *, int getElfEntries(struct link_map *, Elf64_Rela **, Elf64_Sym **, char **, int *rsz); Elf64_Shdr* getElfSection(char *, const char *); void * getSymbol(const char *, char *); +void * getDynSymbol(const char *, char *); bool is_static(char *); bool is_go(char *); bool is_musl(char *); diff --git a/src/wrap.c b/src/wrap.c index 487005ddd..4a3709a7b 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -1167,9 +1167,6 @@ periodic(void *arg) perf = checkEnv(PRESERVE_PERF_REPORTING, "true"); - // TBD: where to run this? - autoMount(); - while (1) { // we are trying to exit, do nothing if (g_exitdone == TRUE) { diff --git a/src/wrap_go.c b/src/wrap_go.c index 87b42e709..74cb431f6 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -84,6 +84,8 @@ int g_go_maint_ver = UNKNOWN_GO_VER; int g_arch = ARCH; static char g_go_build_ver[7]; static char g_ReadFrame_addr[20]; +static bool valid_id = FALSE; +static char *id = NULL; go_schema_t *g_go_schema = &go_11_schema; // overridden if later version uint64_t g_glibc_guard = 0LL; uint64_t go_systemstack_switch; @@ -107,6 +109,7 @@ tap_t g_tap[] = { {tap_exit, "runtime.exit", /* .abi0 */ go_hook_exit, NULL, 0}, {tap_die, "runtime.dieFromSignal", /* .abi0 */ go_hook_die, NULL, 0}, {tap_sighandler, "runtime.sighandler", /* .abi0 */ go_hook_sighandler, NULL, 0}, + {tap_forkExec, "syscall.forkExec", go_hook_forkExec, NULL, 0}, {tap_end, "", NULL, NULL, 0}, }; @@ -421,10 +424,11 @@ createGoStructFile(void) { // longer needed. // Don't use go_str() for byte arrays. static char * -go_str(void *go_str) +go_str(void *go_str, bool force) { // Go 17 and higher use "c style" null terminated strings instead of a string and a length - if (g_go_minor_ver >= 17) { + + if ((g_go_minor_ver >= 17) && (force == FALSE)) { // We need to deference the address first before casting to a char * if (!go_str) return NULL; return (char *)*(uint64_t *)go_str; @@ -737,6 +741,7 @@ patch_addrs(funchook_t *funchook, // We also patch syscalls at the first (and last) instruction. if (i == 0 && ((tap->assembly_fn == go_hook_exit) || (tap->assembly_fn == go_hook_die) || + (tap->assembly_fn == go_hook_forkExec) || (tap->assembly_fn == go_hook_sighandler))) { // In this case we want to patch the instruction directly @@ -945,10 +950,14 @@ tryAbi0(const char *buf, char *sname) void initGoHook(elf_buf_t *ebuf) { + if (!ebuf || !ebuf->buf) return; + int rc; funchook_t *funchook; char *go_ver; char *go_runtime_version = NULL; + uint64_t base = 0LL; + //Elf64_Ehdr *ehdr = (Elf64_Ehdr *)ebuf->buf; funchook = funchook_create(); @@ -957,8 +966,9 @@ initGoHook(elf_buf_t *ebuf) funchook_set_debug_file(DEFAULT_LOG_PATH); } - // default to a dynamic app? - if (checkEnv("SCOPE_EXEC_TYPE", "static")) { + // check ELF type + //if (checkEnv("SCOPE_EXEC_TYPE", "static")) { + if (is_static(ebuf->buf)) { scopeSetGoAppStateStatic(TRUE); //patchClone(); sysprint("This is a static app\n"); @@ -967,11 +977,9 @@ initGoHook(elf_buf_t *ebuf) sysprint("This is a dynamic app\n"); } - //check ELF type - Elf64_Ehdr *ehdr = (Elf64_Ehdr *)ebuf->buf; // if it's a position independent executable, get the base address from /proc/self/maps - uint64_t base = 0LL; - if (ehdr->e_type == ET_DYN && (scopeGetGoAppStateStatic() == FALSE)) { + // default to a dynamic app? + if (scopeGetGoAppStateStatic() == FALSE) { if (osGetBaseAddr(&base) == FALSE) { sysprint("ERROR: can't get the base address\n"); funchook_destroy(funchook); @@ -992,10 +1000,10 @@ initGoHook(elf_buf_t *ebuf) if (g_go_build_ver[0] != '\0') { go_ver = (char *)((uint64_t)ver_addr); } else { - go_ver = go_str((void *)((uint64_t)ver_addr + base)); + go_ver = go_str((void *)((uint64_t)ver_addr + base), FALSE); } } else { - go_ver = go_str((void *)((uint64_t)go_ver_sym + base)); + go_ver = go_str((void *)((uint64_t)go_ver_sym + base), FALSE); } if (go_ver && (go_runtime_version = go_ver)) { @@ -1081,6 +1089,8 @@ initGoHook(elf_buf_t *ebuf) if (((orig_func = getSymbol(ebuf->buf, tap->func_name)) == NULL) && // look in the .gopclntab section ((orig_func = getGoSymbol(ebuf->buf, tap->func_name, NULL, NULL)) == NULL) && + // check dynamic symbols; exec has been stripped + ((orig_func = getDynSymbol(ebuf->buf, tap->func_name)) == NULL) && // is the symbol defined as an original API; with an abi0 extension ((orig_func = tryAbi0(ebuf->buf, tap->func_name)) == NULL)) { sysprint("WARN: can't get the address for %s\n", tap->func_name); @@ -1234,8 +1244,8 @@ mountDirs(char *src, char *target, char *fstype) { static int once = 0; - if (scope_strstr(fstype, "overlay")) once++; - funcprint("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); + sysprint("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); + if (scope_strstr(fstype, "overlay") && scope_strstr(src, "merged")) once++; /* * Checks: @@ -1249,7 +1259,7 @@ mountDirs(char *src, char *target, char *fstype) * DOCKERD_AFTER_OVERLAY_MOUNTS defines the overlay activity. */ if ((src != NULL) && (target != NULL) && - scope_strstr(g_proc.procname, "dockerd") && + scope_strstr(g_proc.procname, "containerd") && (scope_strstr(g_libpath, "/")) && (once == DOCKERD_AFTER_OVERLAY_MOUNTS)) { int ldfd; @@ -1432,10 +1442,10 @@ c_syscall(char *sys_stack, char *g_stack) char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); + sysprint("Scope: mount of %s to %s\n", src, target); mountDirs(src, target, fstype); break; } - default: break; } @@ -1874,3 +1884,74 @@ go_sighandler(char *stackptr) { return do_cfunc(stackptr, c_sighandler, tap_entry(tap_sighandler)->assembly_fn); } + +static void +updateContainerConfig(char *id) +{ + /* TODO: + * Locate the dir with the passed id + * starting in the runc state dir: /run/containerd/ + * readdir locating the subdir with id. + * Open config.json in the id subdir. + * Modify the json to add LD_PRELOAD to the + * env list and add a mount point for the libdir + * directory. + * Write the changes and close the file. + */ + return; +} + +static void +c_forkExec(char *sys_stack, char *g_stack) +{ + int i; + char *argv0 = (char *)(uint64_t)(sys_stack + 0); + uint64_t *argvv = (uint64_t *)*(uint64_t *)(sys_stack + 0x10); + + /* + * TBD: in order to call scope_strstr() with a needle longer + * than 3-4 chars the stack needs to be aligned on a 16 byte + * boundary due to the "movaps %xmm0,0x20(%rsp)' instruction. + * Aligning in the stack switch code is likley to affect + * current offset values. + * Probably should be applied here? + * We enter this code from the start of the Go runtime function + * before it's stack frame is created. That is unique. Likely + * the cause of this issue. + * Will look into this directly. + */ + + char *cmd = go_str(argv0, TRUE); + // TBD: add a check for curent proc containerd when stack is aligned + if (!scope_strstr(cmd, "runc")) return; + + sysprint("%s execing %s\n", g_proc.procname, cmd); + for (i = 0; argvv[i]; i += 2) { + char *argv = go_str((char *)(argvv + i), TRUE); + if (argv) { + sysprint("\t%s:%d %s argv %s\n", __FUNCTION__, __LINE__, + g_proc.procname, argv); + if (scope_strstr(argv, "-id") && (char *)(argvv + i + 2)) { + sysprint("%s:%d\n", __FUNCTION__, __LINE__); + id = scope_strdup(go_str((char *)(argvv + i + 2), TRUE)); + if (id) sysprint("%s:%d %s\n", __FUNCTION__, __LINE__, id); + } else if (scope_strstr(argv, "sta")) { // should be "start" + valid_id = TRUE; + sysprint("%s:%d\n", __FUNCTION__, __LINE__); + break; + } + scope_free(argv); + } + } + + if (id && (valid_id == TRUE)) updateContainerConfig(id); + scope_free(id); + id = NULL; + valid_id = FALSE; +} + +EXPORTON void * +go_forkExec(char *stackptr) +{ + return do_cfunc(stackptr, c_forkExec, tap_entry(tap_forkExec)->assembly_fn); +} From 46625f47a854b5f988f3b95d045ca86b2b9be98b Mon Sep 17 00:00:00 2001 From: Donn Date: Tue, 23 May 2023 18:15:09 +0000 Subject: [PATCH 13/25] Unit test enablement. --- os/linux/Makefile | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/os/linux/Makefile b/os/linux/Makefile index 4b2e72edf..5ef3eb9d9 100644 --- a/os/linux/Makefile +++ b/os/linux/Makefile @@ -106,28 +106,28 @@ libtest: $(LIBRARY_C_FILES) $(LIBRARY_TEST_C_FILES) $(YAML_AR) $(JSON_AR) $(TEST @echo "$${CI:+::group::}Building Library Tests" $(CC) -c $(TEST_CFLAGS) $(LIBRARY_C_FILES) $(LIBRARY_INCLUDES) $(LIBRARY_TEST_C_FILES) $(INCLUDES) $(CMOCKA_INCLUDES) $(OS_C_FILES) $(CC) $(TEST_CFLAGS) -o test/$(OS)/vdsotest vdsotest.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/coredumptest coredumptest.o coredump.o scopestdlib.o dbg.o utils.o fn.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/ipctest ipctest.o ipc.o ipc_resp.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=jsonConfigurationObject -Wl,--wrap=doAndReplaceConfig - $(CC) $(TEST_CFLAGS) -o test/$(OS)/snapshottest snapshottest.o snapshot.o coredump.o scopestdlib.o dbg.o utils.o fn.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/ostest ostest.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/coredumptest coredumptest.o coredump.o scopestdlib.o dbg.o utils.o fn.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/ipctest ipctest.o ipc.o ipc_resp.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=jsonConfigurationObject -Wl,--wrap=doAndReplaceConfig + $(CC) $(TEST_CFLAGS) -o test/$(OS)/snapshottest snapshottest.o snapshot.o coredump.o scopestdlib.o dbg.o utils.o fn.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/ostest ostest.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/strsettest strsettest.o strset.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/cfgutilstest cfgutilstest.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/cfgutilstest cfgutilstest.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/cfgtest cfgtest.o cfg.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/transporttest transporttest.o transport.o backoff.o scopestdlib.o dbg.o log.o fn.o utils.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/transporttest transporttest.o transport.o backoff.o scopestdlib.o dbg.o log.o fn.o utils.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/backofftest backofftest.o backoff.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/logtest logtest.o log.o transport.o backoff.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/utilstest utilstest.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/mtctest mtctest.o mtc.o log.o transport.o backoff.o mtcformat.o strset.o com.o ctl.o evtformat.o cfg.o cfgutils.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cfgLogStreamEnable - $(CC) $(TEST_CFLAGS) -o test/$(OS)/evtformattest evtformattest.o evtformat.o log.o transport.o backoff.o mtcformat.o strset.o scopestdlib.o dbg.o cfg.o com.o ctl.o mtc.o circbuf.o cfgutils.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/ctltest ctltest.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o com.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cbufGet - $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpstatetest httpstatetest.o httpstate.o plattime.o search.o fn.o utils.o os.o scopestdlib.o dbg.o test.o com.o cfg.o cfgutils.o mtc.o mtcformat.o strset.o ctl.o transport.o backoff.o linklist.o log.o evtformat.o circbuf.o state.o metriccapture.o report.o httpagg.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdPostEvent -lrt - $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpheadertest httpheadertest.o report.o httpagg.o state.o com.o httpstate.o metriccapture.o plattime.o fn.o utils.o os.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o search.o test.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdSendHttp -Wl,--wrap=cmdPostEvent - $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpaggtest httpaggtest.o httpagg.o fn.o utils.o scopestdlib.o dbg.o plattime.o os.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/reporttest reporttest.o report.o httpagg.o state.o httpstate.o metriccapture.o com.o plattime.o fn.o utils.o os.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o search.o test.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdSendEvent -Wl,--wrap=cmdSendMetric - $(CC) $(TEST_CFLAGS) -o test/$(OS)/mtcformattest mtcformattest.o mtcformat.o strset.o scopestdlib.o dbg.o log.o transport.o backoff.o com.o ctl.o mtc.o evtformat.o cfg.o cfgutils.o linklist.o fn.o utils.o circbuf.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/logtest logtest.o log.o transport.o backoff.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/utilstest utilstest.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/mtctest mtctest.o mtc.o log.o transport.o backoff.o mtcformat.o strset.o com.o ctl.o evtformat.o cfg.o cfgutils.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cfgLogStreamEnable + $(CC) $(TEST_CFLAGS) -o test/$(OS)/evtformattest evtformattest.o evtformat.o log.o transport.o backoff.o mtcformat.o strset.o scopestdlib.o dbg.o cfg.o com.o ctl.o mtc.o circbuf.o cfgutils.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/ctltest ctltest.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o com.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cbufGet + $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpstatetest httpstatetest.o httpstate.o plattime.o search.o fn.o utils.o os.o scopestdlib.o dbg.o test.o com.o cfg.o cfgutils.o mtc.o mtcformat.o strset.o ctl.o transport.o backoff.o linklist.o log.o evtformat.o circbuf.o state.o metriccapture.o report.o httpagg.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdPostEvent -lrt + $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpheadertest httpheadertest.o report.o httpagg.o state.o com.o httpstate.o metriccapture.o plattime.o fn.o utils.o os.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o search.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdSendHttp -Wl,--wrap=cmdPostEvent + $(CC) $(TEST_CFLAGS) -o test/$(OS)/httpaggtest httpaggtest.o httpagg.o fn.o utils.o scopestdlib.o dbg.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/reporttest reporttest.o report.o httpagg.o state.o httpstate.o metriccapture.o com.o plattime.o fn.o utils.o os.o ctl.o log.o transport.o backoff.o scopestdlib.o dbg.o cfgutils.o cfg.o mtc.o evtformat.o mtcformat.o strset.o circbuf.o linklist.o search.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=cmdSendEvent -Wl,--wrap=cmdSendMetric + $(CC) $(TEST_CFLAGS) -o test/$(OS)/mtcformattest mtcformattest.o mtcformat.o strset.o scopestdlib.o dbg.o log.o transport.o backoff.o com.o ctl.o mtc.o evtformat.o cfg.o cfgutils.o linklist.o fn.o utils.o circbuf.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/circbuftest circbuftest.o circbuf.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/linklisttest linklisttest.o linklist.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) - $(CC) $(TEST_CFLAGS) -o test/$(OS)/comtest comtest.o com.o ctl.o log.o transport.o backoff.o evtformat.o circbuf.o mtcformat.o strset.o cfgutils.o cfg.o mtc.o scopestdlib.o dbg.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/comtest comtest.o com.o ctl.o log.o transport.o backoff.o evtformat.o circbuf.o mtcformat.o strset.o cfgutils.o cfg.o mtc.o scopestdlib.o dbg.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/dbgtest dbgtest.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/glibcvertest glibcvertest.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/selfinterposetest selfinterposetest.o $(TEST_AR) $(TEST_LD_FLAGS) From ff6faff137a9ccec3c7a9b8ae692278596e8ed0b Mon Sep 17 00:00:00 2001 From: michalbiesek Date: Fri, 26 May 2023 14:53:47 +0200 Subject: [PATCH 14/25] Add arm64 asm changes --- src/gocontext_arm.S | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gocontext_arm.S b/src/gocontext_arm.S index 89e20c89d..625e56762 100644 --- a/src/gocontext_arm.S +++ b/src/gocontext_arm.S @@ -121,8 +121,8 @@ b.eq 2f cmp x8, #SYS_close b.eq 2f - cmp x8, #SYS_mount - b.eq 2f + //cmp x8, #SYS_mount + //b.eq 2f ldr x17, =\retptr ldr x17, [x17] @@ -223,6 +223,7 @@ c_syscall_handler: interpose_system_stack go_hook_exit go_exit c_handler interpose_system_stack go_hook_die go_die c_handler interpose_system_stack go_hook_sighandler go_sighandler c_handler + interpose_system_stack go_hook_forkExec go_forkExec c_handler syscall_filter go_hook_reg_syscall hook_syscall g_syscall_return syscall_filter go_hook_reg_rawsyscall hook_rawsyscall g_rawsyscall_return From 36b4f58c8f3ecdcd4cc4f5757fa8bb738c88b60c Mon Sep 17 00:00:00 2001 From: michalbiesek Date: Mon, 29 May 2023 20:53:03 +0200 Subject: [PATCH 15/25] Add OCI test --- os/linux/Makefile | 1 + test/data/{ => filter}/filter_0.yml | 0 test/data/{ => filter}/filter_1.yml | 0 test/data/{ => filter}/filter_2.yml | 0 test/data/{ => filter}/filter_3.yml | 0 test/data/{ => filter}/filter_4.yml | 0 test/data/{ => filter}/filter_5.yml | 0 test/data/{ => filter}/filter_6.yml | 0 test/data/oci/oci0in.json | 1 + test/data/oci/oci0out.json | 17 ++ test/data/oci/oci1in.json | 4 + test/data/oci/oci1out.json | 17 ++ test/data/oci/oci2in.json | 8 + test/data/oci/oci2out.json | 17 ++ test/data/oci/oci3in.json | 7 + test/data/oci/oci3out.json | 17 ++ test/data/oci/oci4in.json | 19 ++ test/data/oci/oci4out.json | 22 ++ test/data/oci/oci5in.json | 21 ++ test/data/oci/oci5out.json | 22 ++ test/data/oci/oci6in.json | 29 +++ test/data/oci/oci6out.json | 26 ++ test/unit/execute.sh | 1 + test/unit/library/cfgutilstest.c | 40 +-- test/unit/library/ocitest.c | 382 ++++++++++++++++++++++++++++ 25 files changed, 631 insertions(+), 20 deletions(-) rename test/data/{ => filter}/filter_0.yml (100%) rename test/data/{ => filter}/filter_1.yml (100%) rename test/data/{ => filter}/filter_2.yml (100%) rename test/data/{ => filter}/filter_3.yml (100%) rename test/data/{ => filter}/filter_4.yml (100%) rename test/data/{ => filter}/filter_5.yml (100%) rename test/data/{ => filter}/filter_6.yml (100%) create mode 100644 test/data/oci/oci0in.json create mode 100644 test/data/oci/oci0out.json create mode 100644 test/data/oci/oci1in.json create mode 100644 test/data/oci/oci1out.json create mode 100644 test/data/oci/oci2in.json create mode 100644 test/data/oci/oci2out.json create mode 100644 test/data/oci/oci3in.json create mode 100644 test/data/oci/oci3out.json create mode 100644 test/data/oci/oci4in.json create mode 100644 test/data/oci/oci4out.json create mode 100644 test/data/oci/oci5in.json create mode 100644 test/data/oci/oci5out.json create mode 100644 test/data/oci/oci6in.json create mode 100644 test/data/oci/oci6out.json create mode 100644 test/unit/library/ocitest.c diff --git a/os/linux/Makefile b/os/linux/Makefile index 5ef3eb9d9..fb85c6682 100644 --- a/os/linux/Makefile +++ b/os/linux/Makefile @@ -110,6 +110,7 @@ libtest: $(LIBRARY_C_FILES) $(LIBRARY_TEST_C_FILES) $(YAML_AR) $(JSON_AR) $(TEST $(CC) $(TEST_CFLAGS) -o test/$(OS)/ipctest ipctest.o ipc.o ipc_resp.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) -Wl,--wrap=jsonConfigurationObject -Wl,--wrap=doAndReplaceConfig $(CC) $(TEST_CFLAGS) -o test/$(OS)/snapshottest snapshottest.o snapshot.o coredump.o scopestdlib.o dbg.o utils.o fn.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/ostest ostest.o scopestdlib.o dbg.o fn.o utils.o plattime.o os.o scopeelf.o test.o $(TEST_AR) $(TEST_LD_FLAGS) + $(CC) $(TEST_CFLAGS) -o test/$(OS)/ocitest ocitest.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/strsettest strsettest.o strset.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/cfgutilstest cfgutilstest.o cfgutils.o cfg.o mtc.o log.o evtformat.o ctl.o transport.o backoff.o mtcformat.o strset.o com.o scopestdlib.o dbg.o circbuf.o linklist.o fn.o utils.o os.o test.o report.o search.o httpagg.o state.o httpstate.o metriccapture.o plattime.o scopeelf.o $(TEST_AR) $(TEST_LD_FLAGS) $(CC) $(TEST_CFLAGS) -o test/$(OS)/cfgtest cfgtest.o cfg.o scopestdlib.o dbg.o test.o $(TEST_AR) $(TEST_LD_FLAGS) diff --git a/test/data/filter_0.yml b/test/data/filter/filter_0.yml similarity index 100% rename from test/data/filter_0.yml rename to test/data/filter/filter_0.yml diff --git a/test/data/filter_1.yml b/test/data/filter/filter_1.yml similarity index 100% rename from test/data/filter_1.yml rename to test/data/filter/filter_1.yml diff --git a/test/data/filter_2.yml b/test/data/filter/filter_2.yml similarity index 100% rename from test/data/filter_2.yml rename to test/data/filter/filter_2.yml diff --git a/test/data/filter_3.yml b/test/data/filter/filter_3.yml similarity index 100% rename from test/data/filter_3.yml rename to test/data/filter/filter_3.yml diff --git a/test/data/filter_4.yml b/test/data/filter/filter_4.yml similarity index 100% rename from test/data/filter_4.yml rename to test/data/filter/filter_4.yml diff --git a/test/data/filter_5.yml b/test/data/filter/filter_5.yml similarity index 100% rename from test/data/filter_5.yml rename to test/data/filter/filter_5.yml diff --git a/test/data/filter_6.yml b/test/data/filter/filter_6.yml similarity index 100% rename from test/data/filter_6.yml rename to test/data/filter/filter_6.yml diff --git a/test/data/oci/oci0in.json b/test/data/oci/oci0in.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/data/oci/oci0in.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/data/oci/oci0out.json b/test/data/oci/oci0out.json new file mode 100644 index 000000000..2084079ad --- /dev/null +++ b/test/data/oci/oci0out.json @@ -0,0 +1,17 @@ +{ + "process": { + "env": ["LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci1in.json b/test/data/oci/oci1in.json new file mode 100644 index 000000000..adff95a01 --- /dev/null +++ b/test/data/oci/oci1in.json @@ -0,0 +1,4 @@ +{ + "process": { + } +} diff --git a/test/data/oci/oci1out.json b/test/data/oci/oci1out.json new file mode 100644 index 000000000..2084079ad --- /dev/null +++ b/test/data/oci/oci1out.json @@ -0,0 +1,17 @@ +{ + "process": { + "env": ["LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci2in.json b/test/data/oci/oci2in.json new file mode 100644 index 000000000..8f7c00b26 --- /dev/null +++ b/test/data/oci/oci2in.json @@ -0,0 +1,8 @@ +{ + "process": { + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "LD_PRELOAD=/usr/test.so" + ] + } +} diff --git a/test/data/oci/oci2out.json b/test/data/oci/oci2out.json new file mode 100644 index 000000000..5d4920535 --- /dev/null +++ b/test/data/oci/oci2out.json @@ -0,0 +1,17 @@ +{ + "process": { + "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LD_PRELOAD=/opt/libscope.so:/usr/test.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci3in.json b/test/data/oci/oci3in.json new file mode 100644 index 000000000..2543748fe --- /dev/null +++ b/test/data/oci/oci3in.json @@ -0,0 +1,7 @@ +{ + "process": { + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ] + } +} diff --git a/test/data/oci/oci3out.json b/test/data/oci/oci3out.json new file mode 100644 index 000000000..f1146e795 --- /dev/null +++ b/test/data/oci/oci3out.json @@ -0,0 +1,17 @@ +{ + "process": { + "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci4in.json b/test/data/oci/oci4in.json new file mode 100644 index 000000000..b14955367 --- /dev/null +++ b/test/data/oci/oci4in.json @@ -0,0 +1,19 @@ +{ + "process": { + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ] + }, + "mounts":[ + { + "destination":"/proc", + "type":"proc", + "source":"proc", + "options":[ + "nosuid", + "noexec", + "nodev" + ] + } + ] +} diff --git a/test/data/oci/oci4out.json b/test/data/oci/oci4out.json new file mode 100644 index 000000000..14cfed513 --- /dev/null +++ b/test/data/oci/oci4out.json @@ -0,0 +1,22 @@ +{ + "process": { + "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/proc", + "type": "proc", + "source": "proc", + "options": ["nosuid", "noexec", "nodev"] + }, { + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci5in.json b/test/data/oci/oci5in.json new file mode 100644 index 000000000..bd048501d --- /dev/null +++ b/test/data/oci/oci5in.json @@ -0,0 +1,21 @@ +{ + "process": { + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ] + }, + "mounts":[ + { + "destination":"/proc", + "type":"proc", + "source":"proc", + "options":[ + "nosuid", + "noexec", + "nodev" + ] + } + ], + "hooks":{ + } +} diff --git a/test/data/oci/oci5out.json b/test/data/oci/oci5out.json new file mode 100644 index 000000000..14cfed513 --- /dev/null +++ b/test/data/oci/oci5out.json @@ -0,0 +1,22 @@ +{ + "process": { + "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/proc", + "type": "proc", + "source": "proc", + "options": ["nosuid", "noexec", "nodev"] + }, { + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/data/oci/oci6in.json b/test/data/oci/oci6in.json new file mode 100644 index 000000000..f9f8e47f7 --- /dev/null +++ b/test/data/oci/oci6in.json @@ -0,0 +1,29 @@ +{ + "process": { + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ] + }, + "mounts":[ + { + "destination":"/proc", + "type":"proc", + "source":"proc", + "options":[ + "nosuid", + "noexec", + "nodev" + ] + } + ], + "hooks":{ + "containerstatrt":[ + { + "path":"foo", + "args":[ + "bar" + ] + } + ] + } +} diff --git a/test/data/oci/oci6out.json b/test/data/oci/oci6out.json new file mode 100644 index 000000000..2b1761ad1 --- /dev/null +++ b/test/data/oci/oci6out.json @@ -0,0 +1,26 @@ +{ + "process": { + "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LD_PRELOAD=/opt/libscope.so", "SCOPE_SETUP_DONE=true"] + }, + "mounts": [{ + "destination": "/proc", + "type": "proc", + "source": "proc", + "options": ["nosuid", "noexec", "nodev"] + }, { + "destination": "/opt/scope", + "type": "bind", + "source": "/tmp/appscope/dev/scope", + "options": ["rbind", "rprivate"] + }], + "hooks": { + "containerstatrt": [{ + "path": "foo", + "args": ["bar"] + }], + "startContainer": [{ + "path": "/opt/scope", + "args": ["/opt/scope", "extract", "/opt"] + }] + } +} \ No newline at end of file diff --git a/test/unit/execute.sh b/test/unit/execute.sh index 4442777aa..9ca2dce04 100755 --- a/test/unit/execute.sh +++ b/test/unit/execute.sh @@ -69,6 +69,7 @@ run_test test/${OS}/coredumptest run_test test/${OS}/ipctest run_test test/${OS}/snapshottest run_test test/${OS}/ostest +run_test test/${OS}/ocitest run_test test/${OS}/strsettest run_test test/${OS}/cfgutilstest run_test test/${OS}/cfgtest diff --git a/test/unit/library/cfgutilstest.c b/test/unit/library/cfgutilstest.c index 5e22c8f53..e9c4c3c5a 100644 --- a/test/unit/library/cfgutilstest.c +++ b/test/unit/library/cfgutilstest.c @@ -2587,7 +2587,7 @@ cfgReadCustomAnchorExtend(void **state) static void filterEmptyProcName(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus(NULL, "foo", testAccessFilterPath(path), cfg); @@ -2601,7 +2601,7 @@ filterEmptyProcName(void **state) { static void filterEmptyProcCmdLine(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("foo", NULL, testAccessFilterPath(path), cfg); @@ -2633,7 +2633,7 @@ filterNullFilterPath(void **state) { static void filterNullCfg(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); filter_status_t res = cfgFilterStatus("foo", "foo", testAccessFilterPath(path), NULL); assert_int_equal(res, FILTER_ERROR); dbgInit(); // reset dbg for the rest of the tests @@ -2644,7 +2644,7 @@ filterNullCfg(void **state) { static void filterNonExistingFilterFile(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_non_existing.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_non_existing.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("foo", "foo", testAccessFilterPath(path), cfg); @@ -2657,7 +2657,7 @@ filterNonExistingFilterFile(void **state) { static void filterProcNameAllowListPresent(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("redis", "", testAccessFilterPath(path), cfg); @@ -2670,7 +2670,7 @@ filterProcNameAllowListPresent(void **state) { static void filterProcNameDenyListPresent(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("git", "", testAccessFilterPath(path), cfg); @@ -2683,7 +2683,7 @@ filterProcNameDenyListPresent(void **state) { static void filterArgAllowListPresent(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_1.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_1.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("", "redis arg1", testAccessFilterPath(path), cfg); @@ -2696,7 +2696,7 @@ filterArgAllowListPresent(void **state) { static void filterArgDenyListPresent(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_1.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_1.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("", "git arg1", testAccessFilterPath(path), cfg); @@ -2709,13 +2709,13 @@ filterArgDenyListPresent(void **state) { static void filterArgAllowListPartFindPresent(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("redis-server", "redis-server", testAccessFilterPath(path), cfg); assert_int_equal(res, FILTER_NOT_SCOPED); scope_memset(path, 0, sizeof(path)); - scope_snprintf(path, sizeof(path), "%s/data/filter_1.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_1.yml", dirPath); res = cfgFilterStatus("redis-server", "redis-server", testAccessFilterPath(path), cfg); assert_int_equal(res, FILTER_SCOPED_WITH_CFG); // cleanup @@ -2726,7 +2726,7 @@ filterArgAllowListPartFindPresent(void **state) { static void filterArgAllowListEmptyProcMissing(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); @@ -2740,7 +2740,7 @@ filterArgAllowListEmptyProcMissing(void **state) { static void filterArgAllowListNotEmptyProcMissing(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_2.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_2.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("memcached", "memcached", testAccessFilterPath(path), cfg); @@ -2748,7 +2748,7 @@ filterArgAllowListNotEmptyProcMissing(void **state) { scope_memset(path, 0, PATH_MAX); - scope_snprintf(path, sizeof(path), "%s/data/filter_3.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_3.yml", dirPath); res = cfgFilterStatus("memcached", "memcached", testAccessFilterPath(path), cfg); assert_int_equal(res, FILTER_NOT_SCOPED); // cleanup @@ -2759,7 +2759,7 @@ filterArgAllowListNotEmptyProcMissing(void **state) { static void filterVerifyCfg(void **state) { char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_0.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_0.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("redis", "redis", testAccessFilterPath(path), cfg); @@ -2791,7 +2791,7 @@ filterDenyIsProcessedAfterAllow(void **state) // redis is in both the allow list and deny list. // verify that "deny" wins. (is processed after allow) char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_4.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_4.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("redis", "", testAccessFilterPath(path), cfg); @@ -2807,7 +2807,7 @@ filterConfigIsProcessedAfterProcName(void **state) // cfg should be changed if procname or arg matches // make sure we process these fields before config. char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_4.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_4.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); @@ -2830,7 +2830,7 @@ filterMatchAllInAllow(void **state) { // Verify that _MatchAll_ in allow matches all processes char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_5.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_5.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("blue", "", testAccessFilterPath(path), cfg); @@ -2851,7 +2851,7 @@ filterMatchAllInAllowCanBeDenied(void **state) { // Verify that _MatchAll_ in allow is overriden by a match in deny char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_5.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_5.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("redis", "", testAccessFilterPath(path), cfg); @@ -2867,7 +2867,7 @@ filterVerifyMatchAllMergedConfig(void **state) // Verify that matches (including _MatchAll_) are applied in order // And that matches can be "merged" w.r.t. configuration char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_5.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_5.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); @@ -2898,7 +2898,7 @@ filterMatchAllInDeny(void **state) { // Verify that _MatchAll_ in deny denies all processes char path[PATH_MAX] = {0}; - scope_snprintf(path, sizeof(path), "%s/data/filter_6.yml", dirPath); + scope_snprintf(path, sizeof(path), "%s/data/filter/filter_6.yml", dirPath); config_t *cfg = cfgCreateDefault(); assert_non_null(cfg); filter_status_t res = cfgFilterStatus("blue", "", testAccessFilterPath(path), cfg); diff --git a/test/unit/library/ocitest.c b/test/unit/library/ocitest.c new file mode 100644 index 000000000..e6dabe3e2 --- /dev/null +++ b/test/unit/library/ocitest.c @@ -0,0 +1,382 @@ +#define _GNU_SOURCE +#include +#include +#include +#include "cJSON.h" +#include "test.h" +#include "scopestdlib.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +static char dirPath[PATH_MAX]; + +static int +testDirPath(char *path, const char *argv0) { + char buf[PATH_MAX]; + if (argv0[0] == '/') { + scope_strcpy(buf, argv0); + } else { + if (scope_getcwd(buf, PATH_MAX) == NULL) { + scope_perror("getcwd error"); + return -1; + } + scope_strcat(buf, "/"); + scope_strcat(buf, argv0); + } + + if (scope_realpath(buf, path) == NULL) { + scope_perror("scope_realpath error"); + return -1; + } + + /* + * Retrieve the test directory path. + * From: + * //appscope/test/linux/cfgutilsfiltertest + * To: + * //appscope/test/ + */ + for (int i= 0; i < 2; ++i) { + path = scope_dirname(path); + if (path == NULL) { + scope_perror("scope_dirname error"); + return -1; + } + } + return 0; +} + +// This function reflects the logic provided by rewriteOpenContainersConfig +static bool +rewriteOpenContainersConfigTest(int id) { + char inPath [PATH_MAX] = {0}; + char outPath [PATH_MAX] = {0}; + scope_snprintf(inPath, PATH_MAX, "%s/data/oci/oci%din.json", dirPath, id); + scope_snprintf(outPath, PATH_MAX, "%s/data/oci/oci%dout.json", dirPath, id); + + bool res = FALSE; + struct stat fileStat; + if (scope_stat(inPath, &fileStat) == -1) { + assert_non_null(NULL); + return res; + } + + FILE *fp = scope_fopen(inPath, "r"); + if (!fp) { + assert_non_null(NULL); + return res; + } + + /* + * Read the file contents into a string + */ + char *buf = (char *)scope_malloc(fileStat.st_size); + if (!buf) { + scope_fclose(fp); + assert_non_null(NULL); + return res; + } + + scope_fread(buf, sizeof(char), fileStat.st_size, fp); + scope_fclose(fp); + + + cJSON *json = cJSON_Parse(buf); + if (!json) { + assert_non_null(NULL); + return res; + } + scope_free(buf); + char *jsonStr = NULL; + + // Handle the process + cJSON *procNode = cJSON_GetObjectItemCaseSensitive(json, "process"); + if (!procNode) { + procNode = cJSON_CreateObject(); + if (!procNode) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(json, "process", procNode); + } + cJSON *envNodeArr = cJSON_GetObjectItemCaseSensitive(procNode, "env"); + if (envNodeArr) { + bool ldPreloadPresent = FALSE; + // Iterate over environment string array + size_t envSize = cJSON_GetArraySize(envNodeArr); + for (int i = 0; i < envSize ;++i) { + cJSON *item = cJSON_GetArrayItem(envNodeArr, i); + char *strItem = cJSON_GetStringValue(item); + + if (scope_strncmp("LD_PRELOAD=", strItem, sizeof("LD_PRELOAD=")-1) == 0) { + size_t itemLen = scope_strlen(strItem); + size_t newLdprelLen = itemLen + sizeof("/opt/libscope.so:") - 1; + char *newLdPreloadLib = scope_calloc(1, newLdprelLen); + if (!newLdPreloadLib) { + assert_non_null(NULL); + goto exit; + } + scope_strncpy(newLdPreloadLib, "LD_PRELOAD=/opt/libscope.so:", sizeof("LD_PRELOAD=/opt/libscope.so:") - 1); + scope_strcat(newLdPreloadLib, strItem + sizeof("LD_PRELOAD=") - 1); + cJSON *newLdPreloadLibObj = cJSON_CreateString(newLdPreloadLib); + if (!newLdPreloadLibObj) { + scope_free(newLdPreloadLib); + assert_non_null(NULL); + goto exit; + } + cJSON_ReplaceItemInArray(envNodeArr, i, newLdPreloadLibObj); + scope_free(newLdPreloadLib); + + cJSON *scopeEnvNode = cJSON_CreateString("SCOPE_SETUP_DONE=true"); + if (!scopeEnvNode) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToArray(envNodeArr, scopeEnvNode); + ldPreloadPresent = TRUE; + break; + } else if (scope_strncmp("SCOPE_SETUP_DONE=true", strItem, sizeof("SCOPE_SETUP_DONE=true")-1) == 0) { + // we are done here + res = TRUE; + goto exit; + } + } + + + // There was no LD_PRELOAD in environment variables + if (ldPreloadPresent == FALSE) { + const char *const envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + for (int i = 0; i < 2 ;++i) { + cJSON *scopeEnvNode = cJSON_CreateString(envItems[i]); + if (!scopeEnvNode) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToArray(envNodeArr, scopeEnvNode); + } + } + } else { + const char * envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + envNodeArr = cJSON_CreateStringArray(envItems, 2); + if (!envNodeArr) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(procNode, "env", envNodeArr); + } + + cJSON *mountNodeArr = cJSON_GetObjectItemCaseSensitive(json, "mounts"); + if (!mountNodeArr) { + mountNodeArr = cJSON_CreateArray(); + if (!mountNodeArr) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(json, "mounts", mountNodeArr); + } + + cJSON *mountNode = cJSON_CreateObject(); + if (!mountNode) { + assert_non_null(NULL); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "destination", "/opt/scope")) { + cJSON_Delete(mountNode); + assert_non_null(NULL); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "type", "bind")) { + cJSON_Delete(mountNode); + assert_non_null(NULL); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "source", "/tmp/appscope/dev/scope")) { + cJSON_Delete(mountNode); + assert_non_null(NULL); + goto exit; + } + + const char *optItems[2] = + { + "rbind", + "rprivate" + }; + + cJSON *optNodeArr = cJSON_CreateStringArray(optItems, 2); + if (!optNodeArr) { + cJSON_Delete(mountNode); + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(mountNode, "options", optNodeArr); + cJSON_AddItemToArray(mountNodeArr, mountNode); + + cJSON *hooksNode = cJSON_GetObjectItemCaseSensitive(json, "hooks"); + if (!hooksNode) { + hooksNode = cJSON_CreateObject(); + if (!hooksNode) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(json, "hooks", hooksNode); + } + + cJSON *startContainerNodeArr = cJSON_GetObjectItemCaseSensitive(hooksNode, "startContainer"); + if (!startContainerNodeArr) { + startContainerNodeArr = cJSON_CreateArray(); + if (!startContainerNodeArr) { + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(hooksNode, "startContainer", startContainerNodeArr); + } + + cJSON *startContainerNode = cJSON_CreateObject(); + if (!startContainerNode) { + assert_non_null(NULL); + goto exit; + } + + if (!cJSON_AddStringToObjLN(startContainerNode, "path", "/opt/scope")) { + cJSON_Delete(startContainerNode); + assert_non_null(NULL); + goto exit; + } + + const char *argsItems[3] = + { + "/opt/scope", + "extract", + "/opt" + }; + cJSON *argsNodeArr = cJSON_CreateStringArray(argsItems, 3); + if (!argsNodeArr) { + cJSON_Delete(startContainerNode); + assert_non_null(NULL); + goto exit; + } + cJSON_AddItemToObject(startContainerNode, "args", argsNodeArr); + cJSON_AddItemToArray(startContainerNodeArr, startContainerNode); + + jsonStr = cJSON_Print(json); + + // Overwrite the file + int fdOut = scope_open(outPath, O_RDONLY); + if (fdOut == -1) { + cJSON_free(jsonStr); + goto exit; + } + + struct stat stOut; + if (scope_fstat(fdOut, &stOut) == -1) { + cJSON_free(jsonStr); + scope_close(fdOut); + goto exit; + } + + + void *fdOutMap = scope_mmap(NULL, stOut.st_size, PROT_READ, MAP_PRIVATE, fdOut, 0); + if (fdOutMap == MAP_FAILED) { + cJSON_free(jsonStr); + scope_close(fdOut); + goto exit; + } + + if (memcmp(fdOutMap, jsonStr, stOut.st_size) != 0) { + assert_non_null(NULL); + } + + cJSON_free(jsonStr); + scope_close(fdOut); + + res = TRUE; +exit: + cJSON_Delete(json); + return res; +} + +static void +ocitest_empty_json(void **state) +{ + bool res = rewriteOpenContainersConfigTest(0); + assert_int_equal(res, TRUE); +} + +static void +ocitest_process_only_json(void **state) +{ + bool res = rewriteOpenContainersConfigTest(1); + assert_int_equal(res, TRUE); +} + +static void +ocitest_process_env_present_preload(void **state) +{ + bool res = rewriteOpenContainersConfigTest(2); + assert_int_equal(res, TRUE); +} + +static void +ocitest_process_env_empty_preload(void **state) +{ + bool res = rewriteOpenContainersConfigTest(3); + assert_int_equal(res, TRUE); +} + +static void +ocitest_missing_hooks(void **state) +{ + bool res = rewriteOpenContainersConfigTest(4); + assert_int_equal(res, TRUE); +} + +static void +ocitest_hooks_incomplete(void **state) +{ + bool res = rewriteOpenContainersConfigTest(5); + assert_int_equal(res, TRUE); +} + +static void +ocitest_hooks_complete(void **state) +{ + bool res = rewriteOpenContainersConfigTest(6); + assert_int_equal(res, TRUE); +} + +int +main(int argc, char* argv[]) +{ + printf("running %s\n", argv[0]); + if (testDirPath(dirPath, argv[0])) { + return EXIT_FAILURE; + } + + const struct CMUnitTest tests[] = { + cmocka_unit_test(ocitest_empty_json), + cmocka_unit_test(ocitest_process_only_json), + cmocka_unit_test(ocitest_process_env_present_preload), + cmocka_unit_test(ocitest_process_env_empty_preload), + cmocka_unit_test(ocitest_missing_hooks), + cmocka_unit_test(ocitest_hooks_incomplete), + cmocka_unit_test(ocitest_hooks_complete), + cmocka_unit_test(dbgHasNoUnexpectedFailures), + }; + return cmocka_run_group_tests(tests, groupSetup, groupTeardown); +} From 59203f3bace3777b6f97c208b3de2d20cf0efb56 Mon Sep 17 00:00:00 2001 From: michalbiesek Date: Mon, 29 May 2023 20:53:22 +0200 Subject: [PATCH 16/25] Add OCI rewrite --- src/wrap_go.c | 394 +++++++++++++++++++++++++++++++++--- test/unit/library/ocitest.c | 1 + 2 files changed, 370 insertions(+), 25 deletions(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index 74cb431f6..02d02f75e 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -84,8 +84,6 @@ int g_go_maint_ver = UNKNOWN_GO_VER; int g_arch = ARCH; static char g_go_build_ver[7]; static char g_ReadFrame_addr[20]; -static bool valid_id = FALSE; -static char *id = NULL; go_schema_t *g_go_schema = &go_11_schema; // overridden if later version uint64_t g_glibc_guard = 0LL; uint64_t go_systemstack_switch; @@ -1885,19 +1883,350 @@ go_sighandler(char *stackptr) return do_cfunc(stackptr, c_sighandler, tap_entry(tap_sighandler)->assembly_fn); } +/* + * Rewrites the container configuration for specific task ID and container namespace name. + * Please look into opencontainers Linux runtime-spec for details about the exact JSON struct. + * Following changes will be performed: + * - Add mount point + * `scope` will be mounted from host ("/usr/lib/appscope//scope") into the container ("/opt/scope") + * - Extend Environment variable + * `LD_PRELOAD` will contain the following entry `/opt/libscope.so` + * `SCOPE_SETUP_DONE=true` mark that configuration was processed + * - Add prestart hook + * execute scope extract operation to ensure using library with proper loader reference (musl/glibc) + */ static void -updateContainerConfig(char *id) +rewriteOpenContainersConfig(const char *taskId, const char *nsName) { - /* TODO: - * Locate the dir with the passed id - * starting in the runc state dir: /run/containerd/ - * readdir locating the subdir with id. - * Open config.json in the id subdir. - * Modify the json to add LD_PRELOAD to the - * env list and add a mount point for the libdir - * directory. - * Write the changes and close the file. +#ifdef __x86_64__ + /* + * Need to extend the system stack size when calling cJSON_PrintUnformatted(). */ + int arc; + char *exit_stack, *tstack, *gstack; + if ((exit_stack = scope_malloc(SCOPE_STACK_SIZE)) == NULL) { + return; + } + + tstack = exit_stack + SCOPE_STACK_SIZE; + + // save the original stack, switch to the tstack + __asm__ volatile ( + "mov %%rsp, %2 \n" + "mov %1, %%rsp \n" + : "=r"(arc) // output + : "m"(tstack), "m"(gstack) // input + : // clobbered register + ); +#endif + + char path[PATH_MAX] = {0}; + if ((!taskId) || (!nsName)) { + goto exit; + } + + if (scope_snprintf(path, sizeof(path), "/run/containerd/io.containerd.runtime.v2.task/%s/%s/config.json", nsName, taskId) < 0) { + goto exit; + } + + struct stat fileStat; + if (scope_stat(path, &fileStat) == -1) { + goto exit; + } + + FILE *fp = scope_fopen(path, "r"); + if (!fp) { + goto exit; + } + + /* + * Read the file contents into a string + */ + char *buf = (char *)scope_malloc(fileStat.st_size); + if (!buf) { + scope_fclose(fp); + goto exit; + } + + scope_fread(buf, sizeof(char), fileStat.st_size, fp); + scope_fclose(fp); + + cJSON *json = cJSON_Parse(buf); + scope_free(buf); + if (json == NULL) { + goto exit; + } + + /* + * Handle process environment variables + * + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=6735578591bb", + "TERM=xterm", + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + ], + */ + cJSON *procNode = cJSON_GetObjectItemCaseSensitive(json, "process"); + if (!procNode) { + procNode = cJSON_CreateObject(); + if (!procNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(json, "process", procNode); + } + + cJSON *envNodeArr = cJSON_GetObjectItemCaseSensitive(procNode, "env"); + if (envNodeArr) { + bool ldPreloadPresent = FALSE; + // Iterate over environment string array + size_t envSize = cJSON_GetArraySize(envNodeArr); + for (int i = 0; i < envSize ;++i) { + cJSON *item = cJSON_GetArrayItem(envNodeArr, i); + char *strItem = cJSON_GetStringValue(item); + + if (scope_strncmp("LD_PRELOAD=", strItem, sizeof("LD_PRELOAD=")-1) == 0) { + size_t itemLen = scope_strlen(strItem); + size_t newLdprelLen = itemLen + sizeof("/opt/libscope.so:") - 1; + char *newLdPreloadLib = scope_calloc(1, newLdprelLen); + if (!newLdPreloadLib) { + cJSON_Delete(json); + goto exit; + } + scope_strncpy(newLdPreloadLib, "LD_PRELOAD=/opt/libscope.so:", sizeof("LD_PRELOAD=/opt/libscope.so:") - 1); + scope_strcat(newLdPreloadLib, strItem + sizeof("LD_PRELOAD=") - 1); + cJSON *newLdPreloadLibObj = cJSON_CreateString(newLdPreloadLib); + if (!newLdPreloadLibObj) { + scope_free(newLdPreloadLib); + cJSON_Delete(json); + goto exit; + } + cJSON_ReplaceItemInArray(envNodeArr, i, newLdPreloadLibObj); + scope_free(newLdPreloadLib); + + cJSON *scopeEnvNode = cJSON_CreateString("SCOPE_SETUP_DONE=true"); + if (!scopeEnvNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToArray(envNodeArr, scopeEnvNode); + ldPreloadPresent = TRUE; + break; + } else if (scope_strncmp("SCOPE_SETUP_DONE=true", strItem, sizeof("SCOPE_SETUP_DONE=true")-1) == 0) { + // we are done here + cJSON_Delete(json); + goto exit; + } + } + + + // There was no LD_PRELOAD in environment variables + if (ldPreloadPresent == FALSE) { + const char *const envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + for (int i = 0; i < 2 ;++i) { + cJSON *scopeEnvNode = cJSON_CreateString(envItems[i]); + if (!scopeEnvNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToArray(envNodeArr, scopeEnvNode); + } + } + } else { + const char * envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + envNodeArr = cJSON_CreateStringArray(envItems, 2); + if (!envNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(procNode, "env", envNodeArr); + } + + /* + * Handle process mounts + * + "mounts":[ + { + "destination":"/proc", + "type":"proc", + "source":"proc", + "options":[ + "nosuid", + "noexec", + "nodev" + ] + }, + ... + { + "destination":"/opt/scope", + "type":"bind", + "source":"/tmp/appscope/dev/scope", + "options":[ + "rbind", + "rprivate" + ] + } + */ + cJSON *mountNodeArr = cJSON_GetObjectItemCaseSensitive(json, "mounts"); + if (!mountNodeArr) { + mountNodeArr = cJSON_CreateArray(); + if (!mountNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(json, "mounts", mountNodeArr); + } + + cJSON *mountNode = cJSON_CreateObject(); + if (!mountNode) { + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "destination", "/opt/scope")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "type", "bind")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "source", "/tmp/appscope/dev/scope")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + const char *optItems[2] = + { + "rbind", + "rprivate" + }; + + cJSON *optNodeArr = cJSON_CreateStringArray(optItems, 2); + if (!optNodeArr) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(mountNode, "options", optNodeArr); + cJSON_AddItemToArray(mountNodeArr, mountNode); + + /* + * Handle startContainer hooks process + * + "hooks":{ + "prestart":[ + { + "path":"/proc/1513/exe", + "args":[ + "libnetwork-setkey", + "-exec-root=/var/run/docker", + "6735578591bb3c5aebc91e5c702470c52d2c10cea52e4836604bf5a4a6c0f2eb", + "ec7e49ffc98c" + ] + } + ], + "startContainer":[ + { + "path":"/opt/scope" + "args":[ + "/opt/scope", + "extract", + "/opt/", + ] + }, + ] + */ + cJSON *hooksNode = cJSON_GetObjectItemCaseSensitive(json, "hooks"); + if (!hooksNode) { + hooksNode = cJSON_CreateObject(); + if (!hooksNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(json, "hooks", hooksNode); + } + + cJSON *startContainerNodeArr = cJSON_GetObjectItemCaseSensitive(hooksNode, "startContainer"); + if (!startContainerNodeArr) { + startContainerNodeArr = cJSON_CreateArray(); + if (!startContainerNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(hooksNode, "startContainer", startContainerNodeArr); + } + + cJSON *startContainerNode = cJSON_CreateObject(); + if (!startContainerNode) { + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(startContainerNode, "path", "/opt/scope")) { + cJSON_Delete(startContainerNode); + cJSON_Delete(json); + goto exit; + } + + const char *argsItems[3] = + { + "/opt/scope", + "extract", + "/opt" + }; + cJSON *argsNodeArr = cJSON_CreateStringArray(argsItems, 3); + if (!argsNodeArr) { + cJSON_Delete(startContainerNode); + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(startContainerNode, "args", argsNodeArr); + cJSON_AddItemToArray(startContainerNodeArr, startContainerNode); + + char *jsonStr = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + + // Overwrite the file + fp = scope_fopen(path, "w"); + if (fp == NULL) { + cJSON_free(jsonStr); + goto exit; + } + + scope_fprintf(fp, "%s\n", jsonStr); + + cJSON_free(jsonStr); + scope_fclose(fp); + +exit: +#ifdef __x86_64__ + // Switch stack back to the original stack + __asm__ volatile ( + "mov %1, %%rsp \n" + : "=r"(arc) // output + : "r"(gstack) // inputs + : // clobbered register + ); + + scope_free(exit_stack); +#endif + // to handle aarch64 case return; } @@ -1923,31 +2252,46 @@ c_forkExec(char *sys_stack, char *g_stack) char *cmd = go_str(argv0, TRUE); // TBD: add a check for curent proc containerd when stack is aligned - if (!scope_strstr(cmd, "runc")) return; - + if (!scope_strstr(cmd, "runc")) { + scope_free(cmd); + return; + } + bool updateCfg = FALSE; + char *taskUniqueId = NULL; + char *nsName = NULL; sysprint("%s execing %s\n", g_proc.procname, cmd); + scope_free(cmd); for (i = 0; argvv[i]; i += 2) { char *argv = go_str((char *)(argvv + i), TRUE); if (argv) { - sysprint("\t%s:%d %s argv %s\n", __FUNCTION__, __LINE__, - g_proc.procname, argv); + sysprint("\t%s:%d %s argv %s\n", __FUNCTION__, __LINE__, g_proc.procname, argv); if (scope_strstr(argv, "-id") && (char *)(argvv + i + 2)) { - sysprint("%s:%d\n", __FUNCTION__, __LINE__); - id = scope_strdup(go_str((char *)(argvv + i + 2), TRUE)); - if (id) sysprint("%s:%d %s\n", __FUNCTION__, __LINE__, id); + /* + * id of the task (container) + */ + taskUniqueId = go_str((char *)(argvv + i + 2), TRUE); + } else if (scope_strstr(argv, "-na") && (char *)(argvv + i + 2)) { // should be "namespace" + /* + * namespace + */ + nsName = go_str((char *)(argvv + i + 2), TRUE); } else if (scope_strstr(argv, "sta")) { // should be "start" - valid_id = TRUE; - sysprint("%s:%d\n", __FUNCTION__, __LINE__); + /* + * start indicator + */ + updateCfg = TRUE; break; } scope_free(argv); } } - if (id && (valid_id == TRUE)) updateContainerConfig(id); - scope_free(id); - id = NULL; - valid_id = FALSE; + // Update task configuration + if (updateCfg) { + rewriteOpenContainersConfig(taskUniqueId, nsName); + } + scope_free(nsName); + scope_free(taskUniqueId); } EXPORTON void * diff --git a/test/unit/library/ocitest.c b/test/unit/library/ocitest.c index e6dabe3e2..550c4b2b1 100644 --- a/test/unit/library/ocitest.c +++ b/test/unit/library/ocitest.c @@ -301,6 +301,7 @@ rewriteOpenContainersConfigTest(int id) { if (memcmp(fdOutMap, jsonStr, stOut.st_size) != 0) { assert_non_null(NULL); } + scope_munmap(fdOutMap, stOut.st_size); cJSON_free(jsonStr); scope_close(fdOut); From eff5287ffd0a46c793501ac523a34fc9d6541448 Mon Sep 17 00:00:00 2001 From: iapaddler Date: Mon, 5 Jun 2023 19:48:34 +0000 Subject: [PATCH 17/25] Testing with alternate pclntab location. --- test/manual/gosym/gosym/gosym.c | 144 +++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/test/manual/gosym/gosym/gosym.c b/test/manual/gosym/gosym/gosym.c index fba5e3841..d18ec5139 100644 --- a/test/manual/gosym/gosym/gosym.c +++ b/test/manual/gosym/gosym/gosym.c @@ -23,6 +23,8 @@ gcc -o gosym ./test/manual/gosym.c #define GOPCLNTAB_MAGIC_118 0xfffffff0 #define GOPCLNTAB_MAGIC_120 0xfffffff1 +#define TRUE 1 +#define FALSE 0 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) struct sym_status { @@ -85,7 +87,128 @@ static void printSymStatus(void) { } } -int printSymbols(const char *fname) +static void +getSym12(void *pclntab_addr) +{ + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + const void *symtab_addr = pclntab_addr + 16; + printf_info("Symbol count = %ld\n", sym_count); + printf_info("Address\t\tSymbol Name\n"); + printf_info("---------------------------\n"); + for (int i = 0; i < sym_count; i++) { + uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); + uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + func_offset + 8)); + const char *func_name = (const char *)(pclntab_addr + name_offset); + printf_info("0x%lx\t%s\n", sym_addr, func_name); + + symtab_addr += 16; + updateSymStatus(func_name); + } +} + +static void +getSym16(void *pclntab_addr) +{ + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + + uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (3 * 8))); + uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (7 * 8))); + + const void *symtab_addr = pclntab_addr + pclntab_offset; + printf_info("Symbol count = %ld\n", sym_count); + printf_info("Address\t\tSymbol Name\n"); + printf_info("---------------------------\n"); + for (int i = 0; i < sym_count; i++) { + uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); + uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 8)); + const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); + printf_info("0x%lx\t%s\n", sym_addr, func_name); + + symtab_addr += 16; + updateSymStatus(func_name); + } +} + +static void +getSym1820(void *pclntab_addr) +{ + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (4 * 8))); + uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (8 * 8))); + uint64_t text_start = *((const uint64_t *)(pclntab_addr + (3 * 8))); + + const void *symtab_addr = pclntab_addr + pclntab_offset; + + printf_info("Symbol count = %ld\n", sym_count); + printf_info("Address\t\tSymbol Name\n"); + printf_info("---------------------------\n"); + for (int i = 0; i < sym_count; i++) { + uint32_t func_offset = *((uint32_t *)(symtab_addr + 4)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 4)); + func_offset = *((uint32_t *)(symtab_addr)); + uint64_t sym_addr = (uint64_t)(func_offset + text_start); + const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); + printf_info("0x%lx\t%s\n", sym_addr, func_name); + symtab_addr += 8; + updateSymStatus(func_name); + } +} + +static bool +embedPclntab(uint8_t *buf) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)buf; + Elf64_Shdr *sections = (Elf64_Shdr *)(buf + ehdr->e_shoff); + const char *section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + + for (int i = 0; i < ehdr->e_shnum; i++) { + const char *sec_name = section_strtab + sections[i].sh_name; + //printf("%s:%d %s\n", __FUNCTION__, __LINE__, sec_name); + if (strstr(sec_name, "data.rel.ro") != 0) { + + const void *pclntab_addr = buf + sections[i].sh_offset; + char *data = (char *)pclntab_addr; + size_t slen = sections[i].sh_size; + size_t j; + + //printf("%s:%d FOUND at %p %p %p\n", __FUNCTION__, __LINE__, + // buf, data, pclntab_addr); + // Find the magic number in the pclntab header + for (j = 0; j <= slen; j += 4) { //0x3c8c80 + if (((data[j] == 0xf1) || (data[j] == 0xf0) || + (data[j] == 0xfa) || (data[j] == 0xfb)) && + data[j+1] == 0xff && + data[j+2] == 0xff && + data[j+3] == 0xff) { + printf_info("%s:%d pclntab was recognized at %p\n", + __FUNCTION__, __LINE__, &data[j]); + + switch (data[j]) { + // Go 18 and 20 + case 0xf1: + case 0xf0: + getSym1820(&data[j]); + return TRUE; + // Go 16 + case 0xfa: + getSym16(&data[j]); + return TRUE; + // Go 12 + case 0xfb: + getSym12(&data[j]); + return TRUE; + } + } + } + } + } + return FALSE; +} + +int +printSymbols(const char *fname) { int fd; struct stat st; @@ -122,7 +245,7 @@ int printSymbols(const char *fname) for (int i = 0; i < ehdr->e_shnum; i++) { const char *sec_name = section_strtab + sections[i].sh_name; if (strcmp(".gopclntab", sec_name) == 0) { - const void *pclntab_addr = buf + sections[i].sh_offset; + void *pclntab_addr = buf + sections[i].sh_offset; /* Go symbol table is stored in the .gopclntab section More info: https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub @@ -130,6 +253,7 @@ int printSymbols(const char *fname) uint32_t magic = *((const uint32_t *)(pclntab_addr)); if (magic == GOPCLNTAB_MAGIC_112) { printf_validate("[INFO] gopclntab was recognized\n"); +#if 0 uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); const void *symtab_addr = pclntab_addr + 16; printf_info("Symbol count = %ld\n", sym_count); @@ -145,6 +269,9 @@ int printSymbols(const char *fname) symtab_addr += 16; updateSymStatus(func_name); } +#else + getSym12(pclntab_addr); +#endif } else if (magic == GOPCLNTAB_MAGIC_116) { printf_validate("[INFO] gopclntab was recognized\n"); // the layout of pclntab: @@ -182,6 +309,7 @@ int printSymbols(const char *fname) // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] // end PC [thearch.ptrsize bytes] // func structures, pcdata offsets, func data. +#if 0 uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (3 * 8))); @@ -201,8 +329,12 @@ int printSymbols(const char *fname) symtab_addr += 16; updateSymStatus(func_name); } +#else + getSym16(pclntab_addr); +#endif } else if ((magic == GOPCLNTAB_MAGIC_118) || (magic == GOPCLNTAB_MAGIC_120)) { printf_validate("[INFO] gopclntab was recognized\n"); +#if 0 uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (4 * 8))); uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (8 * 8))); @@ -223,6 +355,9 @@ int printSymbols(const char *fname) symtab_addr += 8; updateSymStatus(func_name); } +#else + getSym1820(pclntab_addr); +#endif } else { fprintf(stderr, "[ERROR] Unknown header in .gopclntab\n"); munmap(buf, st.st_size); @@ -230,6 +365,11 @@ int printSymbols(const char *fname) return -1; } break; + } else if (embedPclntab(buf) == FALSE) { + fprintf(stderr, "[ERROR] cannot locate a pclntab\n"); + munmap(buf, st.st_size); + close(fd); + return -1; } } From cacc54548ffc3a280b966ca3564f9b78e4c16ad8 Mon Sep 17 00:00:00 2001 From: Donn Date: Mon, 5 Jun 2023 20:47:12 +0000 Subject: [PATCH 18/25] Tested locating .gopclntab sections in Go 12, 16 & 20. --- test/manual/gosym/gosym/gosym.c | 86 ++++++--------------------------- 1 file changed, 14 insertions(+), 72 deletions(-) diff --git a/test/manual/gosym/gosym/gosym.c b/test/manual/gosym/gosym/gosym.c index d18ec5139..da220de32 100644 --- a/test/manual/gosym/gosym/gosym.c +++ b/test/manual/gosym/gosym/gosym.c @@ -169,7 +169,7 @@ embedPclntab(uint8_t *buf) if (strstr(sec_name, "data.rel.ro") != 0) { const void *pclntab_addr = buf + sections[i].sh_offset; - char *data = (char *)pclntab_addr; + unsigned char *data = (char *)pclntab_addr; size_t slen = sections[i].sh_size; size_t j; @@ -241,10 +241,12 @@ printSymbols(const char *fname) Elf64_Shdr *sections = (Elf64_Shdr *)(buf + ehdr->e_shoff); const char *section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + bool found = FALSE; for (int i = 0; i < ehdr->e_shnum; i++) { const char *sec_name = section_strtab + sections[i].sh_name; if (strcmp(".gopclntab", sec_name) == 0) { + found = TRUE; void *pclntab_addr = buf + sections[i].sh_offset; /* Go symbol table is stored in the .gopclntab section @@ -252,28 +254,10 @@ printSymbols(const char *fname) */ uint32_t magic = *((const uint32_t *)(pclntab_addr)); if (magic == GOPCLNTAB_MAGIC_112) { - printf_validate("[INFO] gopclntab was recognized\n"); -#if 0 - uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); - const void *symtab_addr = pclntab_addr + 16; - printf_info("Symbol count = %ld\n", sym_count); - printf_info("Address\t\tSymbol Name\n"); - printf_info("---------------------------\n"); - for (i = 0; i < sym_count; i++) { - uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); - uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); - uint32_t name_offset = *((const uint32_t *)(pclntab_addr + func_offset + 8)); - const char *func_name = (const char *)(pclntab_addr + name_offset); - printf_info("0x%lx\t%s\n", sym_addr, func_name); - - symtab_addr += 16; - updateSymStatus(func_name); - } -#else + printf_validate("[INFO] gopclntab for 12 was recognized\n"); getSym12(pclntab_addr); -#endif } else if (magic == GOPCLNTAB_MAGIC_116) { - printf_validate("[INFO] gopclntab was recognized\n"); + printf_validate("[INFO] gopclntab for 16 was recognized\n"); // the layout of pclntab: // // .gopclntab/__gopclntab [elf/macho section] @@ -309,55 +293,10 @@ printSymbols(const char *fname) // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] // end PC [thearch.ptrsize bytes] // func structures, pcdata offsets, func data. -#if 0 - uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); - - uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (3 * 8))); - uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (7 * 8))); - - const void *symtab_addr = pclntab_addr + pclntab_offset; - printf_info("Symbol count = %ld\n", sym_count); - printf_info("Address\t\tSymbol Name\n"); - printf_info("---------------------------\n"); - for (i = 0; i < sym_count; i++) { - uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); - uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); - uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 8)); - const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); - printf_info("0x%lx\t%s\n", sym_addr, func_name); - - symtab_addr += 16; - updateSymStatus(func_name); - } -#else getSym16(pclntab_addr); -#endif } else if ((magic == GOPCLNTAB_MAGIC_118) || (magic == GOPCLNTAB_MAGIC_120)) { - printf_validate("[INFO] gopclntab was recognized\n"); -#if 0 - uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); - uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (4 * 8))); - uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (8 * 8))); - uint64_t text_start = *((const uint64_t *)(pclntab_addr + (3 * 8))); - - const void *symtab_addr = pclntab_addr + pclntab_offset; - - printf_info("Symbol count = %ld\n", sym_count); - printf_info("Address\t\tSymbol Name\n"); - printf_info("---------------------------\n"); - for (i = 0; i < sym_count; i++) { - uint32_t func_offset = *((uint32_t *)(symtab_addr + 4)); - uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 4)); - func_offset = *((uint32_t *)(symtab_addr)); - uint64_t sym_addr = (uint64_t)(func_offset + text_start); - const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); - printf_info("0x%lx\t%s\n", sym_addr, func_name); - symtab_addr += 8; - updateSymStatus(func_name); - } -#else + printf_validate("[INFO] gopclntab for 18/20 was recognized\n"); getSym1820(pclntab_addr); -#endif } else { fprintf(stderr, "[ERROR] Unknown header in .gopclntab\n"); munmap(buf, st.st_size); @@ -365,14 +304,17 @@ printSymbols(const char *fname) return -1; } break; - } else if (embedPclntab(buf) == FALSE) { - fprintf(stderr, "[ERROR] cannot locate a pclntab\n"); - munmap(buf, st.st_size); - close(fd); - return -1; } } + // if no .gopclntab section was found, check embedded + if ((found == FALSE) && (embedPclntab(buf) == FALSE)) { + fprintf(stderr, "[ERROR] cannot locate a pclntab\n"); + munmap(buf, st.st_size); + close(fd); + return -1; + } + printSymStatus(); munmap(buf, st.st_size); From 3779fba86a3c240ae0867d99ffd057c20b3a8e81 Mon Sep 17 00:00:00 2001 From: iapaddler Date: Wed, 7 Jun 2023 16:03:52 +0000 Subject: [PATCH 19/25] Located the embedded pclntab and update container config from OCI backend. --- os/linux/os.c | 35 +- os/linux/os.h | 1 + src/wrap_go.c | 1635 ++++++++++++++++++++++++++----------------------- 3 files changed, 916 insertions(+), 755 deletions(-) diff --git a/os/linux/os.c b/os/linux/os.c index 60ec98e9f..f817a447b 100644 --- a/os/linux/os.c +++ b/os/linux/os.c @@ -555,17 +555,50 @@ osGetCmdline(pid_t pid, char **cmd) buf = scope_strdup("none"); } else { // buf is big; try to scope_strdup what we've used and scope_free the rest - char* tmp = scope_strdup(buf); + char *tmp = scope_strdup(buf); if (tmp) { scope_free(buf); buf = tmp; } } + if (fd != -1) scope_close(fd); *cmd = buf; return (*cmd != NULL); } +int +osGetArgv(pid_t pid, char *buf, size_t blen) +{ + int i, fd = -1, argc = 0, bytesRead = 0; + char path[64]; + + if (!buf) return 0; + + if (scope_snprintf(path, sizeof(path), "/proc/%d/cmdline", pid) < 0) { + goto out; + } + + if ((fd = scope_open(path, O_RDONLY)) == -1) { + DBG(NULL); + goto out; + } + + if ((bytesRead = scope_read(fd, buf, blen)) <= 0) { + DBG(NULL); + goto out; + } + + // Replace all but the last null with spaces + for (i=0; i < (bytesRead - 1); i++) { + if (buf[i] == '\0') argc++; + } + +out: + if (fd != -1) scope_close(fd); + return argc; +} + bool osTimerStop(void) { diff --git a/os/linux/os.h b/os/linux/os.h index 669c2b256..5ba2e4f28 100644 --- a/os/linux/os.h +++ b/os/linux/os.h @@ -71,5 +71,6 @@ extern void osCreateSM(proc_id_t *, unsigned long); extern bool osMemPermAllow(void *, size_t, int, int); extern bool osMemPermRestore(void *, size_t, int); extern bool osGetBaseAddr(uint64_t *); +extern int osGetArgv(pid_t, char *, size_t); #endif //__OS_H__ diff --git a/src/wrap_go.c b/src/wrap_go.c index 02d02f75e..80696a431 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -454,229 +454,691 @@ free_go_str(char *str) { } */ -static bool -match_assy_instruction(void *addr, char *mnemonic) +/* + * Rewrites the container configuration for specific task ID and container namespace name. + * Please look into opencontainers Linux runtime-spec for details about the exact JSON struct. + * Following changes will be performed: + * - Add mount point + * `scope` will be mounted from host ("/usr/lib/appscope//scope") into the container ("/opt/scope") + * - Extend Environment variable + * `LD_PRELOAD` will contain the following entry `/opt/libscope.so` + * `SCOPE_SETUP_DONE=true` mark that configuration was processed + * - Add prestart hook + * execute scope extract operation to ensure using library with proper loader reference (musl/glibc) + */ +static void +rewriteOpenContainersConfig(const char *cWorkDir) { - csh dhandle = 0; - cs_arch arch; - cs_mode mode; - cs_insn *asm_inst = NULL; - unsigned int asm_count = 0; - uint64_t size = 32; - bool rc = FALSE; - - arch = CS_ARCH; - mode = CS_MODE; - - if (cs_open(arch, mode, &dhandle) != CS_ERR_OK) return FALSE; + //return; +#ifdef __x86_64__ + /* + * Need to extend the system stack size when calling cJSON_PrintUnformatted(). + */ + int arc; + char *exit_stack, *tstack, *gstack; + if ((exit_stack = scope_malloc(SCOPE_STACK_SIZE)) == NULL) { + return; + } - asm_count = cs_disasm(dhandle, addr, size, (uint64_t)addr, 0, &asm_inst); - if (asm_count <= 0) return FALSE; + tstack = exit_stack + SCOPE_STACK_SIZE; - if (!scope_strcmp((const char*)asm_inst->mnemonic, mnemonic)) rc = TRUE; + // save the original stack, switch to the tstack + __asm__ volatile ( + "mov %%rsp, %2 \n" + "mov %1, %%rsp \n" + : "=r"(arc) // output + : "m"(tstack), "m"(gstack) // input + : // clobbered register + ); +#endif - if (asm_inst) cs_free(asm_inst, asm_count); - cs_close(&dhandle); + char path[PATH_MAX] = {0}; + if (!cWorkDir) { + goto exit; + } - return rc; -} + if (scope_snprintf(path, sizeof(path), "%s/config.json", cWorkDir) < 0) { + goto exit; + } -static void * -getGoVersionAddr(const char* buf) -{ - int i; - Elf64_Ehdr *ehdr; - Elf64_Shdr *sections; - const char *section_strtab = NULL; - const char *sec_name; - const char *sec_data; + struct stat fileStat; + if (scope_stat(path, &fileStat) == -1) { + goto exit; + } - ehdr = (Elf64_Ehdr *)buf; - sections = (Elf64_Shdr *)(buf + ehdr->e_shoff); - section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; - const char magic[0xe] = "\xff Go buildinf:"; - void *go_build_ver_addr = NULL; + FILE *fp = scope_fopen(path, "r"); + if (!fp) { + goto exit; + } - for (i = 0; i < ehdr->e_shnum; i++) { - sec_name = section_strtab + sections[i].sh_name; - sec_data = (const char *)buf + sections[i].sh_offset; - // Since go1.13, the .go.buildinfo section has been added to - // identify where runtime.buildVersion exists, for the case where - // go apps have been stripped of their symbols. + /* + * Read the file contents into a string + */ + char *buf = (char *)scope_malloc(fileStat.st_size); + if (!buf) { + scope_fclose(fp); + goto exit; + } - // offset into sec_data field contents - // ----------------------------------------------------------- - // 0x0 build info magic = "\xff Go buildinf:" - // 0xe binary ptrSize - // 0xf endianness - // 0x10 pointer to string runtime.buildVersion - // 0x10 + ptrSize pointer to runtime.modinfo - // 0x10 + 2 * ptr size pointer to build flags + scope_fread(buf, sizeof(char), fileStat.st_size, fp); + scope_fclose(fp); - if (!scope_strcmp(sec_name, ".go.buildinfo") && - (sections[i].sh_size >= 0x18) && - (!scope_memcmp(&sec_data[0], magic, sizeof(magic))) && - (sec_data[0xe] == 0x08)) { // 64 bit executables only + cJSON *json = cJSON_Parse(buf); + scope_free(buf); + if (json == NULL) { + goto exit; + } - // debug/buildinfo/buildinfo.go - // If the endianness has the 2 bit set, then the pointers are zero - // and the 32-byte header is followed by varint-prefixed string data - // for the two string values we care about. - if (sec_data[0xf] == 0x00) { // little-endian - uint64_t *addressPtr = (uint64_t*)&sec_data[0x10]; - go_build_ver_addr = (void*)*addressPtr; - } else if (sec_data[0xf] == 0x02) { - scope_memmove(g_go_build_ver, (char*)&sec_data[0x21], 6); - g_go_build_ver[6] = '\0'; - go_build_ver_addr = &g_go_build_ver; - } + /* + * Handle process environment variables + * + "env":[ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=6735578591bb", + "TERM=xterm", + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + ], + */ + cJSON *procNode = cJSON_GetObjectItemCaseSensitive(json, "process"); + if (!procNode) { + procNode = cJSON_CreateObject(); + if (!procNode) { + cJSON_Delete(json); + goto exit; } + cJSON_AddItemToObject(json, "process", procNode); } - return go_build_ver_addr; -} - -static void * -getGoSymbol(const char *buf, char *sname, char *altname, char *mnemonic) -{ - int i; - Elf64_Addr symaddr = 0; - Elf64_Ehdr *ehdr; - Elf64_Shdr *sections; - const char *section_strtab = NULL; - const char *sec_name = NULL; - - if (!buf || !sname) return NULL; - ehdr = (Elf64_Ehdr *)buf; - sections = (Elf64_Shdr *)((char *)buf + ehdr->e_shoff); - section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + cJSON *envNodeArr = cJSON_GetObjectItemCaseSensitive(procNode, "env"); + if (envNodeArr) { + bool ldPreloadPresent = FALSE; + // Iterate over environment string array + size_t envSize = cJSON_GetArraySize(envNodeArr); + for (int i = 0; i < envSize ;++i) { + cJSON *item = cJSON_GetArrayItem(envNodeArr, i); + char *strItem = cJSON_GetStringValue(item); - for (i = 0; i < ehdr->e_shnum; i++) { - sec_name = section_strtab + sections[i].sh_name; - if (scope_strstr(sec_name, ".gopclntab")) { - const void *pclntab_addr = buf + sections[i].sh_offset; - /* - Go symbol table is stored in the .gopclntab section - More info: https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub - */ - uint32_t magic = *((const uint32_t *)(pclntab_addr)); - if (magic == GOPCLNTAB_MAGIC_112) { - uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); - const void *symtab_addr = pclntab_addr + 16; - - for(i=0; imnemonic, "mov") && - !scope_strcmp((const char*)asm_inst->op_str, "rcx, qword ptr fs:[0xfffffffffffffff8]")) || - // -buildmode=pie compiles to this: - (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && - !scope_strcmp((const char*)asm_inst->op_str, "rcx, -8")) || - (!scope_strcmp((const char*)asm_inst->mnemonic, "cmp") && - !scope_strcmp((const char*)asm_inst->op_str, "rsp, qword ptr [r14 + 0x10]")) || - (!scope_strcmp((const char*)asm_inst->mnemonic, "lea") && - scope_strstr((const char*)asm_inst->op_str, "r12, [rsp - ")) || - (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && - !scope_strcmp((const char*)asm_inst->op_str, "r10, rsi")) || - (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && - !scope_strcmp((const char*)asm_inst->op_str, "edi, dword ptr [rsp + 8]")); - } else if (g_arch == AARCH64) { - return ((!scope_strcmp((const char*)asm_inst->mnemonic, "ldr") && - scope_strstr((const char*)asm_inst->op_str, "[x28, #"))); + // There was no LD_PRELOAD in environment variables + if (ldPreloadPresent == FALSE) { + const char *const envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + for (int i = 0; i < 2 ;++i) { + cJSON *scopeEnvNode = cJSON_CreateString(envItems[i]); + if (!scopeEnvNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToArray(envNodeArr, scopeEnvNode); + } + } } else { - return FALSE; - } -} - -// Calculate the value to be added/subtracted at an add/sub instruction -// Returns an absolute value -static uint32_t -add_argument(cs_insn* asm_inst) + const char * envItems[2] = + { + "LD_PRELOAD=/opt/libscope.so", + "SCOPE_SETUP_DONE=true" + }; + envNodeArr = cJSON_CreateStringArray(envItems, 2); + if (!envNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(procNode, "env", envNodeArr); + } + + /* + * Handle process mounts + * + "mounts":[ + { + "destination":"/proc", + "type":"proc", + "source":"proc", + "options":[ + "nosuid", + "noexec", + "nodev" + ] + }, + ... + { + "destination":"/opt/scope", + "type":"bind", + "source":"/tmp/appscope/dev/scope", + "options":[ + "rbind", + "rprivate" + ] + } + */ + cJSON *mountNodeArr = cJSON_GetObjectItemCaseSensitive(json, "mounts"); + if (!mountNodeArr) { + mountNodeArr = cJSON_CreateArray(); + if (!mountNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(json, "mounts", mountNodeArr); + } + + cJSON *mountNode = cJSON_CreateObject(); + if (!mountNode) { + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "destination", "/opt/scope")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "type", "bind")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(mountNode, "source", "/tmp/appscope/dev/scope")) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + + const char *optItems[2] = + { + "rbind", + "rprivate" + }; + + cJSON *optNodeArr = cJSON_CreateStringArray(optItems, 2); + if (!optNodeArr) { + cJSON_Delete(mountNode); + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(mountNode, "options", optNodeArr); + cJSON_AddItemToArray(mountNodeArr, mountNode); + + /* + * Handle startContainer hooks process + * + "hooks":{ + "prestart":[ + { + "path":"/proc/1513/exe", + "args":[ + "libnetwork-setkey", + "-exec-root=/var/run/docker", + "6735578591bb3c5aebc91e5c702470c52d2c10cea52e4836604bf5a4a6c0f2eb", + "ec7e49ffc98c" + ] + } + ], + "startContainer":[ + { + "path":"/opt/scope" + "args":[ + "/opt/scope", + "extract", + "/opt/", + ] + }, + ] + */ + cJSON *hooksNode = cJSON_GetObjectItemCaseSensitive(json, "hooks"); + if (!hooksNode) { + hooksNode = cJSON_CreateObject(); + if (!hooksNode) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(json, "hooks", hooksNode); + } + + cJSON *startContainerNodeArr = cJSON_GetObjectItemCaseSensitive(hooksNode, "startContainer"); + if (!startContainerNodeArr) { + startContainerNodeArr = cJSON_CreateArray(); + if (!startContainerNodeArr) { + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(hooksNode, "startContainer", startContainerNodeArr); + } + + cJSON *startContainerNode = cJSON_CreateObject(); + if (!startContainerNode) { + cJSON_Delete(json); + goto exit; + } + + if (!cJSON_AddStringToObjLN(startContainerNode, "path", "/opt/scope")) { + cJSON_Delete(startContainerNode); + cJSON_Delete(json); + goto exit; + } + + const char *argsItems[3] = + { + "/opt/scope", + "extract", + "/opt" + }; + cJSON *argsNodeArr = cJSON_CreateStringArray(argsItems, 3); + if (!argsNodeArr) { + cJSON_Delete(startContainerNode); + cJSON_Delete(json); + goto exit; + } + cJSON_AddItemToObject(startContainerNode, "args", argsNodeArr); + cJSON_AddItemToArray(startContainerNodeArr, startContainerNode); + + char *jsonStr = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + + // Overwrite the file + fp = scope_fopen(path, "w"); + if (fp == NULL) { + cJSON_free(jsonStr); + goto exit; + } + + scope_fprintf(fp, "%s\n", jsonStr); + + cJSON_free(jsonStr); + scope_fclose(fp); + +exit: +#ifdef __x86_64__ + // Switch stack back to the original stack + __asm__ volatile ( + "mov %1, %%rsp \n" + : "=r"(arc) // output + : "r"(gstack) // inputs + : // clobbered register + ); + + scope_free(exit_stack); +#endif + // to handle aarch64 case + return; +} + +static void +containerStart(void) +{ + int i, argc; + char *buf; + const char *cWorkDir; + + if ((buf = scope_calloc(1, NCARGS)) == NULL) return; + + if ((argc = osGetArgv(g_proc.pid, buf, NCARGS)) == 0) return; + + sysprint("Scope: found runc"); + + for (i = 0; buf[i]; i += scope_strlen(&buf[i]) + 1) { + char *arg = &buf[i]; + + if (arg) { + sysprint("\t%s:%d %s %d argv %s\n", __FUNCTION__, __LINE__, g_proc.procname, argc, arg); + + if (scope_strstr(arg, "--bundle")) { + // work dir for the container + cWorkDir = &buf[i + scope_strlen(arg) + 1]; + if (cWorkDir) sysprint("\t%s:%d container path %s\n", __FUNCTION__, __LINE__, cWorkDir); + break; + } + } + } + + if (cWorkDir) rewriteOpenContainersConfig(cWorkDir); + scope_free(buf); +} + +static bool +match_assy_instruction(void *addr, char *mnemonic) +{ + csh dhandle = 0; + cs_arch arch; + cs_mode mode; + cs_insn *asm_inst = NULL; + unsigned int asm_count = 0; + uint64_t size = 32; + bool rc = FALSE; + + arch = CS_ARCH; + mode = CS_MODE; + + if (cs_open(arch, mode, &dhandle) != CS_ERR_OK) return FALSE; + + asm_count = cs_disasm(dhandle, addr, size, (uint64_t)addr, 0, &asm_inst); + if (asm_count <= 0) return FALSE; + + if (!scope_strcmp((const char*)asm_inst->mnemonic, mnemonic)) rc = TRUE; + + if (asm_inst) cs_free(asm_inst, asm_count); + cs_close(&dhandle); + + return rc; +} + +static void * +getGoVersionAddr(const char* buf) +{ + int i; + Elf64_Ehdr *ehdr; + Elf64_Shdr *sections; + const char *section_strtab = NULL; + const char *sec_name; + const char *sec_data; + + ehdr = (Elf64_Ehdr *)buf; + sections = (Elf64_Shdr *)(buf + ehdr->e_shoff); + section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + const char magic[0xe] = "\xff Go buildinf:"; + void *go_build_ver_addr = NULL; + + for (i = 0; i < ehdr->e_shnum; i++) { + sec_name = section_strtab + sections[i].sh_name; + sec_data = (const char *)buf + sections[i].sh_offset; + // Since go1.13, the .go.buildinfo section has been added to + // identify where runtime.buildVersion exists, for the case where + // go apps have been stripped of their symbols. + + // offset into sec_data field contents + // ----------------------------------------------------------- + // 0x0 build info magic = "\xff Go buildinf:" + // 0xe binary ptrSize + // 0xf endianness + // 0x10 pointer to string runtime.buildVersion + // 0x10 + ptrSize pointer to runtime.modinfo + // 0x10 + 2 * ptr size pointer to build flags + + if (!scope_strcmp(sec_name, ".go.buildinfo") && + (sections[i].sh_size >= 0x18) && + (!scope_memcmp(&sec_data[0], magic, sizeof(magic))) && + (sec_data[0xe] == 0x08)) { // 64 bit executables only + + // debug/buildinfo/buildinfo.go + // If the endianness has the 2 bit set, then the pointers are zero + // and the 32-byte header is followed by varint-prefixed string data + // for the two string values we care about. + if (sec_data[0xf] == 0x00) { // little-endian + uint64_t *addressPtr = (uint64_t*)&sec_data[0x10]; + go_build_ver_addr = (void*)*addressPtr; + } else if (sec_data[0xf] == 0x02) { + scope_memmove(g_go_build_ver, (char*)&sec_data[0x21], 6); + g_go_build_ver[6] = '\0'; + go_build_ver_addr = &g_go_build_ver; + } + } + } + return go_build_ver_addr; +} + +static Elf64_Addr +getSym12(const void *pclntab_addr, char *sname) +{ + Elf64_Addr symaddr = 0; + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + const void *symtab_addr = pclntab_addr + 16; + + for (int i = 0; i < sym_count; i++) { + uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + func_offset + 8)); + uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); + const char *func_name = (const char *)(pclntab_addr + name_offset); + + if (scope_strcmp(sname, func_name) == 0) { + symaddr = sym_addr; + scopeLog(CFG_LOG_TRACE, "symbol found %s = 0x%08lx\n", func_name, sym_addr); + break; + } + + symtab_addr += 16; + } + + return symaddr; +} + +static Elf64_Addr +getSym16(const void *pclntab_addr, char *sname, char *altname, char *mnemonic) +{ + Elf64_Addr symaddr = 0; + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + const void *symtab_addr = pclntab_addr + 16; + + for (int i = 0; i < sym_count; i++) { + uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + func_offset + 8)); + uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); + const char *func_name = (const char *)(pclntab_addr + name_offset); + + if (scope_strcmp(sname, func_name) == 0) { + symaddr = sym_addr; + scopeLog(CFG_LOG_TRACE, "symbol found %s = 0x%08lx\n", func_name, sym_addr); + break; + } + + // In go 1.17+ we need to ensure we find the correct symbol in the case of ambiguity + if (altname && mnemonic && + (scope_strcmp(altname, func_name) == 0) && + (match_assy_instruction((void *)sym_addr, mnemonic) == TRUE)) { + symaddr = sym_addr; + break; + } + + symtab_addr += 16; + } + + return symaddr; +} + +static Elf64_Addr +getSym1820(const void *pclntab_addr, char *sname, char *altname, char *mnemonic) +{ + Elf64_Addr symaddr = 0; + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + // In go 1.18 the funcname table and the pcln table are stored in the text section + uint64_t text_start = *((const uint64_t *)(pclntab_addr + (3 * 8))); + uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (4 * 8))); + //uint64_t funcnametab_addr = (uint64_t)(funcnametab_offset + pclntab_addr); + uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (8 * 8))); + // A "symbtab" is an entry in the pclntab, probably better known as a pcln + const void *symtab_addr = (const void *)(pclntab_addr + pclntab_offset); + + for (int i = 0; i < sym_count; i++) { + uint32_t func_offset = *((uint32_t *)(symtab_addr + 4)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 4)); + func_offset = *((uint32_t *)(symtab_addr)); + uint64_t sym_addr = (uint64_t)(func_offset + text_start); + const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); + if (scope_strcmp(sname, func_name) == 0) { + symaddr = sym_addr; + scopeLog(CFG_LOG_ERROR, "symbol found %s = 0x%08lx\n", func_name, sym_addr); + break; + } + + // In go 1.17+ we need to ensure we find the correct symbol in the case of ambiguity + if (altname && mnemonic && + (scope_strcmp(altname, func_name) == 0) && + (match_assy_instruction((void *)sym_addr, mnemonic) == TRUE)) { + symaddr = sym_addr; + break; + } + + symtab_addr += 8; + } + + return symaddr; +} + +static Elf64_Addr +embedPclntab(const char *buf, char *sname, char *altname, char *mnemonic) +{ + Elf64_Addr symaddr = 0; + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)buf; + Elf64_Shdr *sections = (Elf64_Shdr *)(buf + ehdr->e_shoff); + const char *section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + + for (int i = 0; i < ehdr->e_shnum; i++) { + const char *sec_name = section_strtab + sections[i].sh_name; + if (strstr(sec_name, "data.rel.ro") != 0) { + + const void *pclntab_addr = buf + sections[i].sh_offset; + unsigned char *data = (unsigned char *)pclntab_addr; + size_t slen = sections[i].sh_size; + size_t j; + + // Find the magic number in the pclntab header + for (j = 0; j <= slen; j += 4) { //0x3c8c80 + if (((data[j] == 0xf1) || (data[j] == 0xf0) || + (data[j] == 0xfa) || (data[j] == 0xfb)) && + data[j+1] == 0xff && + data[j+2] == 0xff && + data[j+3] == 0xff) { + //sysprint("%s:%d pclntab was recognized at %p\n", + // __FUNCTION__, __LINE__, &data[j]); + + switch (data[j]) { + // Go 18 and 20 + case 0xf1: + case 0xf0: + return getSym1820(&data[j], sname, altname, mnemonic); + // Go 16 + case 0xfa: + return getSym16(&data[j], sname, altname, mnemonic); + // Go 12 + case 0xfb: + return getSym12(&data[j], sname); + } + } + } + } + } + + return symaddr; +} + +static void * +getGoSymbol(const char *buf, char *sname, char *altname, char *mnemonic) +{ + int i; + bool found = FALSE; + Elf64_Addr symaddr = 0; + Elf64_Ehdr *ehdr; + Elf64_Shdr *sections; + const char *section_strtab = NULL; + const char *sec_name = NULL; + + if (!buf || !sname) return NULL; + + ehdr = (Elf64_Ehdr *)buf; + sections = (Elf64_Shdr *)((char *)buf + ehdr->e_shoff); + section_strtab = (char *)buf + sections[ehdr->e_shstrndx].sh_offset; + + for (i = 0; i < ehdr->e_shnum; i++) { + sec_name = section_strtab + sections[i].sh_name; + if (scope_strstr(sec_name, ".gopclntab")) { + found = TRUE; + const void *pclntab_addr = buf + sections[i].sh_offset; + /* + * The Go symbol table is stored in the .gopclntab section + * More info: https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub + */ + uint32_t magic = *((const uint32_t *)(pclntab_addr)); + if (magic == GOPCLNTAB_MAGIC_112) { + symaddr = getSym12(pclntab_addr, sname); + } else if (magic == GOPCLNTAB_MAGIC_116) { + symaddr = getSym16(pclntab_addr, sname, altname, mnemonic); + } else if ((magic == GOPCLNTAB_MAGIC_118) || (magic == GOPCLNTAB_MAGIC_120)) { + symaddr = getSym1820(pclntab_addr, sname, altname, mnemonic); + } else { + scopeLog(CFG_LOG_DEBUG, "Invalid header in .gopclntab"); + break; + } + break; + } + } + + // if no .gopclntab section was found, check embedded + if ((found == FALSE) && ((symaddr = embedPclntab(buf, sname, altname, mnemonic)) == 0)) { + return NULL; + } + + return (void *)symaddr; +} + +// Detect the beginning of a Go Function +// by identifying instructions in the preamble. +static bool +looks_like_first_inst_of_go_func(cs_insn* asm_inst) +{ + if (g_arch == X86_64) { + return (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && + !scope_strcmp((const char*)asm_inst->op_str, "rcx, qword ptr fs:[0xfffffffffffffff8]")) || + // -buildmode=pie compiles to this: + (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && + !scope_strcmp((const char*)asm_inst->op_str, "rcx, -8")) || + (!scope_strcmp((const char*)asm_inst->mnemonic, "cmp") && + !scope_strcmp((const char*)asm_inst->op_str, "rsp, qword ptr [r14 + 0x10]")) || + (!scope_strcmp((const char*)asm_inst->mnemonic, "lea") && + scope_strstr((const char*)asm_inst->op_str, "r12, [rsp - ")) || + (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && + !scope_strcmp((const char*)asm_inst->op_str, "r10, rsi")) || + (!scope_strcmp((const char*)asm_inst->mnemonic, "mov") && + !scope_strcmp((const char*)asm_inst->op_str, "edi, dword ptr [rsp + 8]")); + } else if (g_arch == AARCH64) { + return ((!scope_strcmp((const char*)asm_inst->mnemonic, "ldr") && + scope_strstr((const char*)asm_inst->op_str, "[x28, #"))); + } else { + return FALSE; + } +} + +// Calculate the value to be added/subtracted at an add/sub instruction +// Returns an absolute value +static uint32_t +add_argument(cs_insn* asm_inst) { if (!asm_inst) return 0; @@ -1028,6 +1490,10 @@ initGoHook(elf_buf_t *ebuf) return; // don't install our hooks } + if (scope_strstr(g_proc.procname, "runc") != NULL) { + containerStart(); + } + uint64_t *ReadFrame_addr; if (((ReadFrame_addr = getSymbol(ebuf->buf, "net/http.(*http2Framer).ReadFrame")) == 0) && ((ReadFrame_addr = getGoSymbol(ebuf->buf, "net/http.(*http2Framer).ReadFrame", NULL, NULL)) == 0)) { @@ -1338,7 +1804,6 @@ mountDirs(char *src, char *target, char *fstype) return TRUE; } - // Extract data from syscalls. Values are available in registers saved on sys_stack. static void c_syscall(char *sys_stack, char *g_stack) @@ -1369,6 +1834,10 @@ c_syscall(char *sys_stack, char *g_stack) } funcprint("Scope: open of %ld\n", rc); + //if (scope_strstr(g_proc.procname, "runc") != NULL) { + // containerStart(); + //} + doOpen(rc, path, FD, "open"); } break; @@ -1630,591 +2099,215 @@ c_http2_server_read(char *sys_stack, char *g_stack) scope_memmove(frame, buf, HTTP2_FRAME_HEADER_LEN); scope_memmove(frame + HTTP2_FRAME_HEADER_LEN, readBuf, rc - HTTP2_FRAME_HEADER_LEN); - doProtocol((uint64_t)0, fd, frame, rc, TLSRX, BUF); - funcprint("Scope: c_http2_server_read of %d\n", fd); - - scope_free(frame); -} - -EXPORTON void * -go_http2_server_read(char *stackptr) -{ - return do_cfunc(stackptr, c_http2_server_read, tap_entry(tap_http2_server_read)->assembly_fn); -} - -// Extract data from net/http.(*http2serverConn).Flush (tls http2 server write) -static void -c_http2_server_write(char *sys_stack, char *g_stack) -{ - uint64_t sc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_write_sc); - if (!sc) return; - uint64_t fr = *(uint64_t *)(sc + g_go_schema->struct_offsets.sc_to_fr); - if (!fr) return; - char *writeBuf = (char *)*(uint64_t *)(fr + g_go_schema->struct_offsets.fr_to_writeBuf); - if (!writeBuf) return; - uint64_t conn_if = (sc + g_go_schema->struct_offsets.sc_to_conn); - if (!conn_if) return; - uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); - if (!tcpConn) return; - - int fd = getFDFromConn(tcpConn); - - /* - * If Proto is not yet detected, wait for the preface string to be received. - * We expect that the only write that occurs in this case is a settings - * frame. Refer to src/net/http/h2_bundle.go:(sc *httpServerConn) serve(). - * This is related to the nature of the parallel/async behavior of sc. - */ - if (isProtocolSet(fd) == FALSE) return; - - uint8_t *newbuf = (uint8_t *)writeBuf; - uint32_t rc = 0; - rc += newbuf[0] << 16; - rc += newbuf[1] << 8; - rc += newbuf[2]; - rc += HTTP2_FRAME_HEADER_LEN; - - // At times, the buffer contains 0. We don't want to call doProtocol when - // the header is empty (length and type bytes are all 0). - // Especially since it will set PROTO to false if it's the first call to doProtocol - // on this socket. - if ((rc < 1) && (!newbuf[3])) return; - - funcprint("Scope: c_http2_server_write of %d %i\n", fd, rc); - doProtocol((uint64_t)0, fd, writeBuf, rc, TLSTX, BUF); -} - -EXPORTON void * -go_http2_server_write(char *stackptr) -{ - return do_cfunc(stackptr, c_http2_server_write, tap_entry(tap_http2_server_write)->assembly_fn); -} - -// Extract data from net/http.(*http2serverConn).readPreface (tls http2 server write) -static void -c_http2_server_preface(char *sys_stack, char *g_stack) -{ - // In this function in <= go 16 we have to go to the callee to get input params and return values - g_stack -= g_go_schema->arg_offsets.c_http2_server_preface_callee; - - uint64_t *rc = (uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_preface_rc); - if ((rc == NULL) || (rc == (uint64_t *)0xffffffff)) return; - if (*rc != 0) return; - uint64_t sc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_preface_sc); - if (!sc) return; - uint64_t fr = *(uint64_t *)(sc + g_go_schema->struct_offsets.sc_to_fr); - if (!fr) return; - uint64_t conn_if = (sc + g_go_schema->struct_offsets.sc_to_conn); - if (!conn_if) return; - uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); - if (!tcpConn) return; - - int fd = getFDFromConn(tcpConn); - - funcprint("Scope: c_http2_server_preface of %d %ld\n", fd, *rc); - doProtocol((uint64_t)0, fd, PRI_STR, PRI_STR_LEN - 1, TLSRX, BUF); -} - -EXPORTON void * -go_http2_server_preface(char *stackptr) -{ - return do_cfunc(stackptr, c_http2_server_preface, tap_entry(tap_http2_server_preface)->assembly_fn); -} - -/* - Offsets here may be outdated/incorrect for certain versions. Leaving for reference: - Our variable Memory loc Go variable - cc stackaddr + 68 cc - fr sc + 0x130 cc.fr - buf fr + 0x40 cc.fr.headerBuf - readBuf fr + 0x58 cc.fr.readBuf - tcpConn sc + 0x10 cc.conn - */ -// Extract data from net/http.(*http2clientConnReadLoop).run (tls http2 client read) -static void -c_http2_client_read(char *sys_stack, char *g_stack) -{ - uint64_t cc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_read_cc); - if (!cc) return; - uint64_t fr = *(uint64_t *)(cc + g_go_schema->struct_offsets.cc_to_fr); - if (!fr) return; - char *buf = (char *)(fr + g_go_schema->struct_offsets.fr_to_headerBuf); - if (!buf) return; - char *readBuf = (char *)*(uint64_t *)(fr + g_go_schema->struct_offsets.fr_to_readBuf); - if (!readBuf) return; - uint64_t conn_if = (cc + g_go_schema->struct_offsets.cc_to_tconn); - if (!conn_if) return; - uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); - if (!tcpConn) return; - - int fd = getFDFromConn(tcpConn); - - uint8_t *newbuf = (uint8_t *)buf; - uint32_t rc = 0; - rc += newbuf[0] << 16; - rc += newbuf[1] << 8; - rc += newbuf[2]; - if (rc < 1) return; - rc += HTTP2_FRAME_HEADER_LEN; - - char *frame = (char *)scope_malloc(rc); - if (!frame) return; - scope_memmove(frame, buf, HTTP2_FRAME_HEADER_LEN); - scope_memmove(frame + HTTP2_FRAME_HEADER_LEN, readBuf, rc - HTTP2_FRAME_HEADER_LEN); - - doProtocol((uint64_t)0, fd, frame, rc, TLSRX, BUF); - funcprint("Scope: c_http2_client_read of %d\n", fd); - - scope_free(frame); -} - -EXPORTON void * -go_http2_client_read(char *stackptr) -{ - return do_cfunc(stackptr, c_http2_client_read, tap_entry(tap_http2_client_read)->assembly_fn); -} - -// Extract data from net/http.http2stickyErrWriter.Write (tls http2 client write) -static void -c_http2_client_write(char *sys_stack, char *g_stack) -{ - uint64_t tcpConn = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_tcpConn); - if (!tcpConn) return; - char *buf = (char *)*(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_buf); - if (!buf) return; - uint64_t rc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_rc); - if (rc < 1) return; - - int fd = getFDFromConn(tcpConn); - - doProtocol((uint64_t)0, fd, buf, rc, TLSTX, BUF); - funcprint("Scope: c_http2_client_write of %d\n", fd); -} - -EXPORTON void * -go_http2_client_write(char *stackptr) -{ - return do_cfunc(stackptr, c_http2_client_write, tap_entry(tap_http2_client_write)->assembly_fn); -} - -extern void handleExit(void); -static void -c_exit(char *sys_stack) -{ -#ifdef __x86_64__ - /* - * Need to extend the system stack size when calling handleExit(). - * We see that the stack is exceeded now that we are using an internal libc. - */ - int arc; - char *exit_stack, *tstack, *gstack; - if ((exit_stack = scope_malloc(EXIT_STACK_SIZE)) == NULL) { - return; - } - - tstack = exit_stack + EXIT_STACK_SIZE; - - // save the original stack, switch to the tstack - __asm__ volatile ( - "mov %%rsp, %2 \n" - "mov %1, %%rsp \n" - : "=r"(arc) // output - : "m"(tstack), "m"(gstack) // input - : // clobbered register - ); -#endif - // don't use stackaddr; patch_first_instruction() does not provide - // frame_size, so stackaddr isn't usable - funcprint("c_exit\n"); - - int i; - struct timespec ts = {.tv_sec = 0, .tv_nsec = 10000}; // 10 us - - // ensure the circular buffer is empty - for (i = 0; i < 100; i++) { - if (cmdCbufEmpty(g_ctl)) break; - sigSafeNanosleep(&ts); - } - - handleExit(); - // flush the data - sigSafeNanosleep(&ts); -#ifdef __x86_64__ - // Switch stack back to the original stack - __asm__ volatile ( - "mov %1, %%rsp \n" - : "=r"(arc) // output - : "r"(gstack) // inputs - : // clobbered register - ); - - scope_free(exit_stack); -#endif -} - -EXPORTON void * -go_exit(char *stackptr) -{ - return do_cfunc(stackptr, c_exit, tap_entry(tap_exit)->assembly_fn); -} - -EXPORTON void * -go_die(char *stackptr) -{ - return do_cfunc(stackptr, c_exit, tap_entry(tap_die)->assembly_fn); -} - -static void -c_sighandler(char *sys_stack, char *g_stack) -{ - if (snapshotIsEnabled() == FALSE) return; - - int sig = *(int *)(sys_stack + g_go_schema->arg_offsets.c_signal_sig); - siginfo_t *info = (siginfo_t *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_signal_info); + doProtocol((uint64_t)0, fd, frame, rc, TLSRX, BUF); + funcprint("Scope: c_http2_server_read of %d\n", fd); - if ((sig == SIGILL) || (sig == SIGSEGV) || (sig == SIGBUS) || (sig == SIGFPE)) { - snapshotSignalHandler(sig, info, NULL); - } + scope_free(frame); } EXPORTON void * -go_sighandler(char *stackptr) +go_http2_server_read(char *stackptr) { - return do_cfunc(stackptr, c_sighandler, tap_entry(tap_sighandler)->assembly_fn); + return do_cfunc(stackptr, c_http2_server_read, tap_entry(tap_http2_server_read)->assembly_fn); } -/* - * Rewrites the container configuration for specific task ID and container namespace name. - * Please look into opencontainers Linux runtime-spec for details about the exact JSON struct. - * Following changes will be performed: - * - Add mount point - * `scope` will be mounted from host ("/usr/lib/appscope//scope") into the container ("/opt/scope") - * - Extend Environment variable - * `LD_PRELOAD` will contain the following entry `/opt/libscope.so` - * `SCOPE_SETUP_DONE=true` mark that configuration was processed - * - Add prestart hook - * execute scope extract operation to ensure using library with proper loader reference (musl/glibc) - */ +// Extract data from net/http.(*http2serverConn).Flush (tls http2 server write) static void -rewriteOpenContainersConfig(const char *taskId, const char *nsName) +c_http2_server_write(char *sys_stack, char *g_stack) { -#ifdef __x86_64__ - /* - * Need to extend the system stack size when calling cJSON_PrintUnformatted(). - */ - int arc; - char *exit_stack, *tstack, *gstack; - if ((exit_stack = scope_malloc(SCOPE_STACK_SIZE)) == NULL) { - return; - } - - tstack = exit_stack + SCOPE_STACK_SIZE; - - // save the original stack, switch to the tstack - __asm__ volatile ( - "mov %%rsp, %2 \n" - "mov %1, %%rsp \n" - : "=r"(arc) // output - : "m"(tstack), "m"(gstack) // input - : // clobbered register - ); -#endif - - char path[PATH_MAX] = {0}; - if ((!taskId) || (!nsName)) { - goto exit; - } - - if (scope_snprintf(path, sizeof(path), "/run/containerd/io.containerd.runtime.v2.task/%s/%s/config.json", nsName, taskId) < 0) { - goto exit; - } - - struct stat fileStat; - if (scope_stat(path, &fileStat) == -1) { - goto exit; - } + uint64_t sc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_write_sc); + if (!sc) return; + uint64_t fr = *(uint64_t *)(sc + g_go_schema->struct_offsets.sc_to_fr); + if (!fr) return; + char *writeBuf = (char *)*(uint64_t *)(fr + g_go_schema->struct_offsets.fr_to_writeBuf); + if (!writeBuf) return; + uint64_t conn_if = (sc + g_go_schema->struct_offsets.sc_to_conn); + if (!conn_if) return; + uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); + if (!tcpConn) return; - FILE *fp = scope_fopen(path, "r"); - if (!fp) { - goto exit; - } + int fd = getFDFromConn(tcpConn); /* - * Read the file contents into a string - */ - char *buf = (char *)scope_malloc(fileStat.st_size); - if (!buf) { - scope_fclose(fp); - goto exit; - } - - scope_fread(buf, sizeof(char), fileStat.st_size, fp); - scope_fclose(fp); + * If Proto is not yet detected, wait for the preface string to be received. + * We expect that the only write that occurs in this case is a settings + * frame. Refer to src/net/http/h2_bundle.go:(sc *httpServerConn) serve(). + * This is related to the nature of the parallel/async behavior of sc. + */ + if (isProtocolSet(fd) == FALSE) return; - cJSON *json = cJSON_Parse(buf); - scope_free(buf); - if (json == NULL) { - goto exit; - } + uint8_t *newbuf = (uint8_t *)writeBuf; + uint32_t rc = 0; + rc += newbuf[0] << 16; + rc += newbuf[1] << 8; + rc += newbuf[2]; + rc += HTTP2_FRAME_HEADER_LEN; - /* - * Handle process environment variables - * - "env":[ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=6735578591bb", - "TERM=xterm", - "LD_PRELOAD=/opt/libscope.so", - "SCOPE_SETUP_DONE=true" - ], - */ - cJSON *procNode = cJSON_GetObjectItemCaseSensitive(json, "process"); - if (!procNode) { - procNode = cJSON_CreateObject(); - if (!procNode) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(json, "process", procNode); - } + // At times, the buffer contains 0. We don't want to call doProtocol when + // the header is empty (length and type bytes are all 0). + // Especially since it will set PROTO to false if it's the first call to doProtocol + // on this socket. + if ((rc < 1) && (!newbuf[3])) return; - cJSON *envNodeArr = cJSON_GetObjectItemCaseSensitive(procNode, "env"); - if (envNodeArr) { - bool ldPreloadPresent = FALSE; - // Iterate over environment string array - size_t envSize = cJSON_GetArraySize(envNodeArr); - for (int i = 0; i < envSize ;++i) { - cJSON *item = cJSON_GetArrayItem(envNodeArr, i); - char *strItem = cJSON_GetStringValue(item); + funcprint("Scope: c_http2_server_write of %d %i\n", fd, rc); + doProtocol((uint64_t)0, fd, writeBuf, rc, TLSTX, BUF); +} - if (scope_strncmp("LD_PRELOAD=", strItem, sizeof("LD_PRELOAD=")-1) == 0) { - size_t itemLen = scope_strlen(strItem); - size_t newLdprelLen = itemLen + sizeof("/opt/libscope.so:") - 1; - char *newLdPreloadLib = scope_calloc(1, newLdprelLen); - if (!newLdPreloadLib) { - cJSON_Delete(json); - goto exit; - } - scope_strncpy(newLdPreloadLib, "LD_PRELOAD=/opt/libscope.so:", sizeof("LD_PRELOAD=/opt/libscope.so:") - 1); - scope_strcat(newLdPreloadLib, strItem + sizeof("LD_PRELOAD=") - 1); - cJSON *newLdPreloadLibObj = cJSON_CreateString(newLdPreloadLib); - if (!newLdPreloadLibObj) { - scope_free(newLdPreloadLib); - cJSON_Delete(json); - goto exit; - } - cJSON_ReplaceItemInArray(envNodeArr, i, newLdPreloadLibObj); - scope_free(newLdPreloadLib); +EXPORTON void * +go_http2_server_write(char *stackptr) +{ + return do_cfunc(stackptr, c_http2_server_write, tap_entry(tap_http2_server_write)->assembly_fn); +} - cJSON *scopeEnvNode = cJSON_CreateString("SCOPE_SETUP_DONE=true"); - if (!scopeEnvNode) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToArray(envNodeArr, scopeEnvNode); - ldPreloadPresent = TRUE; - break; - } else if (scope_strncmp("SCOPE_SETUP_DONE=true", strItem, sizeof("SCOPE_SETUP_DONE=true")-1) == 0) { - // we are done here - cJSON_Delete(json); - goto exit; - } - } +// Extract data from net/http.(*http2serverConn).readPreface (tls http2 server write) +static void +c_http2_server_preface(char *sys_stack, char *g_stack) +{ + // In this function in <= go 16 we have to go to the callee to get input params and return values + g_stack -= g_go_schema->arg_offsets.c_http2_server_preface_callee; + uint64_t *rc = (uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_preface_rc); + if ((rc == NULL) || (rc == (uint64_t *)0xffffffff)) return; + if (*rc != 0) return; + uint64_t sc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_server_preface_sc); + if (!sc) return; + uint64_t fr = *(uint64_t *)(sc + g_go_schema->struct_offsets.sc_to_fr); + if (!fr) return; + uint64_t conn_if = (sc + g_go_schema->struct_offsets.sc_to_conn); + if (!conn_if) return; + uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); + if (!tcpConn) return; - // There was no LD_PRELOAD in environment variables - if (ldPreloadPresent == FALSE) { - const char *const envItems[2] = - { - "LD_PRELOAD=/opt/libscope.so", - "SCOPE_SETUP_DONE=true" - }; - for (int i = 0; i < 2 ;++i) { - cJSON *scopeEnvNode = cJSON_CreateString(envItems[i]); - if (!scopeEnvNode) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToArray(envNodeArr, scopeEnvNode); - } - } - } else { - const char * envItems[2] = - { - "LD_PRELOAD=/opt/libscope.so", - "SCOPE_SETUP_DONE=true" - }; - envNodeArr = cJSON_CreateStringArray(envItems, 2); - if (!envNodeArr) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(procNode, "env", envNodeArr); - } + int fd = getFDFromConn(tcpConn); - /* - * Handle process mounts - * - "mounts":[ - { - "destination":"/proc", - "type":"proc", - "source":"proc", - "options":[ - "nosuid", - "noexec", - "nodev" - ] - }, - ... - { - "destination":"/opt/scope", - "type":"bind", - "source":"/tmp/appscope/dev/scope", - "options":[ - "rbind", - "rprivate" - ] - } - */ - cJSON *mountNodeArr = cJSON_GetObjectItemCaseSensitive(json, "mounts"); - if (!mountNodeArr) { - mountNodeArr = cJSON_CreateArray(); - if (!mountNodeArr) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(json, "mounts", mountNodeArr); - } + funcprint("Scope: c_http2_server_preface of %d %ld\n", fd, *rc); + doProtocol((uint64_t)0, fd, PRI_STR, PRI_STR_LEN - 1, TLSRX, BUF); +} - cJSON *mountNode = cJSON_CreateObject(); - if (!mountNode) { - cJSON_Delete(json); - goto exit; - } +EXPORTON void * +go_http2_server_preface(char *stackptr) +{ + return do_cfunc(stackptr, c_http2_server_preface, tap_entry(tap_http2_server_preface)->assembly_fn); +} - if (!cJSON_AddStringToObjLN(mountNode, "destination", "/opt/scope")) { - cJSON_Delete(mountNode); - cJSON_Delete(json); - goto exit; - } +/* + Offsets here may be outdated/incorrect for certain versions. Leaving for reference: + Our variable Memory loc Go variable + cc stackaddr + 68 cc + fr sc + 0x130 cc.fr + buf fr + 0x40 cc.fr.headerBuf + readBuf fr + 0x58 cc.fr.readBuf + tcpConn sc + 0x10 cc.conn + */ +// Extract data from net/http.(*http2clientConnReadLoop).run (tls http2 client read) +static void +c_http2_client_read(char *sys_stack, char *g_stack) +{ + uint64_t cc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_read_cc); + if (!cc) return; + uint64_t fr = *(uint64_t *)(cc + g_go_schema->struct_offsets.cc_to_fr); + if (!fr) return; + char *buf = (char *)(fr + g_go_schema->struct_offsets.fr_to_headerBuf); + if (!buf) return; + char *readBuf = (char *)*(uint64_t *)(fr + g_go_schema->struct_offsets.fr_to_readBuf); + if (!readBuf) return; + uint64_t conn_if = (cc + g_go_schema->struct_offsets.cc_to_tconn); + if (!conn_if) return; + uint64_t tcpConn = *(uint64_t *)(conn_if + g_go_schema->struct_offsets.iface_data); + if (!tcpConn) return; - if (!cJSON_AddStringToObjLN(mountNode, "type", "bind")) { - cJSON_Delete(mountNode); - cJSON_Delete(json); - goto exit; - } + int fd = getFDFromConn(tcpConn); - if (!cJSON_AddStringToObjLN(mountNode, "source", "/tmp/appscope/dev/scope")) { - cJSON_Delete(mountNode); - cJSON_Delete(json); - goto exit; - } + uint8_t *newbuf = (uint8_t *)buf; + uint32_t rc = 0; + rc += newbuf[0] << 16; + rc += newbuf[1] << 8; + rc += newbuf[2]; + if (rc < 1) return; + rc += HTTP2_FRAME_HEADER_LEN; - const char *optItems[2] = - { - "rbind", - "rprivate" - }; + char *frame = (char *)scope_malloc(rc); + if (!frame) return; + scope_memmove(frame, buf, HTTP2_FRAME_HEADER_LEN); + scope_memmove(frame + HTTP2_FRAME_HEADER_LEN, readBuf, rc - HTTP2_FRAME_HEADER_LEN); - cJSON *optNodeArr = cJSON_CreateStringArray(optItems, 2); - if (!optNodeArr) { - cJSON_Delete(mountNode); - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(mountNode, "options", optNodeArr); - cJSON_AddItemToArray(mountNodeArr, mountNode); + doProtocol((uint64_t)0, fd, frame, rc, TLSRX, BUF); + funcprint("Scope: c_http2_client_read of %d\n", fd); - /* - * Handle startContainer hooks process - * - "hooks":{ - "prestart":[ - { - "path":"/proc/1513/exe", - "args":[ - "libnetwork-setkey", - "-exec-root=/var/run/docker", - "6735578591bb3c5aebc91e5c702470c52d2c10cea52e4836604bf5a4a6c0f2eb", - "ec7e49ffc98c" - ] - } - ], - "startContainer":[ - { - "path":"/opt/scope" - "args":[ - "/opt/scope", - "extract", - "/opt/", - ] - }, - ] - */ - cJSON *hooksNode = cJSON_GetObjectItemCaseSensitive(json, "hooks"); - if (!hooksNode) { - hooksNode = cJSON_CreateObject(); - if (!hooksNode) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(json, "hooks", hooksNode); - } + scope_free(frame); +} - cJSON *startContainerNodeArr = cJSON_GetObjectItemCaseSensitive(hooksNode, "startContainer"); - if (!startContainerNodeArr) { - startContainerNodeArr = cJSON_CreateArray(); - if (!startContainerNodeArr) { - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(hooksNode, "startContainer", startContainerNodeArr); - } +EXPORTON void * +go_http2_client_read(char *stackptr) +{ + return do_cfunc(stackptr, c_http2_client_read, tap_entry(tap_http2_client_read)->assembly_fn); +} - cJSON *startContainerNode = cJSON_CreateObject(); - if (!startContainerNode) { - cJSON_Delete(json); - goto exit; - } +// Extract data from net/http.http2stickyErrWriter.Write (tls http2 client write) +static void +c_http2_client_write(char *sys_stack, char *g_stack) +{ + uint64_t tcpConn = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_tcpConn); + if (!tcpConn) return; + char *buf = (char *)*(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_buf); + if (!buf) return; + uint64_t rc = *(uint64_t *)(g_stack + g_go_schema->arg_offsets.c_http2_client_write_rc); + if (rc < 1) return; - if (!cJSON_AddStringToObjLN(startContainerNode, "path", "/opt/scope")) { - cJSON_Delete(startContainerNode); - cJSON_Delete(json); - goto exit; - } + int fd = getFDFromConn(tcpConn); - const char *argsItems[3] = - { - "/opt/scope", - "extract", - "/opt" - }; - cJSON *argsNodeArr = cJSON_CreateStringArray(argsItems, 3); - if (!argsNodeArr) { - cJSON_Delete(startContainerNode); - cJSON_Delete(json); - goto exit; - } - cJSON_AddItemToObject(startContainerNode, "args", argsNodeArr); - cJSON_AddItemToArray(startContainerNodeArr, startContainerNode); + doProtocol((uint64_t)0, fd, buf, rc, TLSTX, BUF); + funcprint("Scope: c_http2_client_write of %d\n", fd); +} - char *jsonStr = cJSON_PrintUnformatted(json); - cJSON_Delete(json); +EXPORTON void * +go_http2_client_write(char *stackptr) +{ + return do_cfunc(stackptr, c_http2_client_write, tap_entry(tap_http2_client_write)->assembly_fn); +} - // Overwrite the file - fp = scope_fopen(path, "w"); - if (fp == NULL) { - cJSON_free(jsonStr); - goto exit; +extern void handleExit(void); +static void +c_exit(char *sys_stack) +{ +#ifdef __x86_64__ + /* + * Need to extend the system stack size when calling handleExit(). + * We see that the stack is exceeded now that we are using an internal libc. + */ + int arc; + char *exit_stack, *tstack, *gstack; + if ((exit_stack = scope_malloc(EXIT_STACK_SIZE)) == NULL) { + return; } - scope_fprintf(fp, "%s\n", jsonStr); + tstack = exit_stack + EXIT_STACK_SIZE; - cJSON_free(jsonStr); - scope_fclose(fp); + // save the original stack, switch to the tstack + __asm__ volatile ( + "mov %%rsp, %2 \n" + "mov %1, %%rsp \n" + : "=r"(arc) // output + : "m"(tstack), "m"(gstack) // input + : // clobbered register + ); +#endif + // don't use stackaddr; patch_first_instruction() does not provide + // frame_size, so stackaddr isn't usable + funcprint("c_exit\n"); -exit: + int i; + struct timespec ts = {.tv_sec = 0, .tv_nsec = 10000}; // 10 us + + // ensure the circular buffer is empty + for (i = 0; i < 100; i++) { + if (cmdCbufEmpty(g_ctl)) break; + sigSafeNanosleep(&ts); + } + + handleExit(); + // flush the data + sigSafeNanosleep(&ts); #ifdef __x86_64__ // Switch stack back to the original stack __asm__ volatile ( @@ -2226,16 +2319,49 @@ rewriteOpenContainersConfig(const char *taskId, const char *nsName) scope_free(exit_stack); #endif - // to handle aarch64 case - return; +} + +EXPORTON void * +go_exit(char *stackptr) +{ + return do_cfunc(stackptr, c_exit, tap_entry(tap_exit)->assembly_fn); +} + +EXPORTON void * +go_die(char *stackptr) +{ + return do_cfunc(stackptr, c_exit, tap_entry(tap_die)->assembly_fn); +} + +static void +c_sighandler(char *sys_stack, char *g_stack) +{ + if (snapshotIsEnabled() == FALSE) return; + + int sig = *(int *)(sys_stack + g_go_schema->arg_offsets.c_signal_sig); + siginfo_t *info = (siginfo_t *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_signal_info); + + if ((sig == SIGILL) || (sig == SIGSEGV) || (sig == SIGBUS) || (sig == SIGFPE)) { + snapshotSignalHandler(sig, info, NULL); + } +} + +EXPORTON void * +go_sighandler(char *stackptr) +{ + return do_cfunc(stackptr, c_sighandler, tap_entry(tap_sighandler)->assembly_fn); } static void c_forkExec(char *sys_stack, char *g_stack) { + //return; + int i; - char *argv0 = (char *)(uint64_t)(sys_stack + 0); - uint64_t *argvv = (uint64_t *)*(uint64_t *)(sys_stack + 0x10); + //char *argv0 = (char *)(uint64_t)(sys_stack + 0x0); + //uint64_t *argvv = (uint64_t *)*(uint64_t *)(sys_stack + 0x10); + char *argv0 = (char *)*(uint64_t *)(g_stack + 0x08); + uint64_t *argvv = (uint64_t *)*(uint64_t *)(g_stack + 0x18); /* * TBD: in order to call scope_strstr() with a needle longer @@ -2249,7 +2375,7 @@ c_forkExec(char *sys_stack, char *g_stack) * the cause of this issue. * Will look into this directly. */ - + return; char *cmd = go_str(argv0, TRUE); // TBD: add a check for curent proc containerd when stack is aligned if (!scope_strstr(cmd, "runc")) { @@ -2288,7 +2414,7 @@ c_forkExec(char *sys_stack, char *g_stack) // Update task configuration if (updateCfg) { - rewriteOpenContainersConfig(taskUniqueId, nsName); + //rewriteOpenContainersConfig(taskUniqueId, nsName); } scope_free(nsName); scope_free(taskUniqueId); @@ -2299,3 +2425,4 @@ go_forkExec(char *stackptr) { return do_cfunc(stackptr, c_forkExec, tap_entry(tap_forkExec)->assembly_fn); } + From 0366e6954ed9c9af39886d557c51ddb58b98d91a Mon Sep 17 00:00:00 2001 From: iapaddler Date: Wed, 7 Jun 2023 19:13:31 +0000 Subject: [PATCH 20/25] Merged OCI config. Remove auto mount code from libscope and loader. --- src/gocontext.S | 3 - src/gocontext.h | 1 - src/gocontext_arm.S | 3 - src/scopestdlib.c | 8 -- src/scopestdlib.h | 1 - src/utils.c | 224 ----------------------------------------- src/utils.h | 3 - src/wrap_go.c | 235 -------------------------------------------- 8 files changed, 478 deletions(-) diff --git a/src/gocontext.S b/src/gocontext.S index 8cc3bce91..6dbf52f41 100644 --- a/src/gocontext.S +++ b/src/gocontext.S @@ -96,8 +96,6 @@ je 2f cmp $SYS_close, %eax je 2f - //cmp $SYS_mount, %eax - //je 2f lea \retptr@GOTPCREL(%rip), %r13 mov (%r13), %r13 @@ -221,7 +219,6 @@ c_syscall_handler: interpose_system_stack go_hook_exit go_exit c_handler interpose_system_stack go_hook_die go_die c_handler interpose_system_stack go_hook_sighandler go_sighandler c_handler - interpose_system_stack go_hook_forkExec go_forkExec c_handler syscall_filter go_hook_reg_syscall hook_syscall g_syscall_return syscall_filter go_hook_reg_rawsyscall hook_rawsyscall g_rawsyscall_return diff --git a/src/gocontext.h b/src/gocontext.h index 949bbd17c..8de73d685 100644 --- a/src/gocontext.h +++ b/src/gocontext.h @@ -133,6 +133,5 @@ extern void go_hook_reg_http2_client_write(void); extern void go_hook_exit(void); extern void go_hook_die(void); extern void go_hook_sighandler(void); -extern void go_hook_forkExec(void); #endif // __GOTCONTEXT_H__ diff --git a/src/gocontext_arm.S b/src/gocontext_arm.S index 625e56762..ef09f9386 100644 --- a/src/gocontext_arm.S +++ b/src/gocontext_arm.S @@ -121,8 +121,6 @@ b.eq 2f cmp x8, #SYS_close b.eq 2f - //cmp x8, #SYS_mount - //b.eq 2f ldr x17, =\retptr ldr x17, [x17] @@ -223,7 +221,6 @@ c_syscall_handler: interpose_system_stack go_hook_exit go_exit c_handler interpose_system_stack go_hook_die go_die c_handler interpose_system_stack go_hook_sighandler go_sighandler c_handler - interpose_system_stack go_hook_forkExec go_forkExec c_handler syscall_filter go_hook_reg_syscall hook_syscall g_syscall_return syscall_filter go_hook_reg_rawsyscall hook_rawsyscall g_rawsyscall_return diff --git a/src/scopestdlib.c b/src/scopestdlib.c index e8787066a..e61f7ca72 100644 --- a/src/scopestdlib.c +++ b/src/scopestdlib.c @@ -77,7 +77,6 @@ extern int scopelibc_rename(const char *, const char *); extern int scopelibc_remove(const char *); extern int scopelibc_pipe2(int [2], int); extern void scopelibc_setbuf(FILE *, char *); -extern int scopelibc_mount(const char *, const char *, const char *, unsigned long, const void *); // String handling operations extern char * scopelibc_realpath(const char *, char *); @@ -519,13 +518,6 @@ scope_mkdir(const char *pathname, mode_t mode) { return scopelibc_mkdir(pathname, mode); } -int -scope_mount(const char *source, const char *target, - const char *fstype, unsigned long flags, - const void *data) { - return scopelibc_mount(source, target, fstype, flags, data); -} - int scope_chdir(const char *path) { return scopelibc_chdir(path); diff --git a/src/scopestdlib.h b/src/scopestdlib.h index 9193ec3ce..5c58aa684 100644 --- a/src/scopestdlib.h +++ b/src/scopestdlib.h @@ -162,7 +162,6 @@ int scope_fileno(FILE *); int scope_flock(int, int); int scope_fstat(int, struct stat *); int scope_mkdir(const char *, mode_t); -int scope_mount(const char *, const char *, const char *, unsigned long, const void *); int scope_chdir(const char *); int scope_rmdir(const char *); char* scope_get_current_dir_name(void); diff --git a/src/utils.c b/src/utils.c index 7ccac6904..89991ec23 100644 --- a/src/utils.c +++ b/src/utils.c @@ -552,44 +552,6 @@ libVersion(const char *version) { return version; } -/* - * The mkdir system call does not support the creation of intermediate dirs. - * This will walk the path and create all dirs one at a time. - */ -bool -makeIntermediateDirs(const char *path, mode_t mode) { - char *dup_path = scope_strdup(path); - if (!dup_path) { - return FALSE; - } - - char *curr = dup_path; - char *slash; - - while ((slash = scope_strrchr(curr, '/'))) { - *slash = '\0'; - if (scope_mkdir(dup_path, mode) == -1) { - if (scope_errno != EEXIST) { - scope_free(dup_path); - return FALSE; - } - } - - *slash = '/'; - curr = slash + 1; - } - - if (scope_mkdir(dup_path, mode) == -1) { - if (scope_errno != EEXIST) { - scope_free(dup_path); - return FALSE; - } - } - - scope_free(dup_path); - return TRUE; -} - #define EDGE_PATH_DOCKER "/var/run/appscope/appscope.sock" #define EDGE_PATH_DEFAULT "/opt/cribl/state/appscope.sock" #define READ_AND_WRITE (R_OK|W_OK) @@ -621,189 +583,3 @@ edgePath(void) { return NULL; } - -bool -createLdsoPreload(char *path) -{ - int lfd; - size_t mlen; - elf_buf_t *ebuf = NULL; - struct stat fstat; - char bpath[PATH_MAX], rpath[PATH_MAX]; - - scope_strncpy(bpath, path, strlen(path) + 1); - scope_strcat(bpath, "/bin/cat"); - mlen = scope_strlen(path); - - if (lstat(bpath, &fstat) == -1) { - scopeLogWarn("lstat of %s in %s:%d", bpath, __FUNCTION__, __LINE__); - return FALSE; - } - - if (S_ISLNK(fstat.st_mode)) { - // musl based fs is a sym link to busybox - if (scope_readlink(bpath, rpath, PATH_MAX - 1) == -1) { - scopeLogWarn("sym link of %s in %s:%d", bpath, __FUNCTION__, __LINE__); - return FALSE; - } - bpath[mlen + 1] = '\0'; - scope_strcat(bpath, rpath); - } - - if ((ebuf = getElf(bpath)) == NULL) { - scopeLogWarn("get ELF of %s in %s:%d", bpath, __FUNCTION__, __LINE__); - return FALSE; - } - - // write to /etc/ld.so.preload - bpath[mlen + 1] = '\0'; - scope_strcat(bpath, "/etc/ld.so.preload"); - - if ((lfd = scope_open(bpath, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { - scopeLogWarn("open of %s in %s:%d", bpath, __FUNCTION__, __LINE__); - free(ebuf); - return FALSE; - } - - scope_memset(rpath, 0, PATH_MAX); - scope_strncpy(rpath, g_libpath, scope_strlen(g_libpath)); - if (is_musl(ebuf->buf)) { - scope_strcat(rpath, "/libscope.so.musl"); - } else { - scope_strcat(rpath, "/libscope.so.glibc"); - } - - if (scope_write(lfd, rpath, scope_strlen(rpath) + 1) <= 0) { - scopeLogWarn("write to %s in %s:%d", rpath, __FUNCTION__, __LINE__); - scope_close(lfd); - scope_free(ebuf); - return FALSE; - } - - scope_close(lfd); - scope_free(ebuf); - return TRUE; -} - -char * -getMountPath(pid_t pid) -{ - bool candidate = FALSE; - size_t len; - char *buf = NULL, *mount = NULL; - char path[PATH_MAX]; - - scope_snprintf(path, sizeof(path), "/proc/%d/mounts", pid); - FILE *fstream = scope_fopen(path, "r"); - if (fstream == NULL) return NULL; - - while (scope_getline(&buf, &len, fstream) != -1) { - // if a docker overlay mount and not already mounted; appscope - if ((scope_strstr(buf, "overlay")) && - (scope_strstr(buf, "docker"))) { - char *start, *end; - if (((start = scope_strstr(buf, "workdir="))) && - ((end = scope_strstr(buf, "/work")))) { - start += scope_strlen("workdir="); - *end = '\0'; - scope_strcat(start, "/merged"); - mount = scope_strdup(start); - candidate = TRUE; - } - } - - // no longer a candidate as we've already mounted this proc - if (scope_strstr(buf, "appscope")) candidate = FALSE; - - scope_free(buf); - buf = NULL; - len = 0; - } - - if (buf) scope_free(buf); - scope_fclose(fstream); - - if (candidate == TRUE) return mount; - return NULL; -} - -bool -mountCDirs(char *target, char *fstype) -{ - char *filterdir; - size_t targetlen = scope_strlen(target); - - if ((filterdir = scope_malloc(targetlen + 128)) == NULL) return FALSE; - scope_strcpy(filterdir, target); - scope_strcat(filterdir, g_libpath); - - // make a dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) == FALSE) { - scopeLogWarn("Warn: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); - scope_free(filterdir); - return FALSE; - } - - // mount the merged dir addition into the host FS - if (scope_mount(g_libpath, filterdir, fstype, MS_BIND, NULL) != 0) { - scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); - scope_free(filterdir); - return FALSE; - } -#if 0 // TBD - // mount the Edge socket path if it exists - char *sockpath = edgePath(); - if (sockpath) { - char *sockdir = scope_dirname(sockpath); - filterdir[targetlen + 1] = '\0'; - scope_strcat(filterdir, sockdir); - - // make the socket dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) == TRUE) { - // mount the Edge socket - if (scope_mount(sockdir, filterdir, fstype, MS_BIND, NULL) != 0) { - scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); - } - } else { - scopeLogWarn("WARN: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); - } - - scope_free(sockpath); - } else { - scopeLogInfo("Note: can't resolve the Edge socket path %s:%d", __FUNCTION__, __LINE__); - } -#endif - return TRUE; -} - -bool -autoMount() -{ - DIR *dirp; - struct dirent *entry; - char *mpath = NULL; - - dirp = scope_opendir("/proc"); - if (dirp == NULL) { - scopeLogError("opendir"); - return FALSE; - } - - // Iterate all procs - while ((entry = scope_readdir(dirp)) != NULL) { - // procs/tasks are a dir - if (entry->d_type == DT_DIR) { - pid_t pid = scope_atoi(entry->d_name); - if (pid > 0) { - // if pid is in a supported container, get the requisite mount path - if ((mpath = getMountPath(pid)) != NULL) { - sysprint("%s:%d mount %s\n", __FUNCTION__, __LINE__, mpath); - mountCDirs(mpath, NULL); - createLdsoPreload(mpath); - } - } - } - } - - return TRUE; -} diff --git a/src/utils.h b/src/utils.h index afdf8c549..c755355f0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,11 +24,8 @@ void setUUID(char *); void setMachineID(char *); char *edgePath(void); -bool makeIntermediateDirs(const char *, mode_t); const char *libVersion(const char *); void sysprint(const char *, ...); -bool mountCDirs(char *, char *); -bool autoMount(void); // Signal Safe API int sigSafeNanosleep(const struct timespec *); diff --git a/src/wrap_go.c b/src/wrap_go.c index 80696a431..55b259807 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -31,7 +31,6 @@ #define PRI_STR_LEN sizeof(PRI_STR) #define UNDEF_OFFSET (-1) #define EXIT_STACK_SIZE (32 * 1024) -#define DOCKERD_AFTER_OVERLAY_MOUNTS 3 enum go_arch_t { X86_64, @@ -107,7 +106,6 @@ tap_t g_tap[] = { {tap_exit, "runtime.exit", /* .abi0 */ go_hook_exit, NULL, 0}, {tap_die, "runtime.dieFromSignal", /* .abi0 */ go_hook_die, NULL, 0}, {tap_sighandler, "runtime.sighandler", /* .abi0 */ go_hook_sighandler, NULL, 0}, - {tap_forkExec, "syscall.forkExec", go_hook_forkExec, NULL, 0}, {tap_end, "", NULL, NULL, 0}, }; @@ -469,29 +467,6 @@ free_go_str(char *str) { static void rewriteOpenContainersConfig(const char *cWorkDir) { - //return; -#ifdef __x86_64__ - /* - * Need to extend the system stack size when calling cJSON_PrintUnformatted(). - */ - int arc; - char *exit_stack, *tstack, *gstack; - if ((exit_stack = scope_malloc(SCOPE_STACK_SIZE)) == NULL) { - return; - } - - tstack = exit_stack + SCOPE_STACK_SIZE; - - // save the original stack, switch to the tstack - __asm__ volatile ( - "mov %%rsp, %2 \n" - "mov %1, %%rsp \n" - : "=r"(arc) // output - : "m"(tstack), "m"(gstack) // input - : // clobbered register - ); -#endif - char path[PATH_MAX] = {0}; if (!cWorkDir) { goto exit; @@ -787,17 +762,6 @@ rewriteOpenContainersConfig(const char *cWorkDir) scope_fclose(fp); exit: -#ifdef __x86_64__ - // Switch stack back to the original stack - __asm__ volatile ( - "mov %1, %%rsp \n" - : "=r"(arc) // output - : "r"(gstack) // inputs - : // clobbered register - ); - - scope_free(exit_stack); -#endif // to handle aarch64 case return; } @@ -1201,7 +1165,6 @@ patch_addrs(funchook_t *funchook, // We also patch syscalls at the first (and last) instruction. if (i == 0 && ((tap->assembly_fn == go_hook_exit) || (tap->assembly_fn == go_hook_die) || - (tap->assembly_fn == go_hook_forkExec) || (tap->assembly_fn == go_hook_sighandler))) { // In this case we want to patch the instruction directly @@ -1695,115 +1658,6 @@ getFDFromConn(uint64_t tcpConn) { return -1; } -/* - * Create bind mounts in the overlay files within a container. - * Provides access to the library, filter, config, CLI and - * the Edge Unix socket in a container. - * - * Update ld.so.preload such that all procs load the library. - * Initially intended for dockerd. - */ -static bool -mountDirs(char *src, char *target, char *fstype) -{ - static int once = 0; - - sysprint("Scope: mount(%d) of src: %s target: %s fstype: %s\n", once, src, target, fstype); - if (scope_strstr(fstype, "overlay") && scope_strstr(src, "merged")) once++; - - /* - * Checks: - * only applies to the dockerd proc - * ensure the constructor populated a path - * proceed only after container mounts are done - * - * We need to apply auto mounts after the overlay mounts are performed. - * Given that there are 3 mounts that encompass the overlays, we count - * the mounts and apply auto mounts at that point. The constant - * DOCKERD_AFTER_OVERLAY_MOUNTS defines the overlay activity. - */ - if ((src != NULL) && (target != NULL) && - scope_strstr(g_proc.procname, "containerd") && - (scope_strstr(g_libpath, "/")) && - (once == DOCKERD_AFTER_OVERLAY_MOUNTS)) { - int ldfd; - char *filterdir; - size_t targetlen = scope_strlen(target); - - sysprint("Interposed Mount overlay: %s %d\n", g_proc.procname, once); - once = 0; - - if ((filterdir = scope_malloc(targetlen + 128)) == NULL) return FALSE; - scope_strcpy(filterdir, target); - scope_strcat(filterdir, g_libpath); - - // make a dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) == FALSE) { - scopeLogWarn("Warn: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); - scope_free(filterdir); - return FALSE; - } - - // mount the merged dir addition into the host FS - if (scope_mount(g_libpath, filterdir, "overlay", MS_BIND, NULL) != 0) { - scopeLogWarn("WARN: mount %s on %s from %s:%d", g_libpath, filterdir, __FUNCTION__, __LINE__); - scope_free(filterdir); - return FALSE; - } - - // mount the Edge socket path if it exists - char *sockpath = edgePath(); - if (sockpath) { - char *sockdir = scope_dirname(sockpath); - filterdir[targetlen + 1] = '\0'; - scope_strcat(filterdir, sockdir); - - // make the socket dir in the merged dir - if (makeIntermediateDirs((const char *)filterdir, 0666) == TRUE) { - // mount the Edge socket - if (scope_mount(sockdir, filterdir, "overlay", MS_BIND, NULL) != 0) { - scopeLogWarn("WARN: mount %s on %s from %s:%d", sockpath, filterdir, __FUNCTION__, __LINE__); - } - } else { - scopeLogWarn("WARN: mkdir of %s from %s:%d", filterdir, __FUNCTION__, __LINE__); - } - - scope_free(sockpath); - } else { - scopeLogInfo("Note: can't resolve the Edge socket path %s:%d", __FUNCTION__, __LINE__); - } - - // write to /etc/ld.so.preload, not a mount - filterdir[targetlen + 1] = '\0'; - scope_strcat(filterdir, "etc/ld.so.preload"); - - if ((ldfd = scope_open(filterdir, O_RDWR | O_CREAT | O_APPEND, 0666)) == -1) { - scopeLogWarn("WARN: open %s:%d", __FUNCTION__, __LINE__); - scope_free(filterdir); - return FALSE; - } - - size_t liblen = scope_strlen(g_libpath); - char libpath[liblen + sizeof("/libscope.so")]; - - scope_memset(libpath, 0, liblen + sizeof("/libscope.so")); - scope_strncpy(libpath, g_libpath, liblen); - scope_strcat(libpath, "/libscope.so"); - - if (scope_write(ldfd, libpath, sizeof(libpath)) <= 0) { - scopeLogWarn("WARN: write %s:%d", __FUNCTION__, __LINE__); - scope_close(ldfd); - scope_free(filterdir); - return FALSE; - } - - scope_close(ldfd); - scope_free(filterdir); - } - - return TRUE; -} - // Extract data from syscalls. Values are available in registers saved on sys_stack. static void c_syscall(char *sys_stack, char *g_stack) @@ -1834,10 +1688,6 @@ c_syscall(char *sys_stack, char *g_stack) } funcprint("Scope: open of %ld\n", rc); - //if (scope_strstr(g_proc.procname, "runc") != NULL) { - // containerStart(); - //} - doOpen(rc, path, FD, "open"); } break; @@ -1903,16 +1753,6 @@ c_syscall(char *sys_stack, char *g_stack) doCloseAndReportFailures(fd, (rc != -1), "go_close"); // If net, deletes a net object } break; - case SYS_mount: - { - char *src = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p1); - char *target = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p2); - char *fstype = (char *)*(uint64_t *)(sys_stack + g_go_schema->arg_offsets.c_syscall_p3); - - sysprint("Scope: mount of %s to %s\n", src, target); - mountDirs(src, target, fstype); - break; - } default: break; } @@ -2351,78 +2191,3 @@ go_sighandler(char *stackptr) { return do_cfunc(stackptr, c_sighandler, tap_entry(tap_sighandler)->assembly_fn); } - -static void -c_forkExec(char *sys_stack, char *g_stack) -{ - //return; - - int i; - //char *argv0 = (char *)(uint64_t)(sys_stack + 0x0); - //uint64_t *argvv = (uint64_t *)*(uint64_t *)(sys_stack + 0x10); - char *argv0 = (char *)*(uint64_t *)(g_stack + 0x08); - uint64_t *argvv = (uint64_t *)*(uint64_t *)(g_stack + 0x18); - - /* - * TBD: in order to call scope_strstr() with a needle longer - * than 3-4 chars the stack needs to be aligned on a 16 byte - * boundary due to the "movaps %xmm0,0x20(%rsp)' instruction. - * Aligning in the stack switch code is likley to affect - * current offset values. - * Probably should be applied here? - * We enter this code from the start of the Go runtime function - * before it's stack frame is created. That is unique. Likely - * the cause of this issue. - * Will look into this directly. - */ - return; - char *cmd = go_str(argv0, TRUE); - // TBD: add a check for curent proc containerd when stack is aligned - if (!scope_strstr(cmd, "runc")) { - scope_free(cmd); - return; - } - bool updateCfg = FALSE; - char *taskUniqueId = NULL; - char *nsName = NULL; - sysprint("%s execing %s\n", g_proc.procname, cmd); - scope_free(cmd); - for (i = 0; argvv[i]; i += 2) { - char *argv = go_str((char *)(argvv + i), TRUE); - if (argv) { - sysprint("\t%s:%d %s argv %s\n", __FUNCTION__, __LINE__, g_proc.procname, argv); - if (scope_strstr(argv, "-id") && (char *)(argvv + i + 2)) { - /* - * id of the task (container) - */ - taskUniqueId = go_str((char *)(argvv + i + 2), TRUE); - } else if (scope_strstr(argv, "-na") && (char *)(argvv + i + 2)) { // should be "namespace" - /* - * namespace - */ - nsName = go_str((char *)(argvv + i + 2), TRUE); - } else if (scope_strstr(argv, "sta")) { // should be "start" - /* - * start indicator - */ - updateCfg = TRUE; - break; - } - scope_free(argv); - } - } - - // Update task configuration - if (updateCfg) { - //rewriteOpenContainersConfig(taskUniqueId, nsName); - } - scope_free(nsName); - scope_free(taskUniqueId); -} - -EXPORTON void * -go_forkExec(char *stackptr) -{ - return do_cfunc(stackptr, c_forkExec, tap_entry(tap_forkExec)->assembly_fn); -} - From 9f975a43ca586746019ee07ac4a3e249162442ab Mon Sep 17 00:00:00 2001 From: iapaddler Date: Wed, 7 Jun 2023 19:24:14 +0000 Subject: [PATCH 21/25] Resolved conflicts in start.go --- cli/start/start.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/start/start.go b/cli/start/start.go index 49dcf1945..d18a82e3d 100644 --- a/cli/start/start.go +++ b/cli/start/start.go @@ -27,7 +27,6 @@ func Start(rootdir string) error { } } else { stdoutStderr, err := ld.InstallNamespace(rootdir) - if err != nil { log.Warn(). Err(err). From c4f730bcb824b2993b68da16560a014b38f3f499 Mon Sep 17 00:00:00 2001 From: iapaddler Date: Wed, 7 Jun 2023 21:02:21 +0000 Subject: [PATCH 22/25] Removed mount from redfine list and a tap entry for forkExec. --- contrib/redefine_syms.lst | 1 - src/gocontext.h | 1 - 2 files changed, 2 deletions(-) diff --git a/contrib/redefine_syms.lst b/contrib/redefine_syms.lst index d33a64692..78bb8d80a 100644 --- a/contrib/redefine_syms.lst +++ b/contrib/redefine_syms.lst @@ -117,4 +117,3 @@ usleep scopelibc_usleep vfprintf scopelibc_vfprintf vsnprintf scopelibc_vsnprintf write scopelibc_write -mount scopelibc_mount diff --git a/src/gocontext.h b/src/gocontext.h index 8de73d685..225040a4a 100644 --- a/src/gocontext.h +++ b/src/gocontext.h @@ -76,7 +76,6 @@ enum tap_id { tap_exit, tap_die, tap_sighandler, - tap_forkExec, tap_end, }; From f4f56289ef50851b86216f3d53f12e2c7eda7a12 Mon Sep 17 00:00:00 2001 From: Donn Date: Thu, 8 Jun 2023 16:20:04 +0000 Subject: [PATCH 23/25] Fixed a copy/pasta error getting symbols from pclntab in Go 16/17. --- src/wrap_go.c | 55 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index 55b259807..f387894d6 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -415,15 +415,23 @@ createGoStructFile(void) { } } -// Use go_str() whenever a "go string" type needs to be interpreted. -// The resulting go_str will need to be passed to free_go_str() when it is no -// longer needed. -// Don't use go_str() for byte arrays. +/* + * Use go_str() whenever a "go string" type needs to be interpreted. + * The resulting go_str will need to be passed to free_go_str() when it is no + * longer needed. + * Don't use go_str() for byte arrays. + * + * Go 17 and higher use "c style" null terminated strings instead of a string + * and a length. Therfore, we do nothing here for Go >= 17. + * However, there is a case where argv values are passed as go strings. + * In that case we we need to force a conversion even when we are >= Go 17. + * We are no longer interposing a function that references argv, however, the + * force param is left in place expecting that since it has been needed, it + * will be needed. + */ static char * go_str(void *go_str, bool force) { - // Go 17 and higher use "c style" null terminated strings instead of a string and a length - if ((g_go_minor_ver >= 17) && (force == FALSE)) { // We need to deference the address first before casting to a char * if (!go_str) return NULL; @@ -453,12 +461,14 @@ free_go_str(char *str) { */ /* - * Rewrites the container configuration for specific task ID and container namespace name. + * Rewrite the container configuration for the given container. + * A path to the container specific work dir defines the location of the configuration file. + * * Please look into opencontainers Linux runtime-spec for details about the exact JSON struct. - * Following changes will be performed: - * - Add mount point - * `scope` will be mounted from host ("/usr/lib/appscope//scope") into the container ("/opt/scope") - * - Extend Environment variable + * The following changes will be performed: + * - Add a mount point + * `scope` will be mounted from the host ("ex: /usr/lib/appscope//scope") into the container ("ex: /opt/scope") + * - Extend Environment variables * `LD_PRELOAD` will contain the following entry `/opt/libscope.so` * `SCOPE_SETUP_DONE=true` mark that configuration was processed * - Add prestart hook @@ -762,7 +772,6 @@ rewriteOpenContainersConfig(const char *cWorkDir) scope_fclose(fp); exit: - // to handle aarch64 case return; } @@ -882,6 +891,8 @@ getGoVersionAddr(const char* buf) static Elf64_Addr getSym12(const void *pclntab_addr, char *sname) { + if ((!pclntab_addr) || (!sname)) return 0; + Elf64_Addr symaddr = 0; uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); const void *symtab_addr = pclntab_addr + 16; @@ -907,15 +918,19 @@ getSym12(const void *pclntab_addr, char *sname) static Elf64_Addr getSym16(const void *pclntab_addr, char *sname, char *altname, char *mnemonic) { + if ((!pclntab_addr) || (!sname)) return 0; + Elf64_Addr symaddr = 0; - uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); - const void *symtab_addr = pclntab_addr + 16; + uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); + uint64_t funcnametab_offset = *((const uint64_t *)(pclntab_addr + (3 * 8))); + uint64_t pclntab_offset = *((const uint64_t *)(pclntab_addr + (7 * 8))); + const void *symtab_addr = pclntab_addr + pclntab_offset; for (int i = 0; i < sym_count; i++) { - uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); - uint32_t name_offset = *((const uint32_t *)(pclntab_addr + func_offset + 8)); - uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); - const char *func_name = (const char *)(pclntab_addr + name_offset); + uint64_t func_offset = *((const uint64_t *)(symtab_addr + 8)); + uint32_t name_offset = *((const uint32_t *)(pclntab_addr + pclntab_offset + func_offset + 8)); + uint64_t sym_addr = *((const uint64_t *)(symtab_addr)); + const char *func_name = (const char *)(pclntab_addr + funcnametab_offset + name_offset); if (scope_strcmp(sname, func_name) == 0) { symaddr = sym_addr; @@ -940,6 +955,8 @@ getSym16(const void *pclntab_addr, char *sname, char *altname, char *mnemonic) static Elf64_Addr getSym1820(const void *pclntab_addr, char *sname, char *altname, char *mnemonic) { + if ((!pclntab_addr) || (!sname)) return 0; + Elf64_Addr symaddr = 0; uint64_t sym_count = *((const uint64_t *)(pclntab_addr + 8)); // In go 1.18 the funcname table and the pcln table are stored in the text section @@ -986,7 +1003,7 @@ embedPclntab(const char *buf, char *sname, char *altname, char *mnemonic) for (int i = 0; i < ehdr->e_shnum; i++) { const char *sec_name = section_strtab + sections[i].sh_name; - if (strstr(sec_name, "data.rel.ro") != 0) { + if (scope_strstr(sec_name, "data.rel.ro") != 0) { const void *pclntab_addr = buf + sections[i].sh_offset; unsigned char *data = (unsigned char *)pclntab_addr; From ad7ce02f2864d3efee1b0367bff3df3418d52af4 Mon Sep 17 00:00:00 2001 From: Donn Date: Wed, 14 Jun 2023 08:00:40 -0500 Subject: [PATCH 24/25] Missed a free. --- src/wrap_go.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wrap_go.c b/src/wrap_go.c index f387894d6..4033dc5d5 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -784,7 +784,10 @@ containerStart(void) if ((buf = scope_calloc(1, NCARGS)) == NULL) return; - if ((argc = osGetArgv(g_proc.pid, buf, NCARGS)) == 0) return; + if ((argc = osGetArgv(g_proc.pid, buf, NCARGS)) == 0) { + scope_free(buf); + return; + } sysprint("Scope: found runc"); From 6c77ea9c34a998dae98d53e38762663a283dfe4e Mon Sep 17 00:00:00 2001 From: Donn Date: Wed, 14 Jun 2023 11:17:31 -0500 Subject: [PATCH 25/25] Remove console debug enable and a comment clarfication. --- src/utils.c | 2 +- src/wrap_go.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils.c b/src/utils.c index 8c7b79b58..3cb74efde 100644 --- a/src/utils.c +++ b/src/utils.c @@ -14,7 +14,7 @@ #include "plattime.h" #include "scopeelf.h" -#define SYSPRINT_CONSOLE 1 +#define SYSPRINT_CONSOLE 0 #define PRINT_BUF_SIZE 1024 #define MAC_ADDR_LEN 17 #define ZERO_MACHINE_ID "00000000000000000000000000000000" diff --git a/src/wrap_go.c b/src/wrap_go.c index fbfa18326..0f3e190cd 100644 --- a/src/wrap_go.c +++ b/src/wrap_go.c @@ -1024,7 +1024,7 @@ embedPclntab(const char *buf, char *sname, char *altname, char *mnemonic) // __FUNCTION__, __LINE__, &data[j]); switch (data[j]) { - // Go 18 and 20 + // Go 18 - 20 case 0xf1: case 0xf0: return getSym1820(&data[j], sname, altname, mnemonic);