diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/ChangeLog | 15 | ||||
-rw-r--r-- | runtime/addr-map.c | 181 | ||||
-rw-r--r-- | runtime/loc2c-runtime.h | 230 | ||||
-rw-r--r-- | runtime/runtime.h | 1 | ||||
-rw-r--r-- | runtime/staprun/ChangeLog | 13 | ||||
-rw-r--r-- | runtime/staprun/cap.c | 166 | ||||
-rw-r--r-- | runtime/staprun/mainloop.c | 628 | ||||
-rw-r--r-- | runtime/staprun/staprun.c | 106 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 20 | ||||
-rw-r--r-- | runtime/staprun/staprun_funcs.c | 30 | ||||
-rw-r--r-- | runtime/task_finder.c | 9 |
11 files changed, 748 insertions, 651 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog index e02c5f0b..eb091d01 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,18 @@ +2008-09-08 Tim Moore <timoore@redhat.com> + + PR 1288 + * addr-map.c: New file with functions for looking up addresses + * loc2c-runtime.h (deref, store_deref): Use lookup_bad_addr to + avoid dereferencing known dangerous addresses. + * runtime.h: Include addr-map.c. + +2008-09-06 Frank Ch. Eigler <fche@elastic.org> + + PR 6445 + * task_finder.c (stap_start_task_finder): When _stp_target + is set (stap -c or -x mode), restrict initial utrace attach + iteration to target process only. + 2008-09-01 Frank Ch. Eigler <fche@elastic.org> * task_finder.c: Move CONFIG_UTRACE assertion here. diff --git a/runtime/addr-map.c b/runtime/addr-map.c new file mode 100644 index 00000000..8231b57f --- /dev/null +++ b/runtime/addr-map.c @@ -0,0 +1,181 @@ +/* -*- linux-c -*- + * Map of addresses to disallow. + * Copyright (C) 2005-2008 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _ADDR_MAP_C_ +#define _ADDR_MAP_C_ 1 + +/** @file addr-map + * @brief Implements functions used by the deref macro to blacklist + * certain addresses. + */ + +struct addr_map_entry +{ + unsigned long min; + unsigned long max; +}; + +struct addr_map +{ + size_t size; + struct addr_map_entry entries[0]; +}; + +static DEFINE_SPINLOCK(addr_map_lock); + +struct addr_map* blackmap; + +/* Find address of entry where we can insert a new one. */ +static size_t +upper_bound(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end = map->size; + struct addr_map_entry *entry = 0; + + if (end == 0) + return 0; + do + { + size_t new_idx; + if (addr < map->entries[start].min) + return start; + if (addr >= map->entries[end-1].max) + return end; + new_idx = (end + start) / 2; + entry = &map->entries[new_idx]; + if (addr < entry->min) + end = new_idx; + else + start = new_idx + 1; + } while (end != start); + return entry - &map->entries[0]; +} + +static struct addr_map_entry* +lookup_addr_aux(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end; + if (!map) + return 0; + end = map->size; + if (map->size == 0) + return 0; + + do + { + int entry_idx; + struct addr_map_entry *entry = 0; + if (addr < map->entries[start].min || addr >= map->entries[end - 1].max) + return 0; + entry_idx = (end + start) / 2; + entry = &map->entries[entry_idx]; + if (entry->min <= addr && entry->max > addr) + return entry; + if (addr < entry->min) + end = entry_idx; + else + start = entry_idx + 1; + } while (start < end); + return 0; +} + +int +lookup_bad_addr(unsigned long addr) +{ + struct addr_map_entry* result = 0; + spin_lock(&addr_map_lock); + result = lookup_addr_aux(addr, blackmap); + spin_unlock(&addr_map_lock); + if (result) + return 1; + else + return 0; +} + + +int +add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr, + struct addr_map_entry** existing_min, + struct addr_map_entry** existing_max) +{ + struct addr_map* new_map = 0; + struct addr_map* old_map = 0; + struct addr_map_entry* min_entry = 0; + struct addr_map_entry* max_entry = 0; + struct addr_map_entry* new_entry = 0; + size_t existing = 0; + + while (1) + { + size_t old_size; + spin_lock(&addr_map_lock); + old_map = blackmap; + if (!blackmap) + { + existing = 0; + old_size = 0; + } + else + { + min_entry = lookup_addr_aux(min_addr, blackmap); + max_entry = lookup_addr_aux(max_addr, blackmap); + if (min_entry || max_entry) + { + if (existing_min) + *existing_min = min_entry; + if (existing_max) + *existing_max = max_entry; + spin_unlock(&addr_map_lock); + return 1; + } + existing = upper_bound(min_addr, old_map); + old_size = old_map->size; + } + spin_unlock(&addr_map_lock); + new_map = kmalloc(sizeof(*new_map) + + sizeof(*new_entry) * (old_size + 1), + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + spin_lock(&addr_map_lock); + if (blackmap != old_map) + { + kfree(new_map); + spin_unlock(&addr_map_lock); + continue; + } + new_entry = &new_map->entries[existing]; + new_entry->min = min_addr; + new_entry->max = max_addr; + if (old_map) + { + memcpy(&new_map->entries, old_map->entries, + existing * sizeof(*new_entry)); + if (old_map->size > existing) + memcpy(new_entry + 1, &old_map->entries[existing + 1], + (old_map->size - existing) * sizeof(*new_entry)); + } + new_map->size = blackmap->size + 1; + blackmap = new_map; + spin_unlock(&addr_map_lock); + if (old_map) + kfree(old_map); + return 0; + } +} + +void +delete_bad_addr_entry(struct addr_map_entry* entry) +{ +} + +#endif diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h index 1247da51..0af19edc 100644 --- a/runtime/loc2c-runtime.h +++ b/runtime/loc2c-runtime.h @@ -201,6 +201,8 @@ #define deref(size, addr) ({ \ intptr_t _i; \ + if (lookup_bad_addr((unsigned long)addr)) \ + __deref_bad(); \ switch (size) { \ case 1: _i = kread((u8 *)(addr)); break; \ case 2: _i = kread((u16 *)(addr)); break; \ @@ -213,6 +215,8 @@ }) #define store_deref(size, addr, value) ({ \ + if (lookup_bad_addr((unsigned long)addr)) \ + __store_deref_bad(); \ switch (size) { \ case 1: kwrite((u8 *)(addr), (value)); break; \ case 2: kwrite((u16 *)(addr), (value)); break; \ @@ -234,30 +238,36 @@ extern void __store_deref_bad(void); int _bad = 0; \ u8 _b; u16 _w; u32 _l; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ - case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ - case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ - default: _v = __get_user_bad(); \ - } \ - if (_bad) \ - DEREF_FAULT(addr); \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ + case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ + case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ + default: _v = __get_user_bad(); \ + } \ + if (_bad) \ + DEREF_FAULT(addr); \ _v; \ }) #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ - case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break; \ - case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break; \ - default: __put_user_bad(); \ - } \ - if (_bad) \ - STORE_DEREF_FAULT(addr); \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break;\ + case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break;\ + case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break;\ + default: __put_user_bad(); \ + } \ + if (_bad) \ + STORE_DEREF_FAULT(addr); \ }) @@ -268,14 +278,17 @@ extern void __store_deref_bad(void); int _bad = 0; \ u8 _b; u16 _w; u32 _l; u64 _q; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ - case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ - case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ - case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \ - default: _v = __get_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ + case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ + case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ + case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \ + default: _v = __get_user_bad(); \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -284,14 +297,17 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ - case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break; \ - case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break; \ - case 8: __put_user_asm(((u64)(value)),addr,_bad,"q","","Zr",1); break; \ - default: __put_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ + case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break;\ + case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break;\ + case 8: __put_user_asm(((u64)(value)),addr,_bad,"q","","Zr",1); break; \ + default: __put_user_bad(); \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -301,13 +317,16 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v=0; \ - switch (size){ \ - case 1: __get_user_size(_v, addr, 1, _bad); break; \ - case 2: __get_user_size(_v, addr, 2, _bad); break; \ - case 4: __get_user_size(_v, addr, 4, _bad); break; \ - case 8: __get_user_size(_v, addr, 8, _bad); break; \ - default: __get_user_unknown(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __get_user_size(_v, addr, 1, _bad); break; \ + case 2: __get_user_size(_v, addr, 2, _bad); break; \ + case 4: __get_user_size(_v, addr, 4, _bad); break; \ + case 8: __get_user_size(_v, addr, 8, _bad); break; \ + default: __get_user_unknown(); break; \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -316,13 +335,16 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad=0; \ - switch (size){ \ - case 1: __put_user_size(value, addr, 1, _bad); break; \ - case 2: __put_user_size(value, addr, 2, _bad); break; \ - case 4: __put_user_size(value, addr, 4, _bad); break; \ - case 8: __put_user_size(value, addr, 8, _bad); break; \ - default: __put_user_unknown(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __put_user_size(value, addr, 1, _bad); break; \ + case 2: __put_user_size(value, addr, 2, _bad); break; \ + case 4: __put_user_size(value, addr, 4, _bad); break; \ + case 8: __put_user_size(value, addr, 8, _bad); break; \ + default: __put_user_unknown(); break; \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -373,30 +395,36 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __stp_get_user_asm(_v,addr,_bad,"lbz"); break; \ - case 2: __stp_get_user_asm(_v,addr,_bad,"lhz"); break; \ - case 4: __stp_get_user_asm(_v,addr,_bad,"lwz"); break; \ - case 8: __stp_get_user_asm(_v,addr,_bad,"ld"); break; \ - default: _v = __get_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __stp_get_user_asm(_v,addr,_bad,"lbz"); break; \ + case 2: __stp_get_user_asm(_v,addr,_bad,"lhz"); break; \ + case 4: __stp_get_user_asm(_v,addr,_bad,"lwz"); break; \ + case 8: __stp_get_user_asm(_v,addr,_bad,"ld"); break; \ + default: _v = __get_user_bad(); \ + } \ if (_bad) \ - DEREF_FAULT(addr); \ + DEREF_FAULT(addr); \ _v; \ }) #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __stp_put_user_asm(((u8)(value)),addr,_bad,"stb"); break; \ - case 2: __stp_put_user_asm(((u16)(value)),addr,_bad,"sth"); break; \ - case 4: __stp_put_user_asm(((u32)(value)),addr,_bad,"stw"); break; \ - case 8: __stp_put_user_asm(((u64)(value)),addr,_bad, "std"); break; \ - default: __put_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __stp_put_user_asm(((u8)(value)),addr,_bad,"stb"); break; \ + case 2: __stp_put_user_asm(((u16)(value)),addr,_bad,"sth"); break; \ + case 4: __stp_put_user_asm(((u32)(value)),addr,_bad,"stw"); break; \ + case 8: __stp_put_user_asm(((u64)(value)),addr,_bad, "std"); break; \ + default: __put_user_bad(); \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -541,12 +569,15 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v=0; \ - switch (size){ \ - case 1: __stp_get_user_asm_byte(_v, addr, _bad); break; \ - case 2: __stp_get_user_asm_half(_v, addr, _bad); break; \ - case 4: __stp_get_user_asm_word(_v, addr, _bad); break; \ - default: __get_user_bad(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __stp_get_user_asm_byte(_v, addr, _bad); break; \ + case 2: __stp_get_user_asm_half(_v, addr, _bad); break; \ + case 4: __stp_get_user_asm_word(_v, addr, _bad); break; \ + default: __get_user_bad(); break; \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -555,13 +586,16 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad=0; \ - switch (size){ \ - case 1: __stp_put_user_asm_byte(value, addr, _bad); break; \ - case 2: __stp_put_user_asm_half(value, addr, _bad); break; \ - case 4: __stp_put_user_asm_word(value, addr, _bad); break; \ - case 8: __stp_put_user_asm_dword(value, addr, _bad); break; \ - default: __put_user_bad(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __stp_put_user_asm_byte(value, addr, _bad); break; \ + case 2: __stp_put_user_asm_half(value, addr, _bad); break; \ + case 4: __stp_put_user_asm_word(value, addr, _bad); break; \ + case 8: __stp_put_user_asm_dword(value, addr, _bad); break; \ + default: __put_user_bad(); break; \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -624,28 +658,31 @@ extern void __store_deref_bad(void); u8 _b; u16 _w; u32 _l; u64 _q; \ int _bad = 0; \ intptr_t _v = 0; \ - switch (size) { \ - case 1: { \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) { \ + case 1: { \ __stp_get_asm(_b, addr, _bad, 1); \ _v = _b; \ break; \ - }; \ - case 2: { \ + }; \ + case 2: { \ __stp_get_asm(_w, addr, _bad, 2); \ _v = _w; \ break; \ - }; \ - case 4: { \ + }; \ + case 4: { \ __stp_get_asm(_l, addr, _bad, 4); \ _v = _l; \ break; \ - }; \ - case 8: { \ + }; \ + case 8: { \ __stp_get_asm(_q, addr, _bad, 8); \ _v = _q; \ break; \ - }; \ - default: \ + }; \ + default: \ _bad = -EFAULT; \ } \ if (_bad) \ @@ -657,12 +694,17 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ int i; \ - for(i=0;i<size;i++){ \ - __stp_put_asm((u8)(value>>((size-i-1)*8)&0xff), \ - (u64)addr+i,_bad); \ - if (_bad) \ - STORE_DEREF_FAULT(addr); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + for(i=0;i<size;i++){ \ + __stp_put_asm((u8)(value>>((size-i-1)*8)&0xff), \ + (u64)addr+i,_bad); \ + if (_bad) \ + break; \ + } \ + if (_bad) \ + STORE_DEREF_FAULT(addr); \ }) diff --git a/runtime/runtime.h b/runtime/runtime.h index 2711f531..fd0cac9e 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -88,6 +88,7 @@ static struct #ifdef STP_PERFMON #include "perf.c" #endif +#include "addr-map.c" /* Support functions for int64_t module parameters. */ int param_set_int64_t(const char *val, struct kernel_param *kp) diff --git a/runtime/staprun/ChangeLog b/runtime/staprun/ChangeLog index a4f47880..21e02e47 100644 --- a/runtime/staprun/ChangeLog +++ b/runtime/staprun/ChangeLog @@ -1,3 +1,16 @@ +2008-09-06 Frank Ch. Eigler <fche@elastic.org> + + * mainloop.c (start_cmd): Rewrite to use wordexp/execvp/ptrace. + (stp_main_loop): Use ptrace detach to resume target process. + +2008-09-05 Frank Ch. Eigler <fche@elastic.org> + + * staprun.c (run_as): Teach it to exec too. Update callers. + Always do set[ug]id as dictated. + * staprun.h (do_cap): Remove. Update all callers. + * staprun_funcs.c: Ditto. + * cap.c: Removed. Update headers. + 2008-07-10 Frank Ch. Eigler <fche@elastic.org> PR 6736. diff --git a/runtime/staprun/cap.c b/runtime/staprun/cap.c deleted file mode 100644 index 6ac6701f..00000000 --- a/runtime/staprun/cap.c +++ /dev/null @@ -1,166 +0,0 @@ -/* -*- linux-c -*- - * - * cap.c - staprun capabilities functions - * - * 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 2 of the License, 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) 2007 Red Hat, Inc. - * - */ - -#include "staprun.h" -#include <sys/prctl.h> - -static int _stp_no_caps = 0; - -/* like perror, but exits */ -#define ferror(msg) { \ - _perr(msg); \ - exit(1); \ - } \ - -/* - * init_cap() sets up the initial capabilities for staprun. Then - * it calls prctl( PR_SET_KEEPCAPS) to arrrange to keep these capabilities - * even when not running as root. Next it resets the real, effective, and - * saved uid and gid back to the normal user. - * - * There are two sets of capabilities we are concerned with; permitted - * and effective. The permitted capabilities are all the capabilities - * that this process is ever permitted to have. They are defined in init_cap() - * and may be permanently removed with drop_cap(). - * - * Effective capabilities are the capabilities from the permitted set - * that are currently enabled. A good practice would be to only enable - * capabilities when necessary and to delete or drop them as soon as possible. - * - * Capabilities we might use include: - * - * CAP_SYS_MODULE - insert and remove kernel modules - * CAP_SYS_ADMIN - misc, including mounting and unmounting - * CAP_SYS_NICE - setpriority() - * CAP_SETUID - allows setuid - * CAP_SETGID - allows setgid - * CAP_CHOWN - allows chown - */ - -void init_cap(void) -{ - cap_t caps = cap_init(); - cap_value_t capv[] = { CAP_SYS_MODULE, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SETUID, CAP_SETGID, CAP_DAC_OVERRIDE }; - const int numcaps = sizeof(capv) / sizeof(capv[0]); - uid_t uid = getuid(); - gid_t gid = getgid(); - - cap_clear(caps); - if (caps == NULL) - ferror("cap_init"); - - if (cap_set_flag(caps, CAP_PERMITTED, numcaps, capv, CAP_SET) < 0) - ferror("cap_set_flag"); - - if (cap_set_proc(caps) < 0) { - dbug(1, "Setting capabilities failed. Capabilities disabled.\n"); - _stp_no_caps = 1; - return; - } - - cap_free(caps); - - if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) - ferror("prctl"); - - if (setresuid(uid, uid, uid) < 0) - ferror("setresuid"); - - if (setresgid(gid, gid, gid) < 0) - ferror("setresgid"); -} - -void print_cap(char *text) -{ - int p; - cap_t caps = cap_get_proc(); - uid_t uid, euid, suid; - gid_t gid, egid, sgid; - - if (caps == NULL) { - perr("cap_get_proc"); - return; - } - - getresuid(&uid, &euid, &suid); - getresgid(&gid, &egid, &sgid); - - printf("***** %s\n", text); - - if ((p = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0)) < 0) - perr("Couldn't get PR_SET_KEEPCAPS flag value"); - else - printf("KEEPCAPS: %d\n", p); - - printf("uid: %d, euid: %d, suid: %d\ngid: %d. egid: %d, sgid: %d\n", uid, euid, suid, gid, egid, sgid); - printf("Caps: %s\n", cap_to_text(caps, NULL)); - cap_free(caps); - printf("*****\n\n"); -} - -/* drop_cap() permanently removes a capability from the permitted set. There is - * no way to recover the capability after this. You do not need to remove - * it from the effective set before calling this. - */ -void drop_cap(cap_value_t cap) -{ - if (_stp_no_caps == 0) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); - } -} - -/* add_cap() adds a permitted capability to the effective set. */ -void add_cap(cap_value_t cap) -{ - if (_stp_no_caps == 0) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) - ferror("Could not set effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); - } -} - -/* del_cap() deletes a permitted capability from the effective set. */ -void del_cap(cap_value_t cap) -{ - if (_stp_no_caps == 0) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); - } -} diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index a7b919cb..6fc061ae 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -12,6 +12,9 @@ #include "staprun.h" #include <sys/utsname.h> +#include <sys/ptrace.h> +#include <wordexp.h> + /* globals */ int ncpus; @@ -22,172 +25,192 @@ static int use_old_transport = 0; static void *signal_thread(void *arg) { - sigset_t *s = (sigset_t *) arg; - int signum, rc, btype = STP_EXIT; - - while (1) { - if (sigwait(s, &signum) < 0) { - _perr("sigwait"); - continue; - } - dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); - if (signum == SIGQUIT) - cleanup_and_exit(1); - else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { - // send STP_EXIT - rc = write(control_channel, &btype, sizeof(btype)); - break; - } - } - return NULL; + sigset_t *s = (sigset_t *) arg; + int signum, rc, btype = STP_EXIT; + + while (1) { + if (sigwait(s, &signum) < 0) { + _perr("sigwait"); + continue; + } + dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); + if (signum == SIGQUIT) + cleanup_and_exit(1); + else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); + break; + } + } + return NULL; } static void chld_proc(int signum) { - int32_t rc, btype = STP_EXIT; - dbug(2, "chld_proc %d (%s)\n", signum, strsignal(signum)); - pid_t pid = waitpid(-1, NULL, WNOHANG); - if (pid != target_pid) - return; - // send STP_EXIT - rc = write(control_channel, &btype, sizeof(btype)); + int32_t rc, btype = STP_EXIT; + dbug(2, "chld_proc %d (%s)\n", signum, strsignal(signum)); + pid_t pid = waitpid(-1, NULL, WNOHANG); + if (pid != target_pid) + return; + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); } static void setup_main_signals(void) { - pthread_t tid; - struct sigaction sa; - sigset_t *s = malloc(sizeof(*s)); - if (!s) { - _perr("malloc failed"); - exit(1); - } - sigfillset(s); - pthread_sigmask(SIG_SETMASK, s, NULL); - memset(&sa, 0, sizeof(sa)); - sigfillset(&sa.sa_mask); - sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - - sa.sa_handler = chld_proc; - sigaction(SIGCHLD, &sa, NULL); - - sigemptyset(s); - sigaddset(s, SIGINT); - sigaddset(s, SIGTERM); - sigaddset(s, SIGHUP); - sigaddset(s, SIGQUIT); - pthread_sigmask(SIG_SETMASK, s, NULL); - if (pthread_create(&tid, NULL, signal_thread, s) < 0) { - _perr("failed to create thread"); - exit(1); - } + pthread_t tid; + struct sigaction sa; + sigset_t *s = malloc(sizeof(*s)); + if (!s) { + _perr("malloc failed"); + exit(1); + } + sigfillset(s); + pthread_sigmask(SIG_SETMASK, s, NULL); + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + + sa.sa_handler = chld_proc; + sigaction(SIGCHLD, &sa, NULL); + + sigemptyset(s); + sigaddset(s, SIGINT); + sigaddset(s, SIGTERM); + sigaddset(s, SIGHUP); + sigaddset(s, SIGQUIT); + pthread_sigmask(SIG_SETMASK, s, NULL); + if (pthread_create(&tid, NULL, signal_thread, s) < 0) { + _perr("failed to create thread"); + exit(1); + } } -/* - * start_cmd forks the command given on the command line - * with the "-c" option. It will not exec that command - * until it received signal SIGUSR1. We do it this way because - * we must have the pid of the forked command so it can be set to - * the module and made available internally as _stp_target. - * SIGUSR1 is sent from stp_main_loop() below when it receives - * STP_START from the module. +/* + * start_cmd forks the command given on the command line with the "-c" + * option. It will wait just at the cusp of the exec until we get the + * signal from the kernel to let it run. We do it this way because we + * must have the pid of the forked command so it can be set to the + * module and made available internally as _stp_target. PTRACE_DETACH + * is sent from stp_main_loop() below when it receives STP_START from + * the module. */ void start_cmd(void) { - pid_t pid; - sigset_t usrset; - struct sigaction a; - - sigemptyset(&usrset); - sigaddset(&usrset, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &usrset, NULL); - - /* if we are execing a target cmd, ignore ^C in stapio */ - /* and let the target cmd get it. */ - sigemptyset(&a.sa_mask); - a.sa_flags = 0; - a.sa_handler = SIG_IGN; - sigaction(SIGINT, &a, NULL); - - dbug(1, "execing target_cmd %s\n", target_cmd); - if ((pid = fork()) < 0) { - _perr("fork"); - exit(1); - } else if (pid == 0) { - int signum; - - a.sa_handler = SIG_DFL; - sigaction(SIGINT, &a, NULL); - - /* commands we fork need to run at normal priority */ - setpriority(PRIO_PROCESS, 0, 0); - - /* wait here until signaled */ - sigwait(&usrset, &signum); - - if (execl("/bin/sh", "sh", "-c", target_cmd, NULL) < 0) - perror(target_cmd); - _exit(1); - } - target_pid = pid; + pid_t pid; + struct sigaction a; + + /* if we are execing a target cmd, ignore ^C in stapio */ + /* and let the target cmd get it. */ + sigemptyset(&a.sa_mask); + a.sa_flags = 0; + a.sa_handler = SIG_IGN; + sigaction(SIGINT, &a, NULL); + + if ((pid = fork()) < 0) { + _perr("fork"); + exit(1); + } else if (pid == 0) { + /* We're in the target process. Let's start the execve of target_cmd, */ + int rc; + wordexp_t words; + + a.sa_handler = SIG_DFL; + sigaction(SIGINT, &a, NULL); + + /* Formerly, we just execl'd(sh,-c,$target_cmd). But this does't + work well if target_cmd is a shell builtin. We really want to + probe a new child process, not a mishmash of shell-interpreted + stuff. */ + rc = wordexp (target_cmd, & words, WRDE_NOCMD); + if (rc != 0) { _perr ("wordexp parsing error"); _exit (1); } + if (words.we_wordc < 1) { _perr ("empty target_cmd"); _exit (1); } + + rc = ptrace (PTRACE_TRACEME, 0, 0, 0); + if (rc < 0) perror ("ptrace me"); + +#if 0 + dbug(1, "blocking briefly\n"); + raise (SIGCONT); /* Harmless; just passes control to parent. */ +#endif + + dbug(1, "execing target_cmd %s\n", target_cmd); + + /* Note that execvp() is not a direct system call; it does a $PATH + search in glibc. We would like to filter out these dummy syscalls + from the utrace events seen by scripts. */ + if (execvp (words.we_wordv[0], words.we_wordv) < 0) + perror(target_cmd); + + /* (There is no need to wordfree() words; they are or will be gone.) */ + + _exit(1); + } else { + /* We're in the parent. The child will parse target_cmd and execv() + the result. It will be stopped thereabouts and send us a SIGTRAP. */ + target_pid = pid; + int status; + waitpid (target_pid, &status, 0); + dbug(1, "waited for target_cmd %s pid %d status %x\n", target_cmd, target_pid, (unsigned) status); + } } -/** +/** * system_cmd() executes system commands in response * to an STP_SYSTEM message from the module. These * messages are sent by the system() systemtap function. */ void system_cmd(char *cmd) { - pid_t pid; - - dbug(2, "system %s\n", cmd); - if ((pid = fork()) < 0) { - _perr("fork"); - } else if (pid == 0) { - setpriority(PRIO_PROCESS, 0, 0); - if (execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) - perr("%s", cmd); - _exit(1); - } + pid_t pid; + + dbug(2, "system %s\n", cmd); + if ((pid = fork()) < 0) { + _perr("fork"); + } else if (pid == 0) { + setpriority(PRIO_PROCESS, 0, 0); + if (execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) + perr("%s", cmd); + _exit(1); + } } /* This is only used in the old relayfs code */ static void read_buffer_info(void) { - char buf[PATH_MAX]; - struct statfs st; - int fd, len, ret; - - if (!use_old_transport) - return; - - if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) - return; - - if (sprintf_chk(buf, "/proc/systemtap/%s/bufsize", modname)) - return; - fd = open(buf, O_RDONLY); - if (fd < 0) - return; - - len = read(fd, buf, sizeof(buf)); - if (len <= 0) { - perr("Couldn't read bufsize"); - close(fd); - return; - } - ret = sscanf(buf, "%u,%u", &n_subbufs, &subbuf_size); - if (ret != 2) - perr("Couldn't read bufsize"); - - dbug(2, "n_subbufs= %u, size=%u\n", n_subbufs, subbuf_size); - close(fd); - return; + char buf[PATH_MAX]; + struct statfs st; + int fd, len, ret; + + if (!use_old_transport) + return; + + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) + return; + + if (sprintf_chk(buf, "/proc/systemtap/%s/bufsize", modname)) + return; + fd = open(buf, O_RDONLY); + if (fd < 0) + return; + + len = read(fd, buf, sizeof(buf)); + if (len <= 0) { + perr("Couldn't read bufsize"); + close(fd); + return; + } + ret = sscanf(buf, "%u,%u", &n_subbufs, &subbuf_size); + if (ret != 2) + perr("Couldn't read bufsize"); + + dbug(2, "n_subbufs= %u, size=%u\n", n_subbufs, subbuf_size); + close(fd); + return; } /** @@ -198,79 +221,79 @@ static void read_buffer_info(void) */ int init_stapio(void) { - dbug(2, "init_stapio\n"); - - /* create control channel */ - use_old_transport = init_ctl_channel(modname, 1); - if (use_old_transport < 0) { - err("Failed to initialize control channel.\n"); - return -1; - } - read_buffer_info(); - - if (attach_mod) { - dbug(2, "Attaching\n"); - if (use_old_transport) { - if (init_oldrelayfs() < 0) { - close_ctl_channel(); - return -1; - } - } else { - if (init_relayfs() < 0) { - close_ctl_channel(); - return -1; - } - } - return 0; - } - - /* fork target_cmd if requested. */ - /* It will not actually exec until signalled. */ - if (target_cmd) - start_cmd(); - - return 0; + dbug(2, "init_stapio\n"); + + /* create control channel */ + use_old_transport = init_ctl_channel(modname, 1); + if (use_old_transport < 0) { + err("Failed to initialize control channel.\n"); + return -1; + } + read_buffer_info(); + + if (attach_mod) { + dbug(2, "Attaching\n"); + if (use_old_transport) { + if (init_oldrelayfs() < 0) { + close_ctl_channel(); + return -1; + } + } else { + if (init_relayfs() < 0) { + close_ctl_channel(); + return -1; + } + } + return 0; + } + + /* fork target_cmd if requested. */ + /* It will not actually exec until signalled. */ + if (target_cmd) + start_cmd(); + + return 0; } /* cleanup_and_exit() closed channels, frees memory, * removes the module (if necessary) and exits. */ void cleanup_and_exit(int detach) { - pid_t err; - static int exiting = 0; - - if (exiting) - return; - exiting = 1; - - setup_main_signals(); - - dbug(1, "detach=%d\n", detach); - - /* what about child processes? we will wait for them here. */ - err = waitpid(-1, NULL, WNOHANG); - if (err >= 0) - err("\nWaiting for processes to exit\n"); - while (wait(NULL) > 0) ; - - if (use_old_transport) - close_oldrelayfs(detach); - else - close_relayfs(); - - dbug(1, "closing control channel\n"); - close_ctl_channel(); - - if (detach) { - err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); - } else { - dbug(2, "removing %s\n", modname); - if (execl(BINDIR "/staprun", "staprun", "-d", modname, NULL) < 0) { - perror(modname); - _exit(1); - } - } - _exit(0); + pid_t err; + static int exiting = 0; + + if (exiting) + return; + exiting = 1; + + setup_main_signals(); + + dbug(1, "detach=%d\n", detach); + + /* what about child processes? we will wait for them here. */ + err = waitpid(-1, NULL, WNOHANG); + if (err >= 0) + err("\nWaiting for processes to exit\n"); + while (wait(NULL) > 0) ; + + if (use_old_transport) + close_oldrelayfs(detach); + else + close_relayfs(); + + dbug(1, "closing control channel\n"); + close_ctl_channel(); + + if (detach) { + err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); + } else { + dbug(2, "removing %s\n", modname); + if (execl(BINDIR "/staprun", "staprun", "-d", modname, NULL) < 0) { + perror(modname); + _exit(1); + } + } + _exit(0); } /** @@ -279,97 +302,106 @@ void cleanup_and_exit(int detach) int stp_main_loop(void) { - ssize_t nb; - void *data; - uint32_t type; - FILE *ofp = stdout; - char recvbuf[8196]; - - setvbuf(ofp, (char *)NULL, _IOLBF, 0); - setup_main_signals(); - dbug(2, "in main loop\n"); - - send_request(STP_READY, NULL, 0); - - /* handle messages from control channel */ - while (1) { - nb = read(control_channel, recvbuf, sizeof(recvbuf)); - dbug(2, "nb=%d\n", (int)nb); - if (nb <= 0) { - if (errno != EINTR) - _perr("Unexpected EOF in read (nb=%ld)", (long)nb); - continue; - } - - type = *(uint32_t *) recvbuf; - data = (void *)(recvbuf + sizeof(uint32_t)); - nb -= sizeof(uint32_t); - - switch (type) { + ssize_t nb; + void *data; + uint32_t type; + FILE *ofp = stdout; + char recvbuf[8196]; + + setvbuf(ofp, (char *)NULL, _IOLBF, 0); + setup_main_signals(); + dbug(2, "in main loop\n"); + + send_request(STP_READY, NULL, 0); + + /* handle messages from control channel */ + while (1) { + nb = read(control_channel, recvbuf, sizeof(recvbuf)); + dbug(2, "nb=%d\n", (int)nb); + if (nb <= 0) { + if (errno != EINTR) + _perr("Unexpected EOF in read (nb=%ld)", (long)nb); + continue; + } + + type = *(uint32_t *) recvbuf; + data = (void *)(recvbuf + sizeof(uint32_t)); + nb -= sizeof(uint32_t); + + switch (type) { #ifdef STP_OLD_TRANSPORT - case STP_REALTIME_DATA: - { - ssize_t bw = write(out_fd[0], data, nb); - if (bw >= 0 && bw != nb) { - nb = nb - bw; - bw = write(out_fd[0], data, nb); - } - if (bw != nb) { - _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(0); - } - break; - } + case STP_REALTIME_DATA: + { + ssize_t bw = write(out_fd[0], data, nb); + if (bw >= 0 && bw != nb) { + nb = nb - bw; + bw = write(out_fd[0], data, nb); + } + if (bw != nb) { + _perr("write error (nb=%ld)", (long)nb); + cleanup_and_exit(0); + } + break; + } #endif - case STP_OOB_DATA: - fputs((char *)data, stderr); - break; - case STP_EXIT: - { - /* module asks us to unload it and exit */ - dbug(2, "got STP_EXIT\n"); - cleanup_and_exit(0); - break; - } - case STP_START: - { - struct _stp_msg_start *t = (struct _stp_msg_start *)data; - dbug(2, "probe_start() returned %d\n", t->res); - if (t->res < 0) { - if (target_cmd) - kill(target_pid, SIGKILL); - cleanup_and_exit(0); - } else if (target_cmd) - kill(target_pid, SIGUSR1); - break; - } - case STP_SYSTEM: - { - struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; - dbug(2, "STP_SYSTEM: %s\n", c->cmd); - system_cmd(c->cmd); - break; - } - case STP_TRANSPORT: - { - struct _stp_msg_start ts; - if (use_old_transport) { - if (init_oldrelayfs() < 0) - cleanup_and_exit(0); - } else { - if (init_relayfs() < 0) - cleanup_and_exit(0); - } - ts.target = target_pid; - send_request(STP_START, &ts, sizeof(ts)); - if (load_only) - cleanup_and_exit(1); - break; - } - default: - err("WARNING: ignored message of type %d\n", (type)); - } - } - fclose(ofp); - return 0; + case STP_OOB_DATA: + fputs((char *)data, stderr); + break; + case STP_EXIT: + { + /* module asks us to unload it and exit */ + dbug(2, "got STP_EXIT\n"); + cleanup_and_exit(0); + break; + } + case STP_START: + { + struct _stp_msg_start *t = (struct _stp_msg_start *)data; + dbug(2, "probe_start() returned %d\n", t->res); + if (t->res < 0) { + if (target_cmd) + kill(target_pid, SIGKILL); + cleanup_and_exit(0); + } else if (target_cmd) { + dbug(1, "detaching pid %d\n", target_pid); + int rc = ptrace (PTRACE_DETACH, target_pid, 0, 0); + if (rc < 0) + { + perror ("ptrace detach"); + if (target_cmd) + kill(target_pid, SIGKILL); + cleanup_and_exit(0); + } + } + break; + } + case STP_SYSTEM: + { + struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; + dbug(2, "STP_SYSTEM: %s\n", c->cmd); + system_cmd(c->cmd); + break; + } + case STP_TRANSPORT: + { + struct _stp_msg_start ts; + if (use_old_transport) { + if (init_oldrelayfs() < 0) + cleanup_and_exit(0); + } else { + if (init_relayfs() < 0) + cleanup_and_exit(0); + } + ts.target = target_pid; + send_request(STP_START, &ts, sizeof(ts)); + if (load_only) + cleanup_and_exit(1); + break; + } + default: + err("WARNING: ignored message of type %d\n", (type)); + } + } + fclose(ofp); + return 0; } diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index 664b75ee..f8a08876 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * staprun.c - SystemTap module loader + * staprun.c - SystemTap module loader * * 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 @@ -34,14 +34,14 @@ extern long delete_module(const char *, unsigned int); int send_relocations (); -static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) +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; - err("execing: "); + err(exec_p ? "execing: ": "spawning: "); while (argv[i]) { err("%s ", argv[i]); i++; @@ -49,36 +49,43 @@ static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) err("\n"); } - if ((pid = fork()) < 0) { - _perr("fork"); - return -1; - } else if (pid == 0) { - /* Make sure we run as the full user. If we're - * switching to a non-root user, this won't allow - * that process to switch back to root (since the - * original process is setuid). */ - if (uid != getuid()) { - if (do_cap(CAP_SETGID, setresgid, gid, gid, gid) < 0) { - _perr("setresgid"); - exit(1); - } - if (do_cap(CAP_SETUID, setresuid, uid, uid, uid) < 0) { - _perr("setresuid"); - exit(1); - } - } + if (exec_p) + pid = 0; + else + pid = fork(); + + if (pid < 0) + { + _perr("fork"); + return -1; + } + + if (pid == 0) /* child process, or exec_p */ + { + /* Make sure we run as the full user. If we're + * switching to a non-root user, this won't allow + * that process to switch back to root (since the + * original process is setuid). */ + if (setresgid (gid, gid, gid) < 0) { + _perr("setresgid"); + exit(1); + } + if (setresuid (uid, uid, uid) < 0) { + _perr("setresuid"); + exit(1); + } - /* Actually run the command. */ - if (execv(path, argv) < 0) - perror(path); - _exit(1); - } + /* Actually run the command. */ + if (execv(path, argv) < 0) + perror(path); + _exit(1); + } if (waitpid(pid, &rstatus, 0) < 0) - return -1; + return -1; if (WIFEXITED(rstatus)) - return WEXITSTATUS(rstatus); + return WEXITSTATUS(rstatus); return -1; } @@ -104,14 +111,13 @@ static int enable_uprobes(void) argv[i++] = "unregister_uprobe"; argv[i++] = "/proc/kallsyms"; argv[i] = NULL; - if (run_as(uid, gid, argv[0], argv) == 0) + if (run_as(0, uid, gid, argv[0], argv) == 0) return 0; /* * TODO: If user can't setresuid to root here, staprun will exit. * Is there a situation where that would fail but the subsequent - * attempt to use CAP_SYS_MODULE privileges (in insert_module()) - * would succeed? + * attempt to insert_module() would succeed? */ dbug(2, "Inserting uprobes module from /lib/modules, if any.\n"); i = 0; @@ -119,7 +125,7 @@ static int enable_uprobes(void) argv[i++] = "-q"; argv[i++] = "uprobes"; argv[i] = NULL; - if (run_as(0, 0, argv[0], argv) == 0) + if (run_as(0, 0, 0, argv[0], argv) == 0) return 0; dbug(2, "Inserting uprobes module from SystemTap runtime.\n"); @@ -169,9 +175,9 @@ static int remove_module(const char *name, int verb) } /* Call init_ctl_channel() which actually attempts an open() - * of the control channel. This is better than using access() because + * of the control channel. This is better than using access() because * an open on an already open channel will fail, preventing us from attempting - * to remove an in-use module. + * to remove an in-use module. */ if (init_ctl_channel(name, 0) < 0) { if (verb) @@ -186,7 +192,7 @@ static int remove_module(const char *name, int verb) if (setpriority(PRIO_PROCESS, 0, 0) < 0) _perr("setpriority"); - ret = do_cap(CAP_SYS_MODULE, delete_module, name, 0); + ret = delete_module (name, 0); if (ret != 0) { err("Error removing module '%s': %s.\n", name, strerror(errno)); return 1; @@ -203,9 +209,6 @@ int init_staprun(void) if (mountfs() < 0) return -1; - /* We're done with CAP_SYS_ADMIN. */ - drop_cap(CAP_SYS_ADMIN); - if (delete_mod) exit(remove_module(modname, 1)); else if (!attach_mod) { @@ -269,25 +272,14 @@ int main(int argc, char **argv) exit(1); } - init_cap(); - if (check_permissions() != 1) usage(argv[0]); - /* now bump the priority */ - rc = do_cap(CAP_SYS_NICE, setpriority, PRIO_PROCESS, 0, -10); - /* failure is not fatal in this case */ - if (rc < 0) - _perr("setpriority"); - - /* We're done with CAP_SYS_NICE. */ - drop_cap(CAP_SYS_NICE); - if (init_staprun()) exit(1); argv[0] = PKGLIBDIR "/stapio"; - if (execv(argv[0], argv) < 0) { + if (run_as (1, getuid(), getgid(), argv[0], argv) < 0) { perror(argv[0]); goto err; } @@ -337,7 +329,7 @@ int send_relocation_kernel () FILE* kallsyms = fopen ("/proc/kallsyms", "r"); if (kallsyms == NULL) { - perror("cannot open /proc/kallsyms"); + perror("cannot open /proc/kallsyms"); // ... and the kernel module will almost certainly fail to initialize. } else @@ -404,18 +396,18 @@ void send_relocation_modules () module_section_file = globbuf.gl_pathv[i]; - /* Tokenize the file name. + /* Tokenize the file name. Sample gl_pathv[]: /sys/modules/zlib_deflate/sections/.text Pieces: ^^^^^^^^^^^^ ^^^^^ */ - section_name = rindex (module_section_file, '/'); + section_name = rindex (module_section_file, '/'); if (! section_name) continue; section_name ++; if (!strcmp (section_name, ".")) continue; if (!strcmp (section_name, "..")) continue; - - module_name = index (module_section_file, '/'); + + module_name = index (module_section_file, '/'); if (! module_name) continue; module_name ++; module_name = index (module_name, '/'); @@ -436,7 +428,7 @@ void send_relocation_modules () /* Now we destructively modify the string, but by now the file is open so we won't need the full name again. */ *module_name_end = '\0'; - + send_a_relocation (module_name, section_name, section_address); } @@ -454,7 +446,7 @@ void send_relocation_modules () same time that a probeworthy module is being unloaded. */ } } - + globfree (& globbuf); } diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 0a35fee6..2014ce5b 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -33,7 +33,6 @@ #include <sys/wait.h> #include <sys/statfs.h> #include <linux/version.h> -#include <sys/capability.h> #define dbug(level, args...) {if (verbose>=level) {fprintf(stderr,"%s:%s:%d ",__name__,__FUNCTION__, __LINE__); fprintf(stderr,args);}} @@ -58,17 +57,6 @@ extern char *__name__; fprintf(stderr, ": %s\n", strerror(_errno)); \ } while (0) #define overflow_error() _err("Internal buffer overflow. Please file a bug report.\n") - -#define do_cap(cap,func,args...) ({ \ - int _rc, _saved_errno; \ - add_cap(cap); \ - _rc = func(args); \ - _saved_errno = errno; \ - del_cap(cap); \ - errno = _saved_errno; \ - _rc; \ - }) \ - /* Error checking version of sprintf() - returns 1 if overflow error */ #define sprintf_chk(str, args...) ({ \ @@ -123,12 +111,6 @@ void close_relayfs(void); int init_oldrelayfs(void); void close_oldrelayfs(int); void setup_signals(void); -/* cap.c */ -void print_cap(char *text); -void init_cap(void); -void add_cap(cap_value_t cap); -void del_cap(cap_value_t cap); -void drop_cap(cap_value_t cap); /* staprun_funcs.c */ void setup_staprun_signals(void); const char *moderror(int err); @@ -147,7 +129,7 @@ void setup_signals(void); int set_clexec(int fd); /* - * variables + * variables */ extern int control_channel; extern int ncpus; diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index c1cb92b7..8fa95e45 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -43,7 +43,7 @@ int insert_module(const char *path, const char *special_options, char **options) char *opts; int fd, saved_errno; struct stat sbuf; - + dbug(2, "inserting module\n"); if (special_options) @@ -71,7 +71,7 @@ int insert_module(const char *path, const char *special_options, char **options) perr("Error opening '%s'", path); return -1; } - + /* Now that the file is open, figure out how big it is. */ if (fstat(fd, &sbuf) < 0) { _perr("Error stat'ing '%s'", path); @@ -87,9 +87,9 @@ int insert_module(const char *path, const char *special_options, char **options) free(opts); return -1; } - + /* Actually insert the module */ - ret = do_cap(CAP_SYS_MODULE, init_module, file, sbuf.st_size, opts); + ret = init_module(file, sbuf.st_size, opts); saved_errno = errno; /* Cleanup. */ @@ -99,7 +99,7 @@ int insert_module(const char *path, const char *special_options, char **options) if (ret != 0) { err("Error inserting module '%s': %s\n", path, moderror(saved_errno)); - return -1; + return -1; } return 0; } @@ -120,8 +120,7 @@ int mountfs(void) rc = stat(DEBUGFSDIR, &sb); if (rc == 0 && S_ISDIR(sb.st_mode)) { /* If we can mount the debugfs dir correctly, we're done. */ - rc = do_cap(CAP_SYS_ADMIN, mount, "debugfs", DEBUGFSDIR, - "debugfs", 0, NULL); + rc = mount ("debugfs", DEBUGFSDIR, "debugfs", 0, NULL); if (rc == 0) return 0; /* If we got ENODEV, that means that debugfs isn't @@ -132,7 +131,7 @@ int mountfs(void) return -1; } } - + /* DEBUGFSDIR couldn't be mounted. So, try RELAYFSDIR. */ /* If the relayfs dir is already mounted correctly, we're done. */ @@ -159,11 +158,12 @@ int mountfs(void) /* To ensure the directory gets created with the * proper group, we'll have to temporarily switch to * root. */ - if (do_cap(CAP_SETUID, setuid, 0) < 0) { + /* XXX: Why not just chown() the thing? */ + if (setuid (0) < 0) { _perr("Couldn't change user while creating %s", RELAYFSDIR); return -1; } - if (do_cap(CAP_SETGID, setgid, 0) < 0) { + if (setgid (0) < 0) { _perr("Couldn't change group while creating %s", RELAYFSDIR); return -1; } @@ -174,11 +174,11 @@ int mountfs(void) saved_errno = errno; /* Restore everything we changed. */ - if (do_cap(CAP_SETGID, setgid, gid) < 0) { + if (setgid (gid) < 0) { _perr("Couldn't restore group while creating %s", RELAYFSDIR); return -1; } - if (do_cap(CAP_SETUID, setuid, uid) < 0) { + if (setuid (uid) < 0) { _perr("Couldn't restore user while creating %s", RELAYFSDIR); return -1; } @@ -192,7 +192,7 @@ int mountfs(void) } /* Now that we're sure the directory exists, try mounting RELAYFSDIR. */ - if (do_cap(CAP_SYS_ADMIN, mount, "relayfs", RELAYFSDIR, "relayfs", 0, NULL) < 0) { + if (mount ("relayfs", RELAYFSDIR, "relayfs", 0, NULL) < 0) { perr("Couldn't mount %s", RELAYFSDIR); return -1; } @@ -262,13 +262,13 @@ check_path(void) " Unable to canonicalize that directory", staplib_dir_path); return -1; } - + /* Use realpath() to canonicalize the module path. */ if (realpath(modpath, module_realpath) == NULL) { perr("Unable to canonicalize path \"%s\"", modpath); return -1; } - + /* To make sure the user can't specify something like * /lib/modules/`uname -r`/systemtapmod.ko, put a '/' on the * end of staplib_dir_realpath. */ diff --git a/runtime/task_finder.c b/runtime/task_finder.c index 9c0dd55b..2d4eed15 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -360,7 +360,7 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen) | UTRACE_EVENT(EXEC) \ | UTRACE_EVENT(DEATH)) -/* +/* * __STP_TASK_BASE_EVENTS: base events for stap_task_finder_target's * without a vm_callback * @@ -460,7 +460,7 @@ __stp_utrace_attach_match_filename(struct task_struct *tsk, else if (tgt->pid != 0) continue; /* Notice that "pid == 0" (which means to probe all - * threads) falls through. */ + * threads) falls through. */ list_for_each(cb_node, &tgt->callback_list_head) { struct stap_task_finder_target *cb_tgt; @@ -1138,6 +1138,11 @@ stap_start_task_finder(void) size_t mmpathlen; struct list_head *tgt_node; + /* Skip over processes other than that specified with + stap -c or -x. */ + if (_stp_target && tsk->tgid != _stp_target) + continue; + rc = stap_utrace_attach(tsk, &__stp_utrace_task_finder_ops, 0, __STP_TASK_FINDER_EVENTS); if (rc == EPERM) { |