staprun/staprun.c - systemtap
Global variables defined
Functions defined
Macros defined
Source code
#define _XOPEN_SOURCE
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include "staprun.h"
#include "../privilege.h"
#include "../runtime/k_syms.h"
#include <string.h>
#include <sys/uio.h>
#include <glob.h>
#include <time.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
char *__name__ = "staprun";
extern long delete_module(const char *, unsigned int);
int send_relocations ();
int send_tzinfo ();
int send_privilege_credentials (privilege_t user_credentials);
int send_remote_id ();
static int remove_module(const char *name, int verb);
static int stap_module_inserted = -1;
static void term_signal_handler(int signum __attribute ((unused)))
{
if (stap_module_inserted == 0) {
close_ctl_channel();
remove_module(modname, 1);
free(modname);
}
_exit(1);
}
void setup_term_signals(void)
{
sigset_t s;
struct sigaction a;
sigfillset(&s);
sigprocmask(SIG_SETMASK, &s, NULL);
memset(&a, 0, sizeof(a));
sigfillset(&a.sa_mask);
a.sa_handler = term_signal_handler;
sigaction(SIGHUP, &a, NULL);
sigaction(SIGINT, &a, NULL);
sigaction(SIGTERM, &a, NULL);
sigaction(SIGQUIT, &a, NULL);
sigemptyset(&s);
sigprocmask(SIG_SETMASK, &s, NULL);
}
static int run_as(int exec_p, uid_t uid, gid_t gid, const char *path, char *const argv[])
{
pid_t pid;
int rstatus;
if (verbose >= 2) {
int i = 0;
eprintf(exec_p ? "execing: ": "spawning: ");
while (argv[i]) {
eprintf("%s ", argv[i]);
i++;
}
eprintf("\n");
}
if (exec_p)
pid = 0;
else
pid = fork();
if (pid < 0)
{
_perr("fork");
return -1;
}
if (pid == 0) {
if (setresgid (gid, gid, gid) < 0) {
_perr("setresgid");
exit(1);
}
if (setresuid (uid, uid, uid) < 0) {
_perr("setresuid");
exit(1);
}
if (execv(path, argv) < 0)
perror(path);
_exit(1);
}
if (waitpid(pid, &rstatus, 0) < 0)
return -1;
if (WIFEXITED(rstatus))
return WEXITSTATUS(rstatus);
return -1;
}
static int enable_uprobes(void)
{
char *argv[10];
char runtimeko[2048];
int rc;
if (uprobes_path)
snprintf (runtimeko, sizeof(runtimeko), "%s", uprobes_path);
else
snprintf (runtimeko, sizeof(runtimeko), "%s/uprobes/uprobes.ko",
(getenv("SYSTEMTAP_RUNTIME") ?: PKGDATADIR "/runtime"));
dbug(2, "Inserting uprobes module from %s.\n", runtimeko);
argv[0] = NULL;
rc = insert_module(runtimeko, NULL, argv, assert_uprobes_module_permissions, NULL);
if ((rc == 0) || (rc == -EEXIST)) return 0;
err("Couldn't insert module '%s': %s\n", runtimeko, moderror(errno));
return 1; }
static int insert_stap_module(privilege_t *user_credentials)
{
char special_options[128];
int rc, fips_mode_fd;
char fips_mode = '0';
char *misc = "";
if (snprintf_chk(special_options, sizeof (special_options),
"_stp_bufsize=%d", buffer_size))
return -1;
fips_mode_fd = open("/proc/sys/crypto/fips_enabled", O_RDONLY);
if (fips_mode_fd >= 0) {
char c;
rc = read(fips_mode_fd, &c, 1);
if (rc == 1) fips_mode = c;
close (fips_mode_fd);
}
if ((fips_mode != '0') && !getenv("STAP_FIPS_OVERRIDE")) {
errno = EPERM;
stap_module_inserted = -1;
misc = "in FIPS mode ";
} else {
stap_module_inserted = insert_module(modpath, special_options,
modoptions,
assert_stap_module_permissions,
user_credentials);
}
if (stap_module_inserted != 0)
err("Couldn't insert module %s'%s': %s\n", misc, modpath, moderror(errno));
return stap_module_inserted;
}
static void remove_all_modules(void)
{
char *base;
struct statfs st;
struct dirent *d;
DIR *moddir;
if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC)
base = "/sys/kernel/debug/systemtap";
else
base = "/proc/systemtap";
moddir = opendir(base);
if (moddir) {
while ((d = readdir(moddir)))
if (remove_module(d->d_name, 0) == 0)
printf("Module %s removed.\n", d->d_name);
closedir(moddir);
}
}
static int remove_module(const char *name, int verb)
{
int ret;
dbug(2, "%s\n", name);
#ifdef PR_SET_NAME
prctl (PR_SET_NAME, "staprun-d");
#endif
(void) verb; XXX
if (strcmp(name, "*") == 0) {
remove_all_modules();
return 0;
}
ret = init_ctl_channel (name, 0);
if (ret < 0) {
err("'%s' is not a zombie systemtap module.\n", name);
return ret;
}
close_ctl_channel ();
dbug(2, "removing module %s\n", name);
PROBE1(staprun, remove__module, name);
ret = delete_module (name, O_NONBLOCK);
if (ret != 0) {
XXX err("Couldn't remove module '%s': %s.\n", name, strerror(errno));
return 1;
}
dbug(1, "Module %s removed.\n", name);
return 0;
}
void disable_kprobes_optimization()
{
const char* proc_kprobes = "/proc/sys/debug/kprobes-optimization";
char prev;
int rc, fd;
struct utsname uts;
if (0 && (uname (&uts) == 0) && (strverscmp (uts.release, "3.4") >= 0))
return;
if (getenv ("STAP_PR13193_OVERRIDE"))
return;
fd = open (proc_kprobes, O_RDONLY);
if (fd < 0)
return;
rc = read (fd, &prev, sizeof(prev));
(void) close (fd);
if (rc < 1 || prev == '0') return;
fd = open (proc_kprobes, O_WRONLY);
if (fd < 0)
return;
prev = '0'; rc = write (fd, &prev, sizeof(prev));
(void) close (fd);
if (rc == 1)
dbug(1, "Disabled %s.\n", proc_kprobes);
else
dbug(1, "Error %d/%d disabling %s.\n", rc, errno, proc_kprobes);
}
int init_staprun(void)
{
privilege_t user_credentials = pr_unknown;
int rc;
dbug(2, "init_staprun\n");
if (mountfs() < 0)
return -1;
rc = 0;
if (delete_mod)
exit(remove_module(modname, 1));
if (attach_mod) {
rc = init_ctl_channel (modname, 0);
if (rc >= 0)
close_ctl_channel ();
} else {
if (need_uprobes && enable_uprobes() != 0)
return -1;
disable_kprobes_optimization();
if (insert_stap_module(& user_credentials) < 0) {
if(!rename_mod && errno == EEXIST)
err("Rerun with staprun option '-R' to rename this module.\n");
return -1;
}
rc = init_ctl_channel (modname, 0);
if (rc >= 0) {
send_privilege_credentials(user_credentials);
rc = send_relocations();
if (rc == 0) {
rc = send_tzinfo();
if (rc == 0 && remote_id >= 0)
send_remote_id();
}
close_ctl_channel ();
}
if (rc != 0)
remove_module(modname, 1);
}
return rc;
}
int main(int argc, char **argv)
{
int rc;
rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV")
|| unsetenv("BASH_ENV");
if (rc) {
_perr("unsetenv failed");
exit(-1);
}
if (getuid() != geteuid()) { rc = unsetenv("SYSTEMTAP_STAPRUN") ||
unsetenv("SYSTEMTAP_STAPIO") ||
unsetenv("SYSTEMTAP_RUNTIME");
if (rc) {
_perr("unsetenv failed");
exit(-1);
}
}
setup_signals();
setup_term_signals();
parse_args(argc, argv);
if (relay_basedir_fd >= 0) {
err(_("Relay basedir -F option is invalid for staprun\n"));
exit(1);
}
if (buffer_size)
dbug(2, "Using a buffer of %u MB.\n", buffer_size);
int mod_optind = optind;
if (optind < argc) {
parse_modpath(argv[optind++]);
dbug(2, "modpath=\"%s\", modname=\"%s\"\n", modpath, modname);
}
if (optind < argc) {
if (attach_mod) {
err("Cannot have module options with attach (-A).\n");
usage(argv[0],1);
} else {
unsigned start_idx = 0;
while (optind < argc && start_idx + 1 < MAXMODOPTIONS)
modoptions[start_idx++] = argv[optind++];
modoptions[start_idx] = NULL;
}
}
if (modpath == NULL || *modpath == '\0') {
err("Need a module name or path to load.\n");
usage(argv[0],1);
}
if (geteuid() != 0) {
err("The effective user ID of staprun must be set to the root user.\n"
" Check permissions on staprun and ensure it is a setuid root program.\n");
exit(1);
}
char verbose_level[33];
sprintf(verbose_level, "%d", verbose);
rc = setenv("SYSTEMTAP_VERBOSE", verbose_level, 0);
if (rc) {
_perr("SYSTEMTAP_VERBOSE setenv failed");
exit(-1);
}
if (init_staprun())
exit(1);
argv[0] = getenv ("SYSTEMTAP_STAPIO") ?: PKGLIBDIR "/stapio";
if(rename_mod)
argv[mod_optind] = modname;
#ifdef HAVE_OPENAT
if (relay_basedir_fd >= 0) {
char ** new_argv = calloc(sizeof(char *),argc+2);
const int new_Foption_size = 10; char * new_Foption = malloc(new_Foption_size);
int i;
if (new_argv && new_Foption) {
snprintf (new_Foption, new_Foption_size, "-F%d", relay_basedir_fd);
for (i=0; i < argc && argv[i] != NULL; i++)
new_argv[i] = argv[i];
new_argv[i++] = new_Foption; new_argv[i++] = NULL;
argv = new_argv;
}
}
#endif
if (run_as (1, getuid(), getgid(), argv[0], argv) < 0) {
perror(argv[0]);
goto err;
}
free(modname);
return 0;
err:
remove_module(modname, 1);
free(modname);
return 1;
}
int send_a_relocation (const char* module, const char* reloc, unsigned long long address)
{
struct _stp_msg_relocation msg;
int rc;
if (strlen(module) >= STP_MODULE_NAME_LEN-1) {
dbug (1, "module name too long: %s\n", module);
return -EINVAL;
}
strncpy (msg.module, module, STP_MODULE_NAME_LEN);
if (strlen(reloc) >= STP_SYMBOL_NAME_LEN-1) {
dbug (1, "reloc name too long: %s\n", reloc);
return -EINVAL;
}
strncpy (msg.reloc, reloc, STP_MODULE_NAME_LEN);
msg.address = address;
rc = send_request (STP_RELOCATION, & msg, sizeof (msg));
if (rc != 0)
perror ("Unable to send relocation");
return rc;
}
int send_relocation_kernel ()
{
FILE* kallsyms;
int rc = 0;
errno = 0;
kallsyms = fopen ("/proc/kallsyms", "r");
if (kallsyms == NULL)
{
perror("cannot open /proc/kallsyms");
return errno;
}
else
{
int done_with_kallsyms = 0;
char *line = NULL;
size_t linesz = 0;
while (! feof(kallsyms) && !done_with_kallsyms)
{
ssize_t linesize = getline (& line, & linesz, kallsyms);
if (linesize > 0)
{
unsigned long long address;
int pos = -1;
if (sscanf (line, "%llx %*c %n", &address, &pos) == 1
&& pos != -1
&& linesize - pos == sizeof KERNEL_RELOC_SYMBOL
&& !strcmp(line + pos, KERNEL_RELOC_SYMBOL "\n"))
{
rc = send_a_relocation ("kernel", "_stext", address);
if (rc != 0)
break;
done_with_kallsyms=1;
}
}
}
free (line);
fclose (kallsyms);
if (!done_with_kallsyms)
return rc;
if (!access("/sys/kernel/notes", R_OK))
rc = send_a_relocation ("kernel", ".note.gnu.build-id", 2);
}
return rc;
}
int send_relocation_modules ()
{
unsigned i = 0;
glob_t globbuf;
globbuf.gl_pathc = 0;
int r = glob("/sys/module/*/sections/*", GLOB_PERIOD, NULL, &globbuf);
if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
return r;
for (i=0; i<globbuf.gl_pathc; i++)
{
char *module_section_file;
char *section_name;
char *module_name;
char *module_name_end;
FILE* secfile;
unsigned long long section_address;
module_section_file = globbuf.gl_pathv[i];
section_name = strrchr (module_section_file, '/');
if (! section_name) continue;
section_name ++;
if (!strcmp (section_name, ".")) continue;
if (!strcmp (section_name, "..")) continue;
module_name = strchr (module_section_file, '/');
if (! module_name) continue;
module_name ++;
module_name = strchr (module_name, '/');
if (! module_name) continue;
module_name ++;
module_name = strchr (module_name, '/');
if (! module_name) continue;
module_name ++;
module_name_end = strchr (module_name, '/');
if (! module_name_end) continue;
secfile = fopen (module_section_file, "r");
if (! secfile) continue;
if (1 == fscanf (secfile, "0x%llx", §ion_address))
{
*module_name_end = '\0';
if (strstr (section_name, "init.") != NULL) section_address = 0;
(void) send_a_relocation (module_name, section_name, section_address);
}
if (strcmp (section_name, ".gnu.linkonce.this_module"))
fclose (secfile);
else
{
(void)set_clexec (fileno (secfile));
}
}
globfree (& globbuf);
return 0;
}
int send_relocations ()
{
int rc;
rc = send_relocation_kernel ();
if (rc == 0)
rc = send_relocation_modules ();
return rc;
}
int send_tzinfo ()
{
struct _stp_msg_tzinfo tzi;
time_t now_t;
struct tm* now;
int rc;
#if 0
#endif
time (& now_t);
now = localtime (& now_t);
tzi.tz_gmtoff = - now->tm_gmtoff;
strncpy (tzi.tz_name, now->tm_zone, STP_TZ_NAME_LEN);
rc = send_request(STP_TZINFO, & tzi, sizeof(tzi));
if (rc != 0)
perror ("Unable to send time zone information");
return rc;
}
int send_privilege_credentials (privilege_t user_credentials)
{
struct _stp_msg_privilege_credentials pc;
int rc;
pc.pc_group_mask = user_credentials;
rc = send_request(STP_PRIVILEGE_CREDENTIALS, & pc, sizeof(pc));
if (rc != 0) {
dbug (1, "Unable to send user privilege credentials\n");
}
return rc;
}
int send_remote_id ()
{
struct _stp_msg_remote_id rem;
int rc;
rem.remote_id = remote_id;
strncpy (rem.remote_uri, remote_uri, STP_REMOTE_URI_LEN);
rem.remote_uri [STP_REMOTE_URI_LEN-1]='\0'; XXX rc = send_request(STP_REMOTE_ID, & rem, sizeof(rem));
if (rc != 0)
perror ("Unable to send remote id");
return rc;
}