Skip to content

Latest commit

 

History

History
749 lines (677 loc) · 20.6 KB

200311231540.txt.md

File metadata and controls

749 lines (677 loc) · 20.6 KB

24.5 进程如何分辨谁在kill()自己

A: scz [email protected] 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 * )&p;
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: [email protected] 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 * )&p;
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