siginfo中的数据值得信赖吗?

R..*_*R.. 9 c linux posix signals sigqueue

我发现在Linux上,通过自己调用rt_sigqueue系统调用,我可以在si_uidsi_pid字段中放入我喜欢的任何内容,并且调用成功并愉快地传递不正确的值.当然,发送信号的uid限制提供了一些防止这种欺骗的保护,但我担心依赖这些信息可能是危险的.关于我能读到的主题,有没有好的文件?为什么Linux允许调用者指定siginfo参数而不是在内核空间中生成它们的明显不正确的行为?这似乎是荒谬的,特别是因为可能需要额外的sys调用(因此性能成本)才能在用户空间中获取uid/gid.

编辑:基于我对POSIX的阅读(我强调):

如果si_code是SI_USER或SI_QUEUE,[XSI]或小于或等于0的任何值,则信号由进程生成,si_pid和si_uid 应分别设置为进程ID和发送方的真实用户ID.

我相信Linux的这种行为是不符合的并且是一个严重的错误.

Bil*_*osa 5

你引用的POSIX页面的那一部分也列出了什么si-code意思,这里是含义:

SI_QUEUE
    The signal was sent by the sigqueue() function.
Run Code Online (Sandbox Code Playgroud)

该部分继续说:

如果信号不是由上面列出的某个函数或事件生成的,si_code则应将其设置为XBD中描述的信号特定值之一,或者设置为不等于上面定义的任何值的实现定义值.

如果仅sigqueue()使用该功能,则不会违反任何内容SI_QUEUE.您的方案涉及sigqueue()使用函数以外的代码SI_QUEUE 问题是POSIX是否设想一个操作系统强制执行只允许指定的库函数(而不是某个不是POSIX定义的库函数的函数)进行系统调用特点.我相信答案是"不".

编辑截至2011-03-26,太平洋标准时间14:00:

这个编辑是对8小时前R ..的评论的回应,因为该页面不会让我留下足够多的评论:

我觉得你基本上是对的.但是系统是POSIX兼容还是不兼容.如果非库函数执行系统调用导致uid,pid和'si_code'的不兼容组合,那么我引用的第二个语句清楚地表明调用本身不符合.人们可以用两种方式来解释这一点.一种方法是:"如果用户违反此规则,那么他会使系统不合规." 但你是对的,我认为这很愚蠢.当任何非特权用户可以使其不合规时,系统有什么用处?正如我所看到的那样,修复程序在某种程度上让系统知道它不是库'sigqueue()'进行系统调用,然后内核本身应该将'si_code'设置为'SI_QUEUE'以外的其他东西,并保留你设置它们的uid和pid.在我看来,你应该向内核人员提出这个问题.然而,他们可能有困难; 我不知道他们有什么安全的方法来检测系统调用是否是由特定的库函数构成的,看看库的运行方式.几乎按定义,它们只是系统调用的便利包装器.这可能是他们采取的立场,我知道这将是一种失望.

(篇幅)EDIT截至2011-03-26,太平洋标准时间18:00:

再次因为评论长度的限制.

这是对大约一小时前R ..的评论的回应.

我对系统调用主题有点新意,所以请耐心等待.

通过"内核sysqueue系统调用",你的意思是`__NR_rt_sigqueueinfo'调用?那是我在做这件事时唯一发现的:

grep -Ri 'NR.*queue' /usr/include
Run Code Online (Sandbox Code Playgroud)

如果是这样的话,我想我不理解你原来的观点.内核会让(非root)使用SI-QUEUE伪造的pid和uid而不会出错.如果我有发送方编码,那么:

#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int    argc,
         char **argv
        )
{
  long john_silver;

  siginfo_t my_siginfo;

  if(argc!=2)
  {
    fprintf(stderr,"missing pid argument\n");

    exit(1);
  }

  john_silver=strtol(argv[1],NULL,0);

  if(kill(john_silver,SIGUSR1))
  {
    fprintf(stderr,"kill() fail\n");

    exit(1);
  }

  sleep(1);

  my_siginfo.si_signo=SIGUSR1;
  my_siginfo.si_code=SI_QUEUE;
  my_siginfo.si_pid=getpid();
  my_siginfo.si_uid=getuid();
  my_siginfo.si_value.sival_int=41;

  if(syscall(__NR_rt_sigqueueinfo,john_silver,SIGUSR1,&my_siginfo))
  {
    perror("syscall()");

    exit(1);
  }

  sleep(1);

  my_siginfo.si_signo=SIGUSR2;
  my_siginfo.si_code=SI_QUEUE;
  my_siginfo.si_pid=getpid()+1;
  my_siginfo.si_uid=getuid()+1;
  my_siginfo.si_value.sival_int=42;

  if(syscall(__NR_rt_sigqueueinfo,john_silver,SIGUSR2,&my_siginfo))
  {
    perror("syscall()");

    exit(1);
  }

  return 0;

} /* main() */
Run Code Online (Sandbox Code Playgroud)

并且接收方编码如下:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int signaled_flag=0;

siginfo_t received_information;

void
my_handler(int        signal_number,
           siginfo_t *signal_information,
           void      *we_ignore_this
          )
{
  memmove(&received_information,
          signal_information,
          sizeof(received_information)
         );

  signaled_flag=1;

} /* my_handler() */

/*--------------------------------------------------------------------------*/

int
main(void)
{
  pid_t            myself;

  struct sigaction the_action;

  myself=getpid();

  printf("signal receiver is process %d\n",myself);

  the_action.sa_sigaction=my_handler;
  sigemptyset(&the_action.sa_mask);
  the_action.sa_flags=SA_SIGINFO;

  if(sigaction(SIGUSR1,&the_action,NULL))
  {
    fprintf(stderr,"sigaction(SIGUSR1) fail\n");

    exit(1);
  }

  if(sigaction(SIGUSR2,&the_action,NULL))
  {
    fprintf(stderr,"sigaction(SIGUSR2) fail\n");

    exit(1);
  }

  for(;;)
  {
    while(!signaled_flag)
    {
      sleep(1);
    }

    printf("si_signo: %d\n",received_information.si_signo);
    printf("si_pid  : %d\n",received_information.si_pid  );
    printf("si_uid  : %d\n",received_information.si_uid  );

    if(received_information.si_signo==SIGUSR2)
    {
      break;
    }

    signaled_flag=0;
  }

  return 0;

} /* main() */
Run Code Online (Sandbox Code Playgroud)

然后,我可以运行(非root)接收方:

wally:~/tmp/20110326$ receive
signal receiver is process 9023
si_signo: 10
si_pid  : 9055
si_uid  : 4000
si_signo: 10
si_pid  : 9055
si_uid  : 4000
si_signo: 12
si_pid  : 9056
si_uid  : 4001
wally:~/tmp/20110326$ 
Run Code Online (Sandbox Code Playgroud)

在发送端看到这个(非root):

wally:~/tmp/20110326$ send 9023
wally:~/tmp/20110326$ 
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,第三个事件已经欺骗了pid和uid.这不是你最初反对的吗?没有EINVALEPERM看不到.我想我很困惑.