/* Copyright (C) 2021-2023 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Lineage events for process fork, exec, etc. */ #include "config.h" #include #include #include #include #include #include #include #include "descendants.h" /* TprintfT(,...) definitions. Adjust per module as needed */ #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings #define DBG_LTT 0 // for interposition on GLIBC functions #define DBG_LT1 1 // for configuration details, warnings #define DBG_LT2 2 #define DBG_LT3 3 #define LT_MAXNAMELEN 1024 #define LT_MAXPATHLEN 1024 int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; int dbg_current_mode = FOLLOW_NONE; /* for debug only */ unsigned line_key = COLLECTOR_TSD_INVALID_KEY; line_mode_t line_mode = LM_DORMANT; int user_follow_mode = FOLLOW_ON; int java_mode = 0; static char *user_follow_spec; static char new_lineage[LT_MAXNAMELEN]; static char curr_lineage[LT_MAXNAMELEN]; static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory /* lineage tracking for descendants of this process */ static int fork_linenum = 0; static int line_initted = 0; static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; static collector_mutex_t clone_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; /* interposition */ #define CALL_REAL(x) (*(int(*)())__real_##x) #define CALL_REALC(x) (*(char*(*)())__real_##x) #define CALL_REALF(x) (*(FILE*(*)())__real_##x) #define NULL_PTR(x) ( __real_##x == NULL ) // For a given Linux, which lib functions have more than one GLIBC version? Do this: // objdump -T `find /lib /lib64 -name "*.so*"` | grep GLIBC | grep text | grep \( static void *__real_fork = NULL; static void *__real_vfork = NULL; static void *__real_execve = NULL; static void *__real_execvp = NULL; static void *__real_execv = NULL; static void *__real_execle = NULL; static void *__real_execlp = NULL; static void *__real_execl = NULL; static void *__real_clone = NULL; static void *__real_grantpt = NULL; static void *__real_ptsname = NULL; static void *__real_popen = NULL; static int clone_linenum = 0; #if ARCH(Intel) #if WSIZE(32) static void *__real_popen_2_1 = NULL; static void *__real_popen_2_0 = NULL; static void *__real_posix_spawn_2_15 = NULL; static void *__real_posix_spawnp_2_15 = NULL; static void *__real_posix_spawn_2_2 = NULL; static void *__real_posix_spawnp_2_2 = NULL; #elif WSIZE(64) static void *__real_posix_spawn_2_15 = NULL; static void *__real_posix_spawnp_2_15 = NULL; static void *__real_posix_spawn_2_2_5 = NULL; static void *__real_posix_spawnp_2_2_5 = NULL; #endif /* WSIZE() */ #endif /* ARCH(Intel) */ static void *__real_system = NULL; static void *__real_posix_spawn = NULL; static void *__real_posix_spawnp = NULL; static void *__real_setuid = NULL; static void *__real_seteuid = NULL; static void *__real_setreuid = NULL; static void *__real_setgid = NULL; static void *__real_setegid = NULL; static void *__real_setregid = NULL; static void linetrace_dormant (); static int check_follow_fork (); static int check_follow_exec (const char *execfile); static int check_follow_combo (const char *execfile); static int path_collectable (const char *execfile); static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str); static int init_lineage_intf (); /* ------- "Previously dbx-visible" function prototypes ----------------- */ static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile); static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname); static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork); static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork); static char **linetrace_ext_exec_prologue (const char *variant, const char* path, char *const argv[], char *const envp[], int *following_exec); static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec); static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo); static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo); #ifdef DEBUG static int get_combo_flag () { int * guard = NULL; int combo_flag = ((line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0); return combo_flag; } #endif /* DEBUG */ /* must be called for potentially live experiment */ int __collector_ext_line_init (int *precord_this_experiment, const char * progspec, const char * progname) { *precord_this_experiment = 1; TprintfT (DBG_LT0, "__collector_ext_line_init(%s)\n", progspec); if (NULL_PTR (fork)) if (init_lineage_intf ()) { TprintfT (DBG_LT0, "__collector_ext_line_init() ERROR: initialization failed.\n"); return COL_ERROR_LINEINIT; } /* check the follow spec */ user_follow_spec = CALL_UTIL (getenv)(SP_COLLECTOR_FOLLOW_SPEC); if (user_follow_spec != NULL) { TprintfT (DBG_LT0, "collector: %s=%s\n", SP_COLLECTOR_FOLLOW_SPEC, user_follow_spec); if (!linetrace_follow_experiment (user_follow_spec, curr_lineage, progname)) { *precord_this_experiment = 0; TprintfT (DBG_LT0, "collector: -F = does not match, will NOT be followed\n"); } else TprintfT (DBG_LT0, "collector: -F = matches, will be followed\n"); user_follow_mode = FOLLOW_ALL; } __collector_env_save_preloads (); TprintfT (DBG_LT0, "__collector_ext_line_init(), progname=%s, followspec=%s, followthis=%d\n", progname, user_follow_spec ? user_follow_spec : "NULL", *precord_this_experiment); line_mode = LM_TRACK_LINEAGE; /* even if we don't follow, we report the interposition */ line_initted = 1; return COL_ERROR_NONE; } /* * int __collector_ext_line_install(args) * Check args to determine which line events to follow. * Create tsd key for combo flag. */ int __collector_ext_line_install (char *args, const char * expname) { if (!line_initted) { TprintfT (DBG_LT0, "__collector_ext_line_install(%s) ERROR: init hasn't be called yet\n", args); return COL_ERROR_EXPOPEN; } TprintfT (DBG_LT0, "__collector_ext_line_install(%s, %s)\n", args, expname); line_key = __collector_tsd_create_key (sizeof (int), NULL, NULL); /* determine experiment name */ __collector_strlcpy (linetrace_exp_dir_name, expname, sizeof (linetrace_exp_dir_name)); lineage_from_expname (curr_lineage, sizeof (curr_lineage), linetrace_exp_dir_name); user_follow_mode = CALL_UTIL (atoi)(args); TprintfT (DBG_LT0, "__collector_ext_line_install() user_follow_mode=0x%X, linetrace_exp_dir_name=%s\n", user_follow_mode, linetrace_exp_dir_name); // determine java mode char * java_follow_env = CALL_UTIL (getenv)(JAVA_TOOL_OPTIONS); if (java_follow_env != NULL && CALL_UTIL (strstr)(java_follow_env, COLLECTOR_JVMTI_OPTION)) java_mode = 1; // backup collector specific env if (sp_env_backup == NULL) { sp_env_backup = __collector_env_backup (); TprintfT (DBG_LT0, "__collector_ext_line_install creating sp_env_backup -- 0x%p\n", sp_env_backup); } else TprintfT (DBG_LT0, "__collector_ext_line_install existing sp_env_backup -- 0x%p\n", sp_env_backup); if (user_follow_mode == FOLLOW_NONE) __collector_env_unset (NULL); char logmsg[256]; logmsg[0] = '\0'; if (user_follow_mode != FOLLOW_NONE) CALL_UTIL (strlcat)(logmsg, "fork|exec|combo", sizeof (logmsg)); size_t slen = __collector_strlen (logmsg); if (slen > 0) logmsg[slen] = '\0'; else CALL_UTIL (strlcat)(logmsg, "none", sizeof (logmsg)); /* report which line events are followed */ (void) __collector_log_write ("\n", SP_JCMD_LINETRACE, logmsg); TprintfT (DBG_LT0, "__collector_ext_line_install(%s): %s \n", expname, logmsg); return COL_ERROR_NONE; } char * lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname) { TprintfT (DBG_LT0, "lineage_from_expname(%s, %s)\n", lineage_str, expname); char *p = NULL; if (lstr_sz < 1 || !lineage_str || !expname) { TprintfT (DBG_LT0, "lineage_from_expname(): ERROR, null string\n"); return NULL; } /* determine lineage from experiment name */ p = __collector_strrchr (expname, '/'); if ((p == NULL) || (*++p != '_')) { lineage_str[0] = 0; TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\".\" (founder)\n", expname); } else { size_t tmp = __collector_strlcpy (lineage_str, p, lstr_sz); if (tmp >= lstr_sz) TprintfT (DBG_LT0, "lineage_from_expname(): ERROR: expt=%s lineage=\"%s\" truncated %ld characters\n", expname, lineage_str, (long) (tmp - lstr_sz)); lineage_str[lstr_sz - 1] = 0; p = __collector_strchr (lineage_str, '.'); if (p != NULL) *p = '\0'; TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\"%s\"\n", expname, lineage_str); } return lineage_str; } /* * void __collector_line_cleanup (void) * Disable logging. Clear backup ENV. */ void __collector_line_cleanup (void) { if (line_mode == LM_CLOSED) { TprintfT (DBG_LT0, "__collector_line_cleanup(): WARNING, line is already closed\n"); return; } else if (line_mode == LM_DORMANT) TprintfT (DBG_LT0, "__collector_line_cleanup(): ERROR, line is DORMANT\n"); else TprintfT (DBG_LT0, "__collector_line_cleanup()\n"); line_mode = LM_CLOSED; user_follow_mode = FOLLOW_NONE; dbg_current_mode = FOLLOW_NONE; /* for debug only */ line_key = COLLECTOR_TSD_INVALID_KEY; java_mode = 0; if (sp_env_backup != NULL) { __collector_env_backup_free (); sp_env_backup = NULL; } return; } /* * void __collector_ext_line_close (void) * Disable logging. Cleans ENV vars. Clear backup ENV. */ void __collector_ext_line_close (void) { TprintfT (DBG_LT0, "__collector_ext_line_close()\n"); __collector_line_cleanup (); __collector_env_unset (NULL); return; } /* * void linetrace_dormant(void) * Disable logging. Preserve ENV vars. */ static void linetrace_dormant (void) { if (line_mode == LM_DORMANT) { TprintfT (DBG_LT0, "linetrace_dormant() -- already dormant\n"); return; } else if (line_mode == LM_CLOSED) { TprintfT (DBG_LT0, "linetrace_dormant(): ERROR, line is already CLOSED\n"); return; } else TprintfT (DBG_LT0, "linetrace_dormant()\n"); line_mode = LM_DORMANT; return; } static int check_follow_fork () { int follow = (user_follow_mode != FOLLOW_NONE); TprintfT (DBG_LT0, "check_follow_fork()=%d\n", follow); return follow; } static int check_follow_exec (const char *execfile) { int follow = (user_follow_mode != FOLLOW_NONE); if (follow) { /* revise based on collectability of execfile */ follow = path_collectable (execfile); } TprintfT (DBG_LT0, "check_follow_exec(%s)=%d\n", execfile, follow); return follow; } static int check_follow_combo (const char *execfile) { int follow = (user_follow_mode != FOLLOW_NONE); TprintfT (DBG_LT0, "check_follow_combo(%s)=%d\n", execfile, follow); return follow; } static int check_fd_dynamic (int fd) { TprintfT (DBG_LT0, "check_fd_dynamic(%d)\n", fd); off_t off = CALL_UTIL (lseek)(fd, (off_t) 0, SEEK_END); size_t sz = (size_t) 8192; /* one page should suffice */ if (sz > off) sz = off; char *p = CALL_UTIL (mmap64_)((char *) 0, sz, PROT_READ, MAP_PRIVATE, fd, (off64_t) 0); if (p == MAP_FAILED) { TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: mmap failed for `%d'\n", fd); (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_NOFOLLOW, "mmap-failed"); return 0; } char elfclass = p[EI_CLASS]; if ((p[EI_MAG0] != ELFMAG0) || (p[EI_MAG1] != ELFMAG1) || (p[EI_MAG2] != ELFMAG2) || (p[EI_MAG3] != ELFMAG3) || (elfclass != ELFCLASS32 && elfclass != ELFCLASS64) ) { TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' is not executable ELF!\n", fd); CALL_UTIL (munmap)(p, sz); return 1; } Elf32_Ehdr *ehdr32 = (Elf32_Ehdr*) p; Elf64_Ehdr *ehdr64 = (Elf64_Ehdr*) p; Elf64_Off e_phoff; Elf64_Half e_phnum; Elf64_Half e_phentsize; if (elfclass == ELFCLASS32) { e_phoff = ehdr32->e_phoff; e_phnum = ehdr32->e_phnum; e_phentsize = ehdr32->e_phentsize; } else { e_phoff = ehdr64->e_phoff; e_phnum = ehdr64->e_phnum; e_phentsize = ehdr64->e_phentsize; } if ((sizeof (Elf32_Ehdr) > sz) || (sizeof (Elf64_Ehdr) > sz) || (e_phoff + e_phentsize * (e_phnum - 1) > sz)) { TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' ELF file did not fit in page!\n", fd); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "ELF header size"); #endif CALL_UTIL (munmap)(p, sz); return 1; } TprintfT (DBG_LT2, "check_fd_dynamic(): elfclass=%d, e_phoff=%lu e_phnum=%lu e_phentsize=%lu\n", (int) elfclass, (unsigned long) e_phoff, (unsigned long) e_phnum, (unsigned long) e_phentsize); int dynamic = 0; Elf64_Half i; for (i = 0; i < e_phnum; i++) { if (elfclass == ELFCLASS32) { if (PT_DYNAMIC == ((Elf32_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) { dynamic = 1; break; } } else { if (PT_DYNAMIC == ((Elf64_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) { dynamic = 1; break; } } } if (!dynamic) { TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: Command `%d' is not a dynamic executable!\n", fd); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_NOFOLLOW, "!dynamic"); #endif } else TprintfT (DBG_LT2, "check_fd_dynamic(): Command `%d' is a dynamic executable!\n", fd); CALL_UTIL (munmap)(p, sz); return dynamic; } static int check_dynamic (const char *execfile) { TprintfT (DBG_LT2, "check_dynamic(%s)\n", execfile); int fd = CALL_UTIL (open)(execfile, O_RDONLY); if (fd == -1) { TprintfT (DBG_LT0, "check_dynamic(): ERROR/WARNING: Command `%s' could not be opened!\n", execfile); (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "open"); return 1; /* follow, though exec will presumably fail */ } int ret = check_fd_dynamic (fd); CALL_UTIL (close)(fd); return ret; } static int path_collectable (const char *execfile) { TprintfT (DBG_LT0, "path_collectable(%s)\n", execfile); /* Check that execfile exists and is a collectable executable */ /* logging warning when collection is likely to be unsuccessful */ /* (if check isn't accurate, generally best not to include it) */ if (execfile && !__collector_strchr (execfile, '/')) { /* got an unqualified name */ /* XXXX locate execfile on PATH to be able to check it */ TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't check unqualified executable `%s'\n", execfile); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "path"); #endif return 1; /* follow unqualified execfile unchecked */ } struct stat sbuf; if (stat (execfile, &sbuf)) { /* can't stat it */ TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't stat `%s'\n", execfile); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "stat"); #endif return 1; /* follow, though exec will probably fail */ } TprintfT (DBG_LT2, "path_collectable(%s) mode=0%o uid=%d gid=%d\n", execfile, sbuf.st_mode, sbuf.st_uid, sbuf.st_gid); if (((sbuf.st_mode & S_IXUSR) == 0) || ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is NOT an executable file!\n", execfile); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "mode"); #endif return 1; /* follow, though exec will presumably fail */ } /* XXXX setxid(root) is OK iff libcollector is registered as secure */ /* XXXX setxid(non-root) is OK iff umask is accomodating */ if (((sbuf.st_mode & S_ISUID) != 0) || ((sbuf.st_mode & S_ISGID) != 0)) { TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is SetXID!\n", execfile); #if 0 (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_RISKYFOLLOW, "setxid"); #endif return 1; /* follow, though collection may be unreliable */ } if (!check_dynamic (execfile)) { TprintfT (DBG_LT0, "path_collectable(%s) WARNING/ERROR: not dynamic, not collectng!\n", execfile); return 0; /* don't follow, collection will fail unpredictably */ } TprintfT (DBG_LT2, "path_collectable(%s) OK!\n", execfile); return 1; /* OK to follow */ } static char * build_experiment_path (char * instring, size_t instring_sz, const char *lineage_str) { TprintfT (DBG_LT0, "build_experiment_path(,%ld, %s)\n", (long) instring_sz, lineage_str); const char *p = CALL_UTIL (strstr)(linetrace_exp_dir_name, DESCENDANT_EXPT_KEY); int basedir_sz; if (p) basedir_sz = p - linetrace_exp_dir_name + 4; /* +3 because of DESCENDANT_EXPT_KEY */ else basedir_sz = __collector_strlen (linetrace_exp_dir_name) + 1; int additional_sz = __collector_strlen (lineage_str) + 4; if (basedir_sz + additional_sz > instring_sz) { TprintfT (DBG_LT0, "build_experiment_path(%s,%s): ERROR: path too long: %d > %ld\n", linetrace_exp_dir_name, lineage_str, basedir_sz + additional_sz, (long) instring_sz); *instring = 0; return NULL; } __collector_strlcpy (instring, linetrace_exp_dir_name, basedir_sz); size_t slen = __collector_strlen (instring); CALL_UTIL (snprintf)(instring + slen, instring_sz - slen, "/%s.er", lineage_str); assert (__collector_strlen (instring) + 1 == basedir_sz + additional_sz); return instring; } static void check_reuid_change (uid_t ruid, uid_t euid) { uid_t curr_ruid = getuid (); uid_t curr_euid = geteuid (); mode_t curr_umask = umask (0); umask (curr_umask); /* restore original umask */ int W_oth = !(curr_umask & S_IWOTH); TprintfT (DBG_LT0, "check_reuid_change(%d,%d): umask=%03o\n", ruid, euid, curr_umask); TprintfT (DBG_LT0, "check_reuid_change(): umask W usr=%d grp=%d oth=%d\n", (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); if (ruid != -1) { TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_ruid, ruid); if ((curr_euid == 0) && (ruid != 0) && !W_oth) { /* changing to non-root ID, with umask blocking writes by other */ TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after ruid change (%d->%d)\n", curr_ruid, ruid); (void) __collector_log_write ("umask %03o ruid %d->%d\n", SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_ruid, ruid); } } if (euid != -1) { TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_euid, euid); if ((curr_euid == 0) && (euid != 0) && !W_oth) { /* changing to non-root ID, with umask blocking writes by other */ TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after euid change (%d->%d)\n", curr_euid, euid); (void) __collector_log_write ("umask %03o euid %d->%d\n", SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_euid, euid); } } } static void check_regid_change (gid_t rgid, gid_t egid) { gid_t curr_rgid = getgid (); gid_t curr_egid = getegid (); uid_t curr_euid = geteuid (); mode_t curr_umask = umask (0); umask (curr_umask); /* restore original umask */ int W_oth = !(curr_umask & S_IWOTH); TprintfT (DBG_LT0, "check_regid_change(%d,%d): umask=%03o euid=%d\n", rgid, egid, curr_umask, curr_euid); TprintfT (DBG_LT0, "umask W usr=%d grp=%d oth=%d\n", (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); if (rgid != -1) { TprintfT (DBG_LT0, "check_regid_change(%d->%d)\n", curr_rgid, rgid); if ((curr_euid == 0) && (rgid != 0) && !W_oth) { /* changing to non-root ID, with umask blocking writes by other */ TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after rgid change (%d->%d)\n", curr_rgid, rgid); (void) __collector_log_write ("umask %03o rgid %d->%d\n", SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_rgid, rgid); } } if (egid != -1) { TprintfT (DBG_LT0, "check_regid_change(): check_egid_change(%d->%d)\n", curr_egid, egid); if ((curr_euid == 0) && (egid != 0) && !W_oth) { /* changing to non-root ID, with umask blocking writes by other */ TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after egid change (%d->%d)\n", curr_egid, egid); (void) __collector_log_write ("umask %03o egid %d->%d\n", SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_egid, egid); } } } static int init_lineage_intf () { void *dlflag; TprintfT (DBG_LT2, "init_lineage_intf()\n"); static int nesting_check = 0; if (nesting_check >= 2) { /* segv before stack blows up */ nesting_check /= (nesting_check - 2); } nesting_check++; __real_fork = dlsym (RTLD_NEXT, "fork"); if (__real_fork == NULL) { __real_fork = dlsym (RTLD_DEFAULT, "fork"); if (__real_fork == NULL) return 1; dlflag = RTLD_DEFAULT; } else dlflag = RTLD_NEXT; TprintfT (DBG_LT2, "init_lineage_intf() using RTLD_%s\n", dlflag == RTLD_DEFAULT ? "DEFAULT" : "NEXT"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_fork\n", __real_fork); __real_vfork = dlsym (dlflag, "vfork"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_vfork\n", __real_vfork); __real_execve = dlsym (dlflag, "execve"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execve\n", __real_execve); __real_execvp = dlsym (dlflag, "execvp"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execvp\n", __real_execvp); __real_execv = dlsym (dlflag, "execv"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execv\n", __real_execv); __real_execle = dlsym (dlflag, "execle"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execle\n", __real_execle); __real_execlp = dlsym (dlflag, "execlp"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execlp\n", __real_execlp); __real_execl = dlsym (dlflag, "execl"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execl\n", __real_execl); __real_clone = dlsym (dlflag, "clone"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone); __real_posix_spawn = dlsym (dlflag, "posix_spawn"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawn\n", __real_posix_spawn); __real_posix_spawnp = dlsym (dlflag, "posix_spawnp"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawnp\n", __real_posix_spawnp); __real_popen = dlvsym (dlflag, "popen", SYS_POPEN_VERSION); TprintfT (DBG_LT2, "init_lineage_intf()[%s] @0x%p __real_popen\n", SYS_POPEN_VERSION, __real_popen); #if ARCH(Intel) __real_posix_spawn_2_15 = dlvsym (dlflag, "posix_spawn", SYS_POSIX_SPAWN_VERSION); __real_posix_spawnp_2_15 = dlvsym (dlflag, "posix_spawnp", SYS_POSIX_SPAWN_VERSION); #if WSIZE(32) __real_popen_2_1 = __real_popen; __real_popen_2_0 = dlvsym (dlflag, "popen", "GLIBC_2.0"); __real_posix_spawn_2_2 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2"); __real_posix_spawnp_2_2 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2"); #elif WSIZE(64) __real_posix_spawn_2_2_5 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2.5"); __real_posix_spawnp_2_2_5 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2.5"); #endif /* WSIZE() */ #endif /* ARCH(Intel) */ __real_grantpt = dlsym (dlflag, "grantpt"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_grantpt\n", __real_grantpt); __real_ptsname = dlsym (dlflag, "ptsname"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_ptsname\n", __real_ptsname); __real_system = dlsym (dlflag, "system"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_system\n", __real_system); __real_setuid = dlsym (dlflag, "setuid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setuid\n", __real_setuid); __real_seteuid = dlsym (dlflag, "seteuid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_seteuid\n", __real_seteuid); __real_setreuid = dlsym (dlflag, "setreuid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setreuid\n", __real_setreuid); __real_setgid = dlsym (dlflag, "setgid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setgid\n", __real_setgid); __real_setegid = dlsym (dlflag, "setegid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setegid\n", __real_setegid); __real_setregid = dlsym (dlflag, "setregid"); TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setregid\n", __real_setregid); return 0; } /*------------------------------------------------------------------------ */ /* Note: The following _prologue and _epilogue functions used to be dbx-visible. They are used to appropriately manage lineage-changing events, by quiescing and re-enabling/re-setting experiment collection before and after, and logging the lineage-change in the process/experiment undertaking it. As shown by the interposition functions for fork, exec, etc., which follow, the _prologue should be called immediately prior (such as a breakpoint action defined at function entry) and the _epilogue called immediately after (such as a breakpoint action defined at function return). */ /* Notes on MT from Solaris 10 man pthread_atfork: All multithreaded applications that call fork() in a POSIX threads program and do more than simply call exec(2) in the child of the fork need to ensure that the child is protected from deadlock. Since the "fork-one" model results in duplicating only the thread that called fork(), it is possible that at the time of the call another thread in the parent owns a lock. This thread is not duplicated in the child, so no thread will unlock this lock in the child. Deadlock occurs if the sin- gle thread in the child needs this lock. The problem is more serious with locks in libraries. Since a library writer does not know if the application using the library calls fork(), the library must protect itself from such a deadlock scenario. If the application that links with this library calls fork() and does not call exec() in the child, and if it needs a library lock that may be held by some other thread in the parent that is inside the library at the time of the fork, the application deadlocks inside the library. */ static void linetrace_ext_fork_prologue (const char *variant, char * n_lineage, int *following_fork) { TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", variant, n_lineage, *following_fork); __collector_env_print ("fork_prologue start"); if (dbg_current_mode != FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_fork_prologue(%s) ERROR: dbg_current_mode=%d, changing to FOLLOW_FORK!\n", variant, dbg_current_mode); dbg_current_mode = FOLLOW_ON; if (__collector_strncmp ((char *) variant, "clone", sizeof ("clone") - 1) == 0) { __collector_mutex_lock (&clone_lineage_lock); CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_C%d", curr_lineage, ++clone_linenum); __collector_mutex_unlock (&clone_lineage_lock); } else { __collector_mutex_lock (&fork_lineage_lock); CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_f%d", curr_lineage, ++fork_linenum); __collector_mutex_unlock (&fork_lineage_lock); } *following_fork = check_follow_fork (); /* write message before suspending, or it won't be written */ hrtime_t ts = GETRELTIME (); TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", variant, n_lineage, *following_fork); __collector_log_write ("\n", SP_JCMD_DESC_START, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, n_lineage, *following_fork); __collector_ext_dispatcher_thread_timer_suspend (); __collector_ext_hwc_lwp_suspend (); __collector_env_print ("fork_prologue end"); } static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * n_lineage, int *following_fork) { if (dbg_current_mode == FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) ERROR: dbg_current_mode=%d!\n", variant, dbg_current_mode); /* compute descendant experiment name */ char new_exp_name[LT_MAXPATHLEN]; /* save exp_name to global var */ if (!build_experiment_path (new_exp_name, sizeof (new_exp_name), n_lineage)) TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s): ERROR SP_COLLECTOR_EXPNAME not set\n", n_lineage); TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s):%d() returned %d %s; child experiment name = %s\n", variant, *following_fork, ret, (ret ? "parent" : "child"), new_exp_name); if (ret == 0) { /* *************************************child */ __collector_env_print ("fork_epilogue child at start"); /* start a new line */ fork_linenum = 0; __collector_mutex_init (&fork_lineage_lock); clone_linenum = 0; __collector_mutex_init (&clone_lineage_lock); __collector_env_update (NULL); __collector_env_print ("fork_epilogue child after env_update"); __collector_clean_state (); __collector_env_print ("fork_epilogue child after clean_slate"); __collector_line_cleanup (); __collector_env_print ("fork_epilogue child after line_cleanup"); if (*following_fork) { /* stop recording this experiment, but preserve env vars */ linetrace_dormant (); __collector_env_print ("fork_epilogue child after linetrace_dormant"); //static char exp_name_env[LT_MAXPATHLEN]; char * exp_name_env = CALL_UTIL (calloc)(LT_MAXPATHLEN, 1); CALL_UTIL (snprintf)(exp_name_env, LT_MAXPATHLEN, "%s=%s", SP_COLLECTOR_EXPNAME, new_exp_name); CALL_UTIL (putenv)(exp_name_env); const char *params = CALL_UTIL (getenv)(SP_COLLECTOR_PARAMS); int ret; if (*new_exp_name == 0) TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", SP_COLLECTOR_EXPNAME); else if (params == NULL) TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", SP_COLLECTOR_PARAMS); else if ((ret = __collector_open_experiment (new_exp_name, params, SP_ORIGIN_FORK))) TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: '%s' open failed, ret=%d\n", new_exp_name, ret); else TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: opened(%s)\n", new_exp_name); TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) returning to *child*\n", variant); } else { /* disable current and further linetrace experiment resumption */ TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) child calling line_close\n", variant); __collector_ext_line_close (); } __collector_env_print ("fork_epilogue child at end"); /* *************************************end child */ } else { /* *************************************parent */ __collector_env_print ("fork_epilogue parent at start"); __collector_ext_dispatcher_thread_timer_resume (); __collector_ext_hwc_lwp_resume (); hrtime_t ts = GETRELTIME (); char msg[256 + LT_MAXPATHLEN]; if (ret >= 0) CALL_UTIL (snprintf)(msg, sizeof (msg), "pid=%d", ret); else { /* delete stillborn experiment? */ char errmsg[256]; strerror_r (errno, errmsg, sizeof (errmsg)); CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); } __collector_log_write ("\n", SP_JCMD_DESC_STARTED, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, n_lineage, *following_fork, msg); /* environment remains set for collection */ __collector_env_print ("fork_epilogue parent at end"); /* *************************************end parent */ } dbg_current_mode = FOLLOW_NONE; *following_fork = 0; } static char** linetrace_ext_exec_prologue_end (const char *variant, const char* cmd_string, char *const envp[], int following_exec) { char **coll_env; TprintfT (DBG_LT0, "linetrace_ext_exec_prologue_end; variant=%s; cmd_string=%s; follow=%d\n", variant, cmd_string, following_exec); /* write message before suspending, or it won't be written */ hrtime_t ts = GETRELTIME (); __collector_log_write ("\n", SP_JCMD_EXEC_START, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, new_lineage, following_exec, cmd_string); if (following_exec) { coll_env = __collector_env_allocate (envp, 0); __collector_env_update (coll_env); extern char **environ; /* the process' actual environment */ if (environ == envp) /* user selected process environment */ environ = coll_env; } else coll_env = (char**) envp; __collector_env_printall ("linetrace_ext_exec_prologue_end", coll_env); if (!CALL_UTIL (strstr)(variant, "posix_spawn")) { __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_suspend_experiment ("suspend_for_exec"); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; } if (CALL_UTIL (strstr)(variant, "posix_spawn")) { __collector_ext_dispatcher_thread_timer_suspend (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_ext_hwc_lwp_suspend (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; } return (coll_env); } static char** linetrace_ext_exec_prologue (const char *variant, const char* path, char *const argv[], char *const envp[], int *following_exec) { char cmd_string[_POSIX_ARG_MAX] = {'\0'}; if (dbg_current_mode != FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_exec_prologue() ERROR: dbg_current_mode=%d, changing to FOLLOW_EXEC!\n", dbg_current_mode); dbg_current_mode = FOLLOW_ON; *following_exec = check_follow_exec (path); if (path != NULL) { /* escape any newline, " or \ characters in the exec command */ TprintfT (DBG_LT3, "linetrace_ext_exec_prologue(): arg0=%s\n", path); /* leave space in log message for terminator (and header) */ __collector_strlcpy (cmd_string, path, sizeof (cmd_string)); size_t len; unsigned argn = 0; if (argv[0]) { char *p; while (((p = argv[++argn]) != 0) && (len = __collector_strlen (cmd_string)) < sizeof (cmd_string) - 2) { cmd_string[len++] = ' '; __collector_strlcpy (cmd_string + len, p, sizeof (cmd_string) - len); } } } TprintfT (DBG_LT0, "linetrace_ext_exec_prologue(%s), lineage=%s, follow=%d, prog=%s, path=%s \n", variant, new_lineage, *following_exec, cmd_string, path); return linetrace_ext_exec_prologue_end (variant, cmd_string, envp, *following_exec); } static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec) { /* For exec, this routine is only entered if the exec failed */ /* However, posix_spawn() is expected to return */ if (dbg_current_mode == FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: dbg_current_mode=%d!\n", dbg_current_mode); TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue(%s):%d returned: %d, errno=%d\n", variant, *following_exec, ret, errno); if (!CALL_UTIL (strstr)(variant, "posix_spawn")) { __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_resume_experiment (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; } if (CALL_UTIL (strstr)(variant, "posix_spawn")) { __collector_ext_dispatcher_thread_timer_resume (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_ext_hwc_lwp_resume (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; } hrtime_t ts = GETRELTIME (); char msg[256]; if (ret) { char errmsg[256]; strerror_r (errno, errmsg, sizeof (errmsg)); CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); } else CALL_UTIL (snprintf)(msg, sizeof (msg), "rc=%d", ret); if (!CALL_UTIL (strstr)(variant, "posix_spawn")) __collector_log_write ("\n", SP_JCMD_EXEC_ERROR, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, new_lineage, *following_exec, msg); if (envp == NULL) TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: envp NULL after %s!\n", variant); dbg_current_mode = FOLLOW_NONE; *following_exec = 0; } static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo) { char cmd_string[_POSIX_ARG_MAX] = {'\0'}; char execfile[_POSIX_ARG_MAX] = {'\0'}; if (dbg_current_mode != FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_combo_prologue() ERROR: dbg_current_mode=%d! changing to FOLLOW_ON\n", dbg_current_mode); dbg_current_mode = FOLLOW_ON; if (cmd != NULL) { /* extract executable name from combo command */ unsigned len = strcspn (cmd, " "); __collector_strlcpy (execfile, cmd, len + 1); /* escape any newline, " or \ characters in the combo command */ /* leave space in log message for terminator (and header) */ __collector_strlcpy (cmd_string, cmd, sizeof (cmd_string)); } *following_combo = check_follow_combo (execfile); TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(%s) follow=%d, prog=%s\n\n", variant, *following_combo, cmd_string); /* Construct the lineage string for the new image */ new_lineage[0] = 0; __collector_strcat (new_lineage, "XXX"); /* write message before suspending, or it won't be written */ hrtime_t ts = GETRELTIME (); __collector_log_write ("\n", SP_JCMD_DESC_START, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, new_lineage, *following_combo, cmd_string); if (*following_combo) { __collector_env_update (NULL); TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(): Following %s(\"%s\")\n", variant, execfile); } __collector_ext_dispatcher_thread_timer_suspend (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_ext_hwc_lwp_suspend (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; } static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo) { if (dbg_current_mode == FOLLOW_NONE) TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue() ERROR: dbg_current_mode=FOLLOW_NONE\n"); TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue(%s):%d() returned %d\n", variant, *following_combo, ret); __collector_ext_dispatcher_thread_timer_resume (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; __collector_ext_hwc_lwp_resume (); __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; hrtime_t ts = GETRELTIME (); __collector_log_write ("\n", SP_JCMD_DESC_STARTED, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), variant, *following_combo, ret); dbg_current_mode = FOLLOW_NONE; *following_combo = 0; } /*------------------------------------------------------------- fork */ pid_t fork () __attribute__ ((weak, alias ("__collector_fork"))); pid_t _fork () __attribute__ ((weak, alias ("__collector_fork"))); pid_t __collector_fork (void) { pid_t ret; if (NULL_PTR (fork)) { TprintfT (DBG_LT0, "__collector_fork() calling init_lineage_intf()\n"); init_lineage_intf (); } __collector_env_print ("__collector_fork start"); int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_fork() interposition: line_mode=%d combo=%d\n", line_mode, combo_flag); if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) { TprintfT (DBG_LT0, "__collector_fork() not following, returning CALL_REAL(fork)()\n"); return CALL_REAL (fork)(); } int following_fork = 0; linetrace_ext_fork_prologue ("fork", new_lineage, &following_fork); /* since libpthread/fork ends up calling fork1, it's a combo */ PUSH_REENTRANCE (guard); ret = CALL_REAL (fork)(); POP_REENTRANCE (guard); linetrace_ext_fork_epilogue ("fork", ret, new_lineage, &following_fork); return ret; } /*------------------------------------------------------------- vfork */ /* vfork interposition in the usual sense is not possible, since vfork(2) relies on specifics of the stack frames in the parent and child which only work when the child's exec (or _exit) are in the same stack frame as the vfork: this isn't the case when there's interposition on exec. As a workaround, the interposing vfork calls fork1 instead of the real vfork. Note that fork1 is somewhat less efficient than vfork, and requires additional memory, which may result in a change of application behaviour when libcollector is loaded (even when collection is not active), affecting not only direct use of vfork by the subject application, but also indirect use through system, popen, and the other combos. */ pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork"))); pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork"))); pid_t __collector_vfork (void) { if (NULL_PTR (vfork)) init_lineage_intf (); int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_vfork() interposing: line_mode=%d combo=%d\n", line_mode, combo_flag); if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) return CALL_REAL (fork)(); /* this warning is also appropriate for combos which use vfork, however, let's assume this is achieved elsewhere */ (void) __collector_log_write ("%s\n", SP_JCMD_CWARN, COL_WARN_VFORK, "fork"); char new_lineage[LT_MAXNAMELEN]; new_lineage[0] = 0; int following_fork = 0; linetrace_ext_fork_prologue ("vfork", new_lineage, &following_fork); pid_t ret = CALL_REAL (fork)(); linetrace_ext_fork_epilogue ("vfork", ret, new_lineage, &following_fork); return ret; } /*------------------------------------------------------------- execve */ int execve () __attribute__ ((weak, alias ("__collector_execve"))); int __collector_execve (const char* path, char *const argv[], char *const envp[]) { static char **coll_env = NULL; /* environment for collection */ if (NULL_PTR (execve)) init_lineage_intf (); int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_execve(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag); if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ __collector_env_unset ((char**) envp); if (line_mode != LM_TRACK_LINEAGE || combo_flag) return CALL_REAL (execve)(path, argv, envp); int following_exec = 0; coll_env = linetrace_ext_exec_prologue ("execve", path, argv, envp, &following_exec); TprintfT (DBG_LT2, "__collector_execve(): coll_env=0x%p\n", coll_env); __collector_env_printall ("__collector_execve", coll_env); int ret = CALL_REAL (execve)(path, argv, coll_env); linetrace_ext_exec_epilogue ("execve", envp, ret, &following_exec); return ret; } int execvp () __attribute__ ((weak, alias ("__collector_execvp"))); int __collector_execvp (const char* file, char *const argv[]) { extern char **environ; /* the process' actual environment */ char ** envp = environ; if (NULL_PTR (execvp)) init_lineage_intf (); int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_execvp(file=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", file ? file : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", line_mode, combo_flag); if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ __collector_env_unset ((char**) envp); if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) return CALL_REAL (execvp)(file, argv); int following_exec = 0; #ifdef DEBUG char **coll_env = /* environment for collection */ #endif /* DEBUG */ linetrace_ext_exec_prologue ("execvp", file, argv, envp, &following_exec); TprintfT (DBG_LT0, "__collector_execvp(): coll_env=0x%p\n", coll_env); int ret = CALL_REAL (execvp)(file, argv); linetrace_ext_exec_epilogue ("execvp", envp, ret, &following_exec); return ret; } int execv () __attribute__ ((weak, alias ("__collector_execv"))); int __collector_execv (const char* path, char *const argv[]) { int ret; extern char **environ; /* the process' actual environment */ char ** envp = environ; TprintfT (DBG_LT0, "__collector_execv(path=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", line_mode, get_combo_flag ()); ret = __collector_execve (path, argv, envp); return ret; } int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle"))); int __collector_execle (const char* path, const char *arg0, ...) { TprintfT (DBG_LT0, "__collector_execle(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", arg0 ? arg0 : "NULL", line_mode, get_combo_flag ()); char **argp; va_list args; char **argvec; register char **environmentp; int nargs = 0; char *nextarg; va_start (args, arg0); while (va_arg (args, char *) != (char *) 0) nargs++; /* * save the environment pointer, which is at the end of the * variable argument list */ environmentp = va_arg (args, char **); va_end (args); /* * load the arguments in the variable argument list * into the argument vector, and add the terminating null pointer */ va_start (args, arg0); /* workaround for bugid 1242839 */ argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); argp = argvec; *argp++ = (char *) arg0; while ((nextarg = va_arg (args, char *)) != (char *) 0) *argp++ = nextarg; va_end (args); *argp = (char *) 0; return __collector_execve (path, argvec, environmentp); } int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp"))); int __collector_execlp (const char* file, const char *arg0, ...) { TprintfT (DBG_LT0, "__collector_execlp(file=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", file ? file : "NULL", arg0 ? arg0 : "NULL", line_mode, get_combo_flag ()); char **argp; va_list args; char **argvec; int nargs = 0; char *nextarg; va_start (args, arg0); while (va_arg (args, char *) != (char *) 0) nargs++; va_end (args); /* * load the arguments in the variable argument list * into the argument vector and add the terminating null pointer */ va_start (args, arg0); /* workaround for bugid 1242839 */ argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); argp = argvec; *argp++ = (char *) arg0; while ((nextarg = va_arg (args, char *)) != (char *) 0) *argp++ = nextarg; va_end (args); *argp = (char *) 0; return __collector_execvp (file, argvec); } int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl"))); int __collector_execl (const char* path, const char *arg0, ...) { TprintfT (DBG_LT0, "__collector_execl(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", arg0 ? arg0 : "NULL", line_mode, get_combo_flag ()); char **argp; va_list args; char **argvec; extern char **environ; int nargs = 0; char *nextarg; va_start (args, arg0); while (va_arg (args, char *) != (char *) 0) nargs++; va_end (args); /* * load the arguments in the variable argument list * into the argument vector and add the terminating null pointer */ va_start (args, arg0); /* workaround for bugid 1242839 */ argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); argp = argvec; *argp++ = (char *) arg0; while ((nextarg = va_arg (args, char *)) != (char *) 0) *argp++ = nextarg; va_end (args); *argp = (char *) 0; return __collector_execve (path, argvec, environ); } #include /*-------------------------------------------------------- posix_spawn */ #if ARCH(Intel) // map interposed symbol versions static int __collector_posix_spawn_symver (int(real_posix_spawn) (), pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); SYMVER_ATTRIBUTE (__collector_posix_spawn_2_15, posix_spawn@GLIBC_2.15) int __collector_posix_spawn_2_15 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawn_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawn)) init_lineage_intf (); return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_15), pidp, path, file_actions, attrp, argv, envp); } #if WSIZE(32) SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2, posix_spawn@GLIBC_2.2) int __collector_posix_spawn_2_2 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawn_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawn)) init_lineage_intf (); return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2), pidp, path, file_actions, attrp, argv, envp); } #else /* ^WSIZE(32) */ SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2_5, posix_spawn@GLIBC_2.2.5) int __collector_posix_spawn_2_2_5 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawn_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawn)) init_lineage_intf (); return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2_5), pidp, path, file_actions, attrp, argv, envp); } #endif /* ^WSIZE(32) */ static int __collector_posix_spawn_symver (int(real_posix_spawn) (), #else /* ^ARCH(Intel) */ int __collector_posix_spawn ( #endif /* ARCH() */ pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { int ret; static char **coll_env = NULL; /* environment for collection */ if (NULL_PTR (posix_spawn)) init_lineage_intf (); if (NULL_PTR (posix_spawn)) { TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s) interposing: ERROR, posix_spawn() not found by dlsym\n", path ? path : "NULL"); return -1; /* probably should set errno */ } int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag); if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ __collector_env_unset ((char**) envp); if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) { #if ARCH(Intel) return (real_posix_spawn) (pidp, path, file_actions, attrp, argv, envp); #else return CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, envp); #endif } int following_exec = 0; coll_env = linetrace_ext_exec_prologue ("posix_spawn", path, argv, envp, &following_exec); TprintfT (DBG_LT0, "__collector_posix_spawn(): coll_env=0x%p\n", coll_env); __collector_env_printall ("__collector_posix_spawn", coll_env); PUSH_REENTRANCE (guard); #if ARCH(Intel) ret = (real_posix_spawn) (pidp, path, file_actions, attrp, argv, coll_env); #else ret = CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, coll_env); #endif POP_REENTRANCE (guard); linetrace_ext_exec_epilogue ("posix_spawn", envp, ret, &following_exec); return ret; } /*-------------------------------------------------------- posix_spawnp */ #if ARCH(Intel) // map interposed symbol versions static int __collector_posix_spawnp_symver (int(real_posix_spawnp) (), pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_15, posix_spawnp@GLIBC_2.15) int // Common interposition __collector_posix_spawnp_2_15 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawnp_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawnp)) init_lineage_intf (); return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_15), pidp, path, file_actions, attrp, argv, envp); } #if WSIZE(32) SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2, posix_spawnp@GLIBC_2.2) int __collector_posix_spawnp_2_2 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawnp_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawnp)) init_lineage_intf (); return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2), pidp, path, file_actions, attrp, argv, envp); } #else /* ^WSIZE(32) */ SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2_5, posix_spawnp@GLIBC_2.2.5) int __collector_posix_spawnp_2_2_5 (pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n", CALL_REAL (posix_spawnp_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); if (NULL_PTR (posix_spawnp)) init_lineage_intf (); return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2_5), pidp, path, file_actions, attrp, argv, envp); } #endif /* ^WSIZE(32) */ static int __collector_posix_spawnp_symver (int(real_posix_spawnp) (), #else /* ^ARCH(Intel) */ int __collector_posix_spawnp ( #endif /* ARCH() */ pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]){ int ret; static char **coll_env = NULL; /* environment for collection */ if (NULL_PTR (posix_spawnp)) init_lineage_intf (); if (NULL_PTR (posix_spawnp)) { TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s) interposing: ERROR, posix_spawnp() not found by dlsym\n", path ? path : "NULL"); return -1; /* probably should set errno */ } int * guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag); if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ __collector_env_unset ((char**) envp); if (line_mode != LM_TRACK_LINEAGE || combo_flag) { #if ARCH(Intel) return (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, envp); #else return CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, envp); #endif } int following_exec = 0; coll_env = linetrace_ext_exec_prologue ("posix_spawnp", path, argv, envp, &following_exec); TprintfT (DBG_LT0, "__collector_posix_spawnp(): coll_env=0x%p\n", coll_env); __collector_env_printall ("__collector_posix_spawnp", coll_env); PUSH_REENTRANCE (guard); #if ARCH(Intel) ret = (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, coll_env); #else ret = CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, coll_env); #endif POP_REENTRANCE (guard); linetrace_ext_exec_epilogue ("posix_spawnp", envp, ret, &following_exec); return ret; } /*------------------------------------------------------------- system */ int system () __attribute__ ((weak, alias ("__collector_system"))); int __collector_system (const char *cmd) { if (NULL_PTR (system)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_system(cmd=%s) interposing: line_mode=%d combo=%d\n", cmd ? cmd : "NULL", line_mode, get_combo_flag ()); int *guard = NULL; if (line_mode == LM_TRACK_LINEAGE) INIT_REENTRANCE (guard); if (guard == NULL) return CALL_REAL (system)(cmd); int following_combo = 0; linetrace_ext_combo_prologue ("system", cmd, &following_combo); PUSH_REENTRANCE (guard); int ret = CALL_REAL (system)(cmd); POP_REENTRANCE (guard); linetrace_ext_combo_epilogue ("system", ret, &following_combo); return ret; } /*------------------------------------------------------------- popen */ // map interposed symbol versions #if ARCH(Intel) && WSIZE(32) static FILE * __collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode); SYMVER_ATTRIBUTE (__collector_popen_2_1, popen@GLIBC_2.1) FILE * __collector_popen_2_1 (const char *cmd, const char *mode) { if (NULL_PTR (popen)) init_lineage_intf (); TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_1@%p\n", CALL_REAL (popen_2_1)); return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode); } SYMVER_ATTRIBUTE (__collector_popen_2_0, popen@GLIBC_2.0) FILE * __collector_popen_2_0 (const char *cmd, const char *mode) { if (NULL_PTR (popen)) init_lineage_intf (); TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_0@%p\n", CALL_REAL (popen_2_0)); return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode); } SYMVER_ATTRIBUTE (__collector__popen_2_1, _popen@GLIBC_2.1) FILE * __collector__popen_2_1 (const char *cmd, const char *mode) { if (NULL_PTR (popen)) init_lineage_intf (); TprintfT (DBG_LTT, "linetrace: GLIBC: __collector__popen_2_1@%p\n", CALL_REAL (popen_2_1)); return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode); } SYMVER_ATTRIBUTE (__collector__popen_2_0, _popen@GLIBC_2.0) FILE * __collector__popen_2_0 (const char *cmd, const char *mode) { if (NULL_PTR (popen)) init_lineage_intf (); return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode); } #else // WSIZE(64) FILE * popen () __attribute__ ((weak, alias ("__collector_popen"))); #endif #if ARCH(Intel) && WSIZE(32) static FILE * __collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode) #else FILE * __collector_popen (const char *cmd, const char *mode) #endif { FILE *ret; if (NULL_PTR (popen)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_popen(cmd=%s) interposing: line_mode=%d combo=%d\n", cmd ? cmd : "NULL", line_mode, get_combo_flag ()); int *guard = NULL; if (line_mode == LM_TRACK_LINEAGE) INIT_REENTRANCE (guard); if (guard == NULL) { #if ARCH(Intel) && WSIZE(32) return (real_popen) (cmd, mode); #else return CALL_REALF (popen)(cmd, mode); #endif } int following_combo = 0; linetrace_ext_combo_prologue ("popen", cmd, &following_combo); PUSH_REENTRANCE (guard); #if ARCH(Intel) && WSIZE(32) ret = (real_popen) (cmd, mode); #else ret = CALL_REALF (popen)(cmd, mode); #endif POP_REENTRANCE (guard); linetrace_ext_combo_epilogue ("popen", (ret == NULL) ? (-1) : 0, &following_combo); return ret; } /*------------------------------------------------------------- grantpt */ int grantpt () __attribute__ ((weak, alias ("__collector_grantpt"))); int __collector_grantpt (const int fildes) { if (NULL_PTR (grantpt)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_grantpt(%d) interposing: line_mode=%d combo=%d\n", fildes, line_mode, get_combo_flag ()); int *guard = NULL; if (line_mode == LM_TRACK_LINEAGE) INIT_REENTRANCE (guard); if (guard == NULL) return CALL_REAL (grantpt)(fildes); int following_combo = 0; linetrace_ext_combo_prologue ("grantpt", "/usr/lib/pt_chmod", &following_combo); PUSH_REENTRANCE (guard); int ret = CALL_REAL (grantpt)(fildes); POP_REENTRANCE (guard); linetrace_ext_combo_epilogue ("grantpt", ret, &following_combo); return ret; } /*------------------------------------------------------------- ptsname */ char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname"))); char * __collector_ptsname (const int fildes) { if (NULL_PTR (ptsname)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_ptsname(%d) interposing: line_mode=%d combo=%d\n", fildes, line_mode, get_combo_flag ()); int *guard = NULL; if (line_mode == LM_TRACK_LINEAGE) INIT_REENTRANCE (guard); if (guard == NULL) return CALL_REALC (ptsname)(fildes); int following_combo = 0; linetrace_ext_combo_prologue ("ptsname", "/usr/lib/pt_chmod", &following_combo); PUSH_REENTRANCE (guard); char *ret = CALL_REALC (ptsname)(fildes); POP_REENTRANCE (guard); linetrace_ext_combo_epilogue ("ptsname", (ret == NULL) ? (-1) : 1, &following_combo); return ret; } /*------------------------------------------------------------- clone */ /* clone can be fork-like or pthread_create-like, depending on whether * the flag CLONE_VM is set. If CLONE_VM is not set, then we interpose * clone in the way similar to interposing fork; if CLONE_VM is set, * then we interpose clone in the way similar to interposing pthread_create. * One special case is not handled: when CLONE_VM is set but CLONE_THREAD * is not, if the parent process exits earlier than the child process, * experiment will close, losing data from child process. */ typedef struct __collector_clone_arg { int (*fn)(void *); void * arg; char * new_lineage; int following_fork; } __collector_clone_arg_t; static int __collector_clone_fn (void *fn_arg) { int (*fn)(void *) = ((__collector_clone_arg_t*) fn_arg)->fn; void * arg = ((__collector_clone_arg_t*) fn_arg)->arg; char * new_lineage = ((__collector_clone_arg_t*) fn_arg)->new_lineage; int following_fork = ((__collector_clone_arg_t*) fn_arg)->following_fork; __collector_freeCSize (__collector_heap, fn_arg, sizeof (__collector_clone_arg_t)); linetrace_ext_fork_epilogue ("clone", 0, new_lineage, &following_fork); return fn (arg); } int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone"))); int __collector_clone (int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */) { int ret; va_list va; if (flags & CLONE_VM) { va_start (va, arg); ret = __collector_ext_clone_pthread (fn, child_stack, flags, arg, va); va_end (va); } else { if (NULL_PTR (clone)) init_lineage_intf (); int *guard = NULL; int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; TprintfT (DBG_LT0, "__collector_clone() interposition: line_mode=%d combo=%d\n", line_mode, combo_flag); char new_lineage[LT_MAXNAMELEN]; int following_fork = 0; __collector_clone_arg_t *funcinfo = __collector_allocCSize (__collector_heap, sizeof (__collector_clone_arg_t), 1); (*funcinfo).fn = fn; (*funcinfo).arg = arg; (*funcinfo).new_lineage = new_lineage; (*funcinfo).following_fork = 0; pid_t * ptid = NULL; struct user_desc * tls = NULL; pid_t * ctid = NULL; int num_args = 0; va_start (va, arg); if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) { ptid = va_arg (va, pid_t *); tls = va_arg (va, struct user_desc*); ctid = va_arg (va, pid_t *); num_args = 3; } else if (flags & CLONE_SETTLS) { ptid = va_arg (va, pid_t *); tls = va_arg (va, struct user_desc*); num_args = 2; } else if (flags & CLONE_PARENT_SETTID) { ptid = va_arg (va, pid_t *); num_args = 1; } if ((line_mode != LM_TRACK_LINEAGE) || combo_flag || funcinfo == NULL) { switch (num_args) { case 3: ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); break; case 2: ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); break; case 1: ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); break; default: ret = CALL_REAL (clone)(fn, child_stack, flags, arg); break; } va_end (va); return ret; } linetrace_ext_fork_prologue ("clone", new_lineage, &following_fork); (*funcinfo).following_fork = following_fork; switch (num_args) { case 3: ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls, ctid); break; case 2: ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls); break; case 1: ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid); break; default: ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo); break; } va_end (va); if (ret < 0) __collector_freeCSize (__collector_heap, funcinfo, sizeof (__collector_clone_arg_t)); TprintfT (DBG_LT0, "__collector_clone() interposing: pid=%d\n", ret); linetrace_ext_fork_epilogue ("clone", ret, new_lineage, &following_fork); } return ret; } /*-------------------------------------------------------------------- setuid */ int setuid () __attribute__ ((weak, alias ("__collector_setuid"))); int _setuid () __attribute__ ((weak, alias ("__collector_setuid"))); int __collector_setuid (uid_t ruid) { if (NULL_PTR (setuid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_setuid(0x%x) interposing\n", ruid); check_reuid_change (ruid, -1); int ret = CALL_REAL (setuid)(ruid); TprintfT (DBG_LT0, "__collector_setuid(0x%x) returning %d\n", ruid, ret); return ret; } /*------------------------------------------------------------------- seteuid */ int seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); int __collector_seteuid (uid_t euid) { if (NULL_PTR (seteuid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_seteuid(0x%x) interposing\n", euid); check_reuid_change (-1, euid); int ret = CALL_REAL (seteuid)(euid); TprintfT (DBG_LT0, "__collector_seteuid(0x%x) returning %d\n", euid, ret); return ret; } /*------------------------------------------------------------------ setreuid */ int setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); int __collector_setreuid (uid_t ruid, uid_t euid) { if (NULL_PTR (setreuid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) interposing\n", ruid, euid); check_reuid_change (ruid, euid); int ret = CALL_REAL (setreuid)(ruid, euid); TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) returning %d\n", ruid, euid, ret); return ret; } /*-------------------------------------------------------------------- setgid */ int setgid () __attribute__ ((weak, alias ("__collector_setgid"))); int _setgid () __attribute__ ((weak, alias ("__collector_setgid"))); int __collector_setgid (gid_t rgid) { if (NULL_PTR (setgid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_setgid(0x%x) interposing\n", rgid); check_regid_change (rgid, -1); int ret = CALL_REAL (setgid)(rgid); TprintfT (DBG_LT0, "__collector_setgid(0x%x) returning %d\n", rgid, ret); return ret; } /*------------------------------------------------------------------- setegid */ int setegid () __attribute__ ((weak, alias ("__collector_setegid"))); int _setegid () __attribute__ ((weak, alias ("__collector_setegid"))); int __collector_setegid (gid_t egid) { if (NULL_PTR (setegid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_setegid(0x%x) interposing\n", egid); check_regid_change (-1, egid); int ret = CALL_REAL (setegid)(egid); TprintfT (DBG_LT0, "__collector_setegid(0x%x) returning %d\n", egid, ret); return ret; } /*------------------------------------------------------------------ setregid */ int setregid () __attribute__ ((weak, alias ("__collector_setregid"))); int _setregid () __attribute__ ((weak, alias ("__collector_setregid"))); int __collector_setregid (gid_t rgid, gid_t egid) { if (NULL_PTR (setregid)) init_lineage_intf (); TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) interposing\n", rgid, egid); check_regid_change (rgid, egid); int ret = CALL_REAL (setregid)(rgid, egid); TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) returning %d\n", rgid, egid, ret); return ret; } /*------------------------------------------------------- selective following */ static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *progname) { regex_t regex_desc; if (!follow_spec) { TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES NULL follow_spec\n"); return 1; } int ercode = regcomp (®ex_desc, follow_spec, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); if (ercode) { // syntax error in parsing string #ifdef DEBUG char errbuf[256]; regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf)); TprintfT (DBG_LT0, "linetrace_follow_experiment: regerror()=%s\n", errbuf); #endif return 1; } TprintfT (DBG_LT0, "linetrace_follow_experiment(): compiled spec='%s'\n", follow_spec); if (lineage_str) { if (!regexec (®ex_desc, lineage_str, 0, NULL, 0)) { TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES lineage (follow_spec=%s,lineage=%s)\n", follow_spec, lineage_str); return 1; } } if (progname) { if (!regexec (®ex_desc, progname, 0, NULL, 0)) { TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES progname (follow_spec=%s,progname=%s)\n", follow_spec, progname); return 1; } } TprintfT (DBG_LT0, "linetrace_follow_experiment(): DOES NOT MATCH (follow_spec=%s,lineage=%s,progname=%s)\n", follow_spec, lineage_str ? lineage_str : "NULL", progname ? progname : "NULL"); return 0; }