============================================
24.5 进程如何分辨谁在kill()自己
A: scz <scz@nsfocus.com>; 2003-10-11 19:31
至少对于Linux、FreeBSD、Solaris、AIX这四种操作系统,有一种办法。不要安装传
统sa_handler信号句柄,而是安装sa_sigaction信号句柄。细节请man sigaction并
参照头文件加强理解。下面是一个可移植演示程序。
--------------------------------------------------------------------------
/*
* For x86/Linux RedHat_8 2.4.18-14
* For x86/FreeBSD 4.5-RELEASE
* For SPARC/Solaris 8
* For AIX 4.3.3.0
*
* gcc -Wall -pipe -O3 -s -o siginfo_test siginfo_test.c
*/
/************************************************************************
* *
* Head File *
* *
************************************************************************/
#include <stdio.h>;
#include <stdlib.h>;
#include <string.h>;
#include <strings.h>;
#include <signal.h>;
#include <unistd.h>;
#include <setjmp.h>;
#include <sys/time.h>;
/************************************************************************
* *
* Macro *
* *
************************************************************************/
/*
* for signal handlers
*/
typedef void Sigfunc ( int, siginfo_t *, void * );
#define PRIVATE_SIG_ERR ((Sigfunc *)-1)
/************************************************************************
* *
* Function Prototype *
* *
************************************************************************/
static void Atexit ( void ( * func ) ( void ) );
static void init_signal ( void );
static void init_timer ( unsigned int s );
static void on_alarm ( int signo, siginfo_t *si, void *unused );
static void on_segvbus ( int signo, siginfo_t *si, void *unused );
static void on_terminate ( int signo, siginfo_t *si, void *unused );
static Sigfunc * PrivateSignal ( int signo, Sigfunc *func );
static int Setitimer ( int which,
struct itimerval *value,
struct itimerval *ovalue );
static Sigfunc * Signal ( int signo, Sigfunc *func );
static void terminate ( void );
/************************************************************************
* *
* Static Global Var *
* *
************************************************************************/
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump = 0;
/************************************************************************/
static void Atexit ( void ( * func ) ( void ) )
{
if ( atexit( func ) != 0 )
{
exit( EXIT_FAILURE );
}
return;
} /* end of Atexit */
/*
* 初始化信号句柄
*/
static void init_signal ( void )
{
unsigned int i;
Atexit( terminate );
for ( i = 1; i < 9; i++ )
{
Signal( i, on_terminate );
}
Signal( SIGTERM, on_terminate );
Signal( SIGALRM, on_alarm );
Signal( SIGSEGV, on_segvbus );
Signal( SIGBUS , on_segvbus );
return;
} /* end of init_signal */
/*
* 我们的定时器精度只支持到秒
*/
static void init_timer ( unsigned int s )
{
struct itimerval value;
value.it_value.tv_sec = s;
value.it_value.tv_usec = 0;
/*
* 只生效一次
*/
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
Setitimer( ITIMER_REAL, &value, NULL );
return;
} /* end of init_timer */
static void on_alarm ( int signo, siginfo_t *si, void *unused )
{
fprintf
(
stderr,
"\n"
"signo = %d\n"
"si = 0x%08X\n"
"unused = 0x%08X\n",
signo,
( unsigned int )si,
( unsigned int )unused
);
if ( NULL != si )
{
fprintf
(
stderr,
"si->;si_signo = %d\n"
"si->;si_errno = %d\n"
"si->;si_code = %d\n"
"si->;si_pid = %u\n"
"si->;si_uid = %u\n"
"si->;si_status = %d\n"
"si->;si_addr = 0x%08X\n",
si->;si_signo,
si->;si_errno,
si->;si_code,
( unsigned int )si->;si_pid,
( unsigned int )si->;si_uid,
( int )si->;si_status,
( unsigned int )si->;si_addr
);
}
return;
} /* end of on_alarm */
static void on_segvbus ( int signo, siginfo_t *si, void *unused )
{
fprintf
(
stderr,
"\n"
"signo = %d\n"
"si = 0x%08X\n"
"unused = 0x%08X\n",
signo,
( unsigned int )si,
( unsigned int )unused
);
if ( NULL != si )
{
fprintf
(
stderr,
"si->;si_signo = %d\n"
"si->;si_errno = %d\n"
"si->;si_code = %d\n"
"si->;si_pid = %u\n"
"si->;si_uid = %u\n"
"si->;si_status = %d\n"
"si->;si_addr = 0x%08X\n",
si->;si_signo,
si->;si_errno,
si->;si_code,
( unsigned int )si->;si_pid,
( unsigned int )si->;si_uid,
( int )si->;si_status,
( unsigned int )si->;si_addr
);
}
if ( 0 == canjump )
{
/*
* unexpected signal, ignore
*/
return;
}
canjump = 0;
/*
* jump back to main, don't return
*/
siglongjmp( jmpbuf, signo );
} /* end of on_segvbus */
static void on_terminate ( int signo, siginfo_t *si, void *unused )
{
if ( NULL != si )
{
/*
* 演示用,不推荐在信号句柄中使用fprintf()
*/
fprintf
(
stderr,
"\n"
"signo = %d\n"
"si = 0x%08X\n"
"unused = 0x%08X\n"
"si->;si_signo = %d\n"
"si->;si_errno = %d\n"
"si->;si_code = %d\n",
signo,
( unsigned int )si,
( unsigned int )unused,
si->;si_signo,
si->;si_errno,
si->;si_code
);
/*
* si_code为SI_USER时意味着"signal sent by another process with kill()"
*
* 就上四种OS而言,我所测试的FreeBSD反应与其他三种不同,kill进程时
* si_code始终为0,而FreeBSD有如下定义:
*
* #define SI_USER 0x10001
*
* 如果不判断si_code,强行显示si_pid、si_uid,对于FreeBSD而言总是0。
* 下面出于方便演示目的,没有判断si_code。正确作法应该判断si_code,
* 然后显示联合的不同成员。
*/
fprintf
(
stderr,
"si->;si_pid = %u\n"
"si->;si_uid = %u\n"
"si->;si_status = %d\n"
"si->;si_addr = 0x%08X\n",
( unsigned int )si->;si_pid,
( unsigned int )si->;si_uid,
( int )si->;si_status,
( unsigned int )si->;si_addr
);
}
else
{
fprintf
(
stderr,
"\n"
"signo = %d\n"
"si = 0x%08X\n"
"unused = 0x%08X\n",
signo,
( unsigned int )si,
( unsigned int )unused
);
}
/*
* 这次我们使用atexit()函数
*/
exit( EXIT_SUCCESS );
} /* end of on_terminate */
static Sigfunc * PrivateSignal ( int signo, Sigfunc *func )
{
struct sigaction act, oact;
memset( &act, 0, sizeof( act ) );
sigemptyset( &act.sa_mask );
/*
* Invoke signal-catching function with three arguments instead of one.
*/
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = func;
if ( SIGALRM == signo )
{
#ifdef SA_INTERRUPT
/*
* SunOS 4.x
*/
act.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
#ifdef SA_RESTART
/*
* SVR4, 4.4BSD
*/
act.sa_flags |= SA_RESTART;
#endif
}
if ( sigaction( signo, &act, &oact ) < 0 )
{
return( PRIVATE_SIG_ERR );
}
return( oact.sa_sigaction );
} /* end of PrivateSignal */
static int Setitimer ( int which, struct itimerval *value, struct itimerval *ovalue )
{
int ret;
if ( ( ret = setitimer( which, value, ovalue ) ) < 0 )
{
perror( "setitimer error" );
exit( EXIT_FAILURE );
}
return( ret );
} /* end of Setitimer */
static Sigfunc * Signal ( int signo, Sigfunc *func )
{
Sigfunc *sigfunc;
if ( PRIVATE_SIG_ERR == ( sigfunc = PrivateSignal( signo, func ) ) )
{
perror( "signal error" );
exit( EXIT_FAILURE );
}
return( sigfunc );
} /* end of Signal */
static void terminate ( void )
{
/*
* _exit( EXIT_SUCCESS );
*/
return;
} /* end of terminate */
int main ( int argc, char * argv[] )
{
/*
* for autovar, must be volatile
*/
volatile unsigned char *p;
init_signal();
p = ( unsigned char * )&
if ( 0 != sigsetjmp( jmpbuf, 1 ) )
{
printf
(
"p = 0x%08X\n",
( unsigned int )p
);
goto main_continue;
}
/*
* now sigsetjump() is OK
*/
canjump = 1;
while ( 1 )
{
/*
* 诱发SIGSEGV、SIGBUS
*/
*p = *p;
p++;
}
main_continue:
/*
* 启动定时器
*/
init_timer( 1 );
while ( 1 )
{
/*
* 形成阻塞,降低CPU占用率
*/
getchar();
}
return( EXIT_SUCCESS );
} /* end of main */
/************************************************************************/
--------------------------------------------------------------------------
这种技术是操作系统实现相关的。FreeBSD的si_code与头文件不相符。除了FreeBSD,
其他三种OS的si_addr如愿反映了栈底地址、si_pid/si_uid也能正确反映kill()信号
源。而Solaris会出现si为NULL的情形。下面是Linux上执行示例:
[scz@ /home/scz/src]>; ./siginfo_test
signo = 11
si = 0xBFFFF6B0
unused = 0xBFFFF730
si->;si_signo = 11
si->;si_errno = 0
si->;si_code = 1
si->;si_pid = 3221225472
si->;si_uid = 1869479936
si->;si_status = 2712942
si->;si_addr = 0xC0000000 <= 栈底地址
p = 0xC0000000
signo = 14
si = 0xBFFFF5A8
unused = 0xBFFFF628
si->;si_signo = 14
si->;si_errno = 0
si->;si_code = 128 <= SI_KERNEL 0x80 Send by kernel.
si->;si_pid = 0
si->;si_uid = 0
si->;si_status = 896820224
si->;si_addr = 0x00000000
^Z
[scz@ /home/scz/src]>; bg %1
[scz@ /home/scz/src]>; kill %1
signo = 15
si = 0xBFFFF5A8
unused = 0xBFFFF628
si->;si_signo = 15
si->;si_errno = 0
si->;si_code = 0 <= SI_USER 0x00 Sent by kill, sigsend, raise.
si->;si_pid = 27712 <= kill()信号源
si->;si_uid = 1000 <= kill()信号源
si->;si_status = 896820224
si->;si_addr = 0x00006C40
[scz@ /home/scz/src]>; echo $$
27712
[scz@ /home/scz/src]>; id
uid=1000(scz) gid=0(root) groups=0(root)
[scz@ /home/scz/src]>;
最后结论,对于x86/FreeBSD 4.5-RELEASE,无法利用该技术分辨kill()信号源。其
他三种操作系统可以利用该技术。
一个有趣的想法,进程分辨出kill()信号源,反向kill信号源。
D: law@bbs.apue.net 2003-10-13 10:03
修改一下gstack.c,考虑栈向内存高址方向增长的情形。用递归方式确定堆栈增长方
向比较可靠,否则由于不同函数的不同优化可能导致误判,测试中碰上这种情形了。
--------------------------------------------------------------------------
/*
* For x86/Linux RedHat_8 2.4.18-14
* For x86/FreeBSD 4.5-RELEASE
* For SPARC/Solaris 8
* For AIX 4.3.3.0
*
* gcc -Wall -pipe -O3 -s -o gstack gstack.c
*/
/************************************************************************
* *
* Head File *
* *
************************************************************************/
#include <stdio.h>;
#include <stdlib.h>;
#include <string.h>;
#include <strings.h>;
#include <signal.h>;
#include <unistd.h>;
#include <setjmp.h>;
/************************************************************************
* *
* Macro *
* *
************************************************************************/
/*
* for signal handlers
*/
typedef void Sigfunc ( int, siginfo_t *, void * );
#define PRIVATE_SIG_ERR ((Sigfunc *)-1)
/*
* 向上指向高址方向增长,向下指向低址方向增长,后者最常见
*/
#define STACKUP 0
#define STACKDOWN 1
/************************************************************************
* *
* Function Prototype *
* *
************************************************************************/
static unsigned char * get_stack_bottom ( void );
static void on_segvbus ( int signo,
siginfo_t *si,
void *unused );
static Sigfunc * PrivateSignal ( int signo, Sigfunc *func );
static Sigfunc * Signal ( int signo, Sigfunc *func );
static unsigned int stack_grow ( unsigned int level,
unsigned int *addr );
/************************************************************************
* *
* Static Global Var *
* *
************************************************************************/
/*
* start of .text
*/
extern int _etext;
/*
* start of .data
*/
extern int _edata;
/*
* start of heap
*/
extern int _end;
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump = 0;
static Sigfunc *orig_segv = PRIVATE_SIG_ERR;
static Sigfunc *orig_bus = PRIVATE_SIG_ERR;
/************************************************************************/
static unsigned char * get_stack_bottom ( void )
{
/*
* for autovar, must be volatile
*/
volatile unsigned char *p = NULL;
orig_segv = Signal( SIGSEGV, on_segvbus );
orig_bus = Signal( SIGBUS , on_segvbus );
p = ( unsigned char * )&
if ( 0 != sigsetjmp( jmpbuf, 1 ) )
{
Signal( SIGSEGV, orig_segv );
Signal( SIGBUS , orig_bus );
goto get_stack_bottom_exit;
}
/*
* now sigsetjump() is OK
*/
canjump = 1;
if ( STACKUP == stack_grow( 0, NULL ) )
{
while ( 1 )
{
/*
* 诱发SIGSEGV、SIGBUS
*/
*p = *p;
p--;
}
}
else
{
while ( 1 )
{
/*
* 诱发SIGSEGV、SIGBUS
*/
*p = *p;
p++;
}
}
get_stack_bottom_exit:
return( ( unsigned char * )p );
} /* end of get_stack_bottom */
static void on_segvbus ( int signo, siginfo_t *si, void *unused )
{
fprintf
(
stderr,
"signo = %d\n"
"si = 0x%08X\n"
"unused = 0x%08X\n",
signo,
( unsigned int )si,
( unsigned int )unused
);
if ( NULL != si )
{
fprintf
(
stderr,
"si->;si_signo = %d\n"
"si->;si_errno = %d\n"
"si->;si_code = %d\n"
"si->;si_addr = 0x%08X\n",
si->;si_signo,
si->;si_errno,
si->;si_code,
( unsigned int )si->;si_addr
);
}
if ( 0 == canjump )
{
/*
* unexpected signal, ignore
*/
return;
}
canjump = 0;
/*
* jump back to get_stack_bottom, don't return
*/
siglongjmp( jmpbuf, signo );
} /* end of on_segvbus */
static Sigfunc * PrivateSignal ( int signo, Sigfunc *func )
{
struct sigaction act, oact;
memset( &act, 0, sizeof( act ) );
sigemptyset( &act.sa_mask );
/*
* Invoke signal-catching function with three arguments instead of one.
*/
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = func;
if ( SIGALRM == signo )
{
#ifdef SA_INTERRUPT
/*
* SunOS 4.x
*/
act.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
#ifdef SA_RESTART
/*
* SVR4, 4.4BSD
*/
act.sa_flags |= SA_RESTART;
#endif
}
if ( sigaction( signo, &act, &oact ) < 0 )
{
return( PRIVATE_SIG_ERR );
}
return( oact.sa_sigaction );
} /* end of PrivateSignal */
static Sigfunc * Signal ( int signo, Sigfunc *func )
{
Sigfunc *sigfunc;
if ( PRIVATE_SIG_ERR == ( sigfunc = PrivateSignal( signo, func ) ) )
{
perror( "signal error" );
exit( EXIT_FAILURE );
}
return( sigfunc );
} /* end of Signal */
static unsigned int stack_grow ( unsigned int level, unsigned int *addr )
{
unsigned int dummy;
unsigned int ret;
if ( 0 == level )
{
ret = stack_grow( level + 1, &dummy );
}
else
{
if ( ( unsigned int )addr >; ( unsigned int )&dummy )
{
ret = STACKDOWN;
}
else
{
ret = STACKUP;
}
printf
(
"stack_level_0 = 0x%08X\n"
"stack_level_1 = 0x%08X\n"
"stack grow = %s/%u\n",
( unsigned int )addr,
( unsigned int )&dummy,
( STACKUP == ret ) ? "UP/HIGH" : "DOWN/LOW",
ret
);
}
return( ret );
} /* end of stack_grow */
int main ( int argc, char * argv[] )
{
unsigned char *p;
p = get_stack_bottom();
printf
(
"_etext = 0x%08X\n"
"_edata = 0x%08X\n"
"_end = 0x%08X\n"
"stack bottom = 0x%08X\n"
"&p = 0x%08X\n",
( unsigned int )&_etext,
( unsigned int )&_edata,
( unsigned int )&_end,
( unsigned int )p,
( unsigned int )&p
);
return( EXIT_SUCCESS );
} /* end of main */
/************************************************************************/
--------------------------------------------------------------------------
这是在AIX 4.3.3.0上的执行效果:
>; ./gstack
stack_level_0 = 0x2FF22B40
stack_level_1 = 0x2FF22AF8
stack grow = DOWN/LOW/1 <= 栈向低址方向增长
signo = 11
si = 0x2FF22A10
unused = 0x2FF22780
si->;si_signo = 11
si->;si_errno = 0
si->;si_code = 51
si->;si_addr = 0x2FF23000 <= 栈底地址
_etext = 0x10000E40
_edata = 0x20000ED4
_end = 0x200010C0
stack bottom = 0x2FF23000 <= 栈底地址
&p = 0x2FF22BC8