Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Friday, January 18, 2008

Notes on Linux signal in the context of process and thread

Handing Linux signals correctly is difficult, for a few reasons 1) Linux signal descends from the archaic Unix SysV signal system, it still supports the signal/pause calls etc that are susceptible to race conditions and all kinds of haphazard ways of bad signal handling practice; 2) The POSIX standard is intentionally cloudy on a couple of signal related issues, e.g. fields in siginfo_t; 3) Linux signal does not always follow the POSIX standard; 4) There are still lots of code using the SysV signal mechanism, that should be migrated to the better POSIX system.

Linux Programming by Example has an excellent chapter on Linux signal handling. The picture is not complete because Linux thread increases the complexity of signal handling. The following code tries to demonstrate a few important points of Linux signal handling in the context Linux threads:



/*
1. there is no per thread signal mask or signal handler, these concepts only
applies to a process

2. raise and kill work differently with pthreads

3. synchronous signal raised by a thread goes to that thread itself *only* not the process group

*/
#include < iostream>
using namespace std;

#include < boost/thread/thread.hpp>

extern "C"{
#include < signal.h>
}

volatile sig_atomic_t interrupted = 0;
int standby_thr_pid = (long int)syscall(224);

//#define SI_USER 0 /* sent by kill, sigsend, raise */
//#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */
//#define SI_QUEUE -1 /* sent by sigqueue */
//#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
//#define SI_MESGQ __SI_CODE(__SI_MESGQ,-3) /* sent by real time mesq state change */
//#define SI_ASYNCIO -4 /* sent by AIO completion */
//#define SI_SIGIO -5 /* sent by queued SIGIO */
//#define SI_TKILL -6 /* sent by tkill system call */
//#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */

// pid = 0 uid = 0 -> process itself
// else pid > 0 uid > 0 -> external process sent signal
// si_code = 128 (0x80) sent by kernel, e.g. interactive terminal ctl+c
// si_code = -6 sent by tkill
// si_code = 0 sent by kill/killpg/raise call
// there is no per thread signal handler, signal handler is installed
// process wise always
void ctlc(int sig, siginfo_t * info, void * context){
interrupted = 1;
int pid = (long int)syscall(224);
cout << pid << " received: " <<
sig << ' ' << info->si_code << ' ' << info->si_pid << ' ' << info->si_uid << '\n';
}

// raise SIGINT in a separate thread
void sig_sender(void){
cout << "sig sender " << (long int)syscall(224) << '\n';
sleep(3);
raise(SIGINT); // raise = kill(getpid(), sig) in process, signal sent to sender itself only
sleep(1);
kill(standby_thr_pid, SIGINT); // signal only sent to the standby thread process/thread
}

void sig_receiver(void){
//sigset_t set, old_set;

//sigaddset(&set, SIGINT);
//sigprocmask(SIG_BLOCK, &set, &old_set);

cout << "sig installer: " << (long int)syscall(224) << '\n';
struct sigaction act, old_act;
sigaddset(&(act.sa_mask), SIGINT);
sigaddset(&(act.sa_mask), SIGSEGV);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = ctlc;

sigaction(SIGINT, &act, &old_act);
sigaction(SIGSEGV, &act, &old_act);
}

void standby(void){
standby_thr_pid = (long int)syscall(224);
cout << "sig standby " << standby_thr_pid << '\n';
sleep(5);
}
// one thread acts as sender, the other receiver
int main(){
boost::thread trs(sig_sender);
boost::thread trr(sig_receiver);
boost::thread trsb(standby);
while(true){
sleep(1);
if(interrupted){
cout << "interrupt handler invoked\n";
interrupted = 0;
}
}
}