============================================
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