siginfo中的数据是否值得信赖?

我发现,在Linux上,通过自己调用rt_sigqueue系统调用,我可以把我喜欢的东西放在si_uidsi_pid字段中,调用成功,并愉快地传递不正确的值。 自然,发送信号的uid限制为这种欺骗提供了一些保护,但我担心依靠这些信息可能是危险的。 关于这个话题我能读懂吗? 为什么Linux允许让调用者指定siginfo参数而不是在kernelspace中生成它们的明显不正确的行为? 这似乎是荒谬的,特别是因为可能需要额外的系统调用(并且因此性能成本)才能获得用户空间中的uid / gid。

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

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

我相信Linux的这种行为是不合格的,是一个严重的错误。

Solutions Collecting From Web of "siginfo中的数据是否值得信赖?"

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

 SI_QUEUE The signal was sent by the sigqueue() function. 

该部分继续说:

如果信号不是由上面列出的函数或事件之一产生的, si_codesi_code设置为XBD中描述的某个特定于信号的值,或者设置为不等于任何定义值的实现定义值以上。

如果只有sigqueue()函数使用SI_QUEUE则没有违反。 你的场景涉及使用SI_QUEUEsigqueue()函数以外的代码问题是,POSIX是否设想一个操作系统强制只允许一个指定的库函数(而不是一些不是POSIX定义的库函数的函数)系统调用具有一定的特点。 我相信答案是“不”。

编辑2011-03-26,14:00 PST:

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

我想你基本上是对的。 但是或者一个系统是POSIX兼容的,或者不是。 如果一个非库函数执行的系统调用会导致uid,pid和'si_code'的不合规组合,那么我引用的第二个语句就会清楚地表明调用本身不符合规范。 人们可以通过两种方式来解释这一点。 一种方法是:“如果用户违反了这条规则,那么他就会使系统不符合规定。” 但是你是对的,我觉得这很愚蠢。 当任何非特权用户可以使其不符合要求时系统有什么好处? 正如我所看到的那样,修正方法是让系统知道它不是用于调用系统的库“sigqueue()”,那么内核本身应该将“si_code”设置为“SI_QUEUE”之外的其他值,然后离开当你设置他们的时候, 在我看来,你应该向内核人士提出这个问题。 然而,他们可能有困难; 我不知道是否有任何安全的方式来检测系统调用是否由特定的库函数创建,从而查看库的功能。 几乎就定义而言,仅仅是系统调用的方便包装。 这可能是他们所采取的立场,我知道这将是一个失望。

(大量)编辑截至2011年3月26日,太平洋标准时间18:00:

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

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

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

通过“内核sysqueue系统调用”,你的意思是`__NR_rt_sigqueueinfo'调用? 这是我做这件事时唯一找到的:

 grep -Ri 'NR.*queue' /usr/include 

如果是这样,我想我不了解你的原始观点。 内核将让我(非根)使用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() */ 

接收端编码如下:

 #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() */ 

接下来我可以运行(非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$ 

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

 wally:~/tmp/20110326$ send 9023 wally:~/tmp/20110326$ 

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

我同意si_uidsi_pid应该是可信的,如果不是,那就是一个bug。 但是,只有在信号是由子进程的状态更改生成的SIGCHLD ,或者si_codeSI_USERSI_QUEUE ,或者系统支持XSI选项和si_code <= 0si_code <= 0 。 在其他情况下,Linux / glibc也传递si_uidsi_pid值; 这些往往是不可信的,但这不是POSIX一致性问题。

当然,对于kill()信号可能不排队,在这种情况下siginfo_t不提供任何附加信息。

rt_sigqueueinfo允许不止SI_QUEUE原因可能是允许以最小的内核支持实现POSIX异步I / O,消息队列和每个进程定时器。 在用户SI_ASYNCIO实现这些功能需要分别使用SI_ASYNCIOSI_MESGQSI_TIMER发送信号。 我不知道glibc如何分配资源来预先排队信号; 对我来说,它看起来像它不,只是希望rt_sigqueueinfo不会失败。 POSIX明确禁止丢弃定时器到期(异步I / O完成,消息到达消息队列)通知,因为在到期时过多的信号排队; 如果资源不足,实施应该拒绝创建或注册。 对象已被仔细定义,以便每个I / O请求,消息队列或计时器一次最多只能有一个信号在运行。