Technical Blog Post
Abstract
PROBEVUE: SIGACTION() [WHO CHANGED MY SIGNAL HANDLER?]
Body
This small script will track calls to 'sigaction()' for a given signal
and executable. This might become useful when a program does not react
as expected when receiving a signal. This might be because somewhere
along the way, something might have reset the signal disposition using
a call to 'sigaction()'.
Since such call can be also performed from an external API loaded
via a dynamic library, this script will catch as well the 'dlopen()'
and 'dlclose()'. Also it will print the address at which the 'text' and
'data' sections for the loaded object are located in the process memory.
This will help put function names in front of the stack traces dumped
by the script.
The script should be executed like this as root:
probevue -t 10 -e 75 -s 64 -o sigaction2.out sigaction2.pb
The output will look like this:
0x0002edd1d5a6620f [ ExecCCommand - 28966926 - 17236451 - 17957030 ]execve( [db2rocm] [1] [DB2] [db2sbx] [3] [MONITOR] )
0x0002edd1d615e61f [ db2rocm - 28966926 - 17236451 - 17957030 ] kload(/usr/lib/nls/loc//en_US__64) [t 0x090000000046e000 - d 0x0900000000473200]
0x0002edd1d6173b00 [ db2rocm - 28966926 - 17236451 - 17957030 ] kload(libi18n.a(shr_64.o)) [t 0x0900000000477980 - d 0x09001000a3aca000]
0x0002edd1d655ee03 [ db2rocm - 28966926 - 17236451 - 17957030 ] sigaction(15, 0x0000000110082b38) = 0
0x1000002f8
0x100003120
0x1000009d0
0x10000711c
0x900000004ad4660
0x900000004ad3228
Note that the signal number tracked here is 15 and we track any executable
named 'db2rocm'. This can be changed in the script filters below to fit your needs.
The script is like this:
/*
* sigaction2.pb: Track signal settings for a given program. This will track
* calls to 'sigaction()' for a given signal and executable.
* Also, it will dump a stack (address only) for each call that
* is caught. To reconstruct the stack trace with symbols it
* will not only catch kload/kunload calls but provide as well
* the address at which TEXT and DATA sections are loaded for
* the object loaded by dlopen().
*
* Run as user 'root' using the following command line:
*
* probevue -t 10 -e 75 -s 64 -o sigaction2.out sigaction2.pb
*
* To modify the number of threads that can be traced:
*
* probevctrl -c "num_threads_traced=2048"
*
* To modify the per cpu buffer size:
*
* probevctrl -c "default_buffer_size=64"
*
* To modify the maximum amount of memory used by probevue:
*
* probevctrl -c "max_total_mem_size=256"
*
* To modify the default read rate
*
* probevctrl -c "default_read_rate=10"
*
* dalla
*/
/*
* Used to track sigaction() arguments.
*/
typedef struct {
long long ss_set[4];
} sigset64_t;
typedef struct sigaction {
void *sa_sigaction;
sigset64_t sa_mask;
int sa_flags;
} sigaction_t;
/*
* Used for manual stack unwind.
*/
typedef struct ld_info {
long long fields[4];
void *ldinfo_textorg;
long long ldinfo_textsize;
void *ldinfo_dataorg;
long long ldinfo_datasize;
} ld_info_t;
/*
* Function prototypes (at least what we need).
*/
int sigaction(int, sigaction_t *);
void *kload(char *);
int execve(char *, char *);
/*
* For sigaction probes.
*/
__thread int in_sigaction;
__thread int sigaction_sig;
__thread void *sigaction_handler;
/*
* Restrict probe to only SIGTERM (15) and 'db2rocm' executable.
*/
@@syscall:*:sigaction:entry
when ((__arg1 == 15) && (__pname == "db2rocm"))
{
__auto sigaction_t new;
thread:in_sigaction = 1;
thread:sigaction_sig = __arg1;
if (__arg2) {
copy_userdata(__arg2, new);
thread:sigaction_handler = new.sa_sigaction;
} else {
thread:sigaction_handler = 0;
}
}
@@syscall:*:sigaction:exit
when (thread:in_sigaction == 1)
{
thread:in_sigaction = 0;
printf("[ %s - %ld - %ld - %ld ] sigaction(%d, 0x%016llx) = %d\n",
__pname, __pid, __tid, __ppid,
thread:sigaction_sig, thread:sigaction_handler, __rv);
/*
* Collect a stack trace. Only addresses as this simply doesn't
* work properly if symbols are requested (PRINT_SYMBOLS).
* Eventually a raw stack dump in hex can be requested bu adding the
* following piece of code:
*
* String stackdump[4096];
*
* copy_userdata(__mst->r1, stackdump);
* trace(stackdump);
*/
stktrace(GET_USER_TRACE, 20);
}
/*
* Add a probe for loading dynamic libraries. This would be useful
* for matching stack addresses with symbols.
*/
@@syscallx:*:kload:entry
when (__pname == "db2rocm")
{
thread:libname = __arg1;
thread:in_kload = 1;
}
@@syscallx:*:kload:exit
when ((thread:in_kload == 1) && (__rv != (void *) 0))
{
__auto String pathname[256];
__auto ld_info_t ldinfo;
pathname = get_userstring(thread:libname, 255);
copy_userdata(__rv, ldinfo);
printf("[ %s - %ld - %ld - %ld ] kload(%s) [t 0x%016llx - d 0x%016llx]\n",
__pname, __pid, __tid, __ppid, pathname,
ldinfo.ldinfo_textorg, ldinfo.ldinfo_dataorg);
thread:in_kload = 0;
}
/*
* Handle 'execve()' probes so that we can match our 'db2rocm'.
*/
@@syscall:*:execve:entry
{
__auto String buf[256];
__auto long long *addr[8];
__auto long long *ptr;
buf = get_userstring((void *) __arg1, -1);
if (strstr(buf, "db2rocm")) {
copy_userdata(__arg2, addr);
ptr = (long long *) addr[0];
buf = get_userstring(ptr, 256);
if (buf == "db2rocm") {
printf("[ %s - %ld - %ld - %ld ] execve(",
__pname, __pid, __tid, __ppid);
if (ptr) { /* av[0] */
printf(" [%s]", buf);
ptr = (long long *) addr[1];
if (ptr) { /* av[1] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
ptr = (long long *) addr[2];
if (ptr) { /* av[2] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
ptr = (long long *) addr[3];
if (ptr) { /* av[3] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
ptr = (long long *) addr[4];
if (ptr) { /* av[4] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
ptr = (long long *) addr[5];
if (ptr) { /* av[5] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
ptr = (long long *) addr[6];
if (ptr) { /* av[6] */
buf = get_userstring(ptr, 256);
printf(" [%s]", buf);
}
}
}
}
}
}
}
}
printf(" )\n");
}
}
UID
ibm13286269