Linux信号应用之黑匣子程序设计

发表于:2016-6-07 13:29

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:时间轨迹    来源:51Testing软件测试网采编

  4. 使用可替换信号栈&&sigaltstack函数
  使用可替换栈优化后的程序如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <execinfo.h>
void blackbox_handler(int sig)
{
printf("Enter blackbox_handler: ");
printf("SIG name is %s, SIG num is %d\n", strsignal(sig), sig);
// 打印堆栈信息
printf("Stack information:\n");
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL)
{
perror("backtrace_symbol");
exit(EXIT_FAILURE);
}
for(j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
_exit(EXIT_SUCCESS);
}
long count = 0;
void bad_iter()
{
int a, b, c, d;
a = b = c = d = 1;
a = b + 3;
c = count + 4;
d = count + 5 * c;
count++;
printf("count:%ld\n", count);
bad_iter();
}
int main()
{
stack_t ss;
struct  sigaction   sa;
ss.ss_sp = malloc(SIGSTKSZ);
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) == -1)
{
return EXIT_FAILURE;
}
memset(&sa, 0, sizeof(sa));
sa.sa_handler = blackbox_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK;
if (sigaction(SIGSEGV, &sa, NULL) < 0)
{
return EXIT_FAILURE;
}
bad_iter();
while(1);
return EXIT_SUCCESS;
}
  编译 gcc –rdynamic blackbox_overflow.c 后运行,输出为:
... ...
count:261989
count:261990
count:261991
count:261992
Enter blackbox_handler: SIG name is Segmentation fault, SIG num is 11
Stack information:
backtrace() returned 100 addresses
./a.out(blackbox_handler+0x63) [0x400c30]
/lib/x86_64-linux-gnu/libc.so.6(+0x36ff0) [0x7f6e68d74ff0]
/lib/x86_64-linux-gnu/libc.so.6(_IO_file_write+0xb) [0x7f6e68db7e0b]
/lib/x86_64-linux-gnu/libc.so.6(_IO_do_write+0x7c) [0x7f6e68db931c]
/lib/x86_64-linux-gnu/libc.so.6(_IO_file_xsputn+0xb1) [0x7f6e68db84e1]
/lib/x86_64-linux-gnu/libc.so.6(_IO_vfprintf+0x7fa) [0x7f6e68d8879a]
/lib/x86_64-linux-gnu/libc.so.6(_IO_printf+0x99) [0x7f6e68d92749]
./a.out(bad_iter+0x7a) [0x400d62]
./a.out(bad_iter+0x84) [0x400d6c]
./a.out(bad_iter+0x84) [0x400d6c]
./a.out(bad_iter+0x84) [0x400d6c]
./a.out(bad_iter+0x84) [0x400d6c]
./a.out(bad_iter+0x84) [0x400d6c]
... ...
  可以看到,使用可替换栈以后,虽然同样栈溢出了,但是我们的黑匣子程序还是起作用了。所以这种优化是有效的。下面我们来看优化的代码。
  可以看到我们的代码中使用了sigaltstack函数,该函数的作用就是在在堆中为函数分配一块区域,作为该函数的栈使用。所以,虽然递归函数将系统默认的栈空间用尽了,但是当调用我们的信号处理函数时,使用的栈是它实现在堆中分配的空间,而不是系统默认的栈,所以它仍旧可以正常工作。
  该函数函数原型如下:
  #include <signal.h>
  int sigaltstack(const stack_t *ss, stack_t *oss);
  该函数两个个参数为均为stack_t类型的结构体,先来看下这个结构体:
  typedef struct {
  void  *ss_sp;     /* Base address of stack */
  int    ss_flags;  /* Flags */
  size_t ss_size;   /* Number of bytes in stack */
  } stack_t;
  要想创建一个新的可替换信号栈,ss_flags必须设置为0,ss_sp和ss_size分别指明可替换信号栈的起始地址和栈大小。系统定义了一个常数SIGSTKSZ,该常数对极大多数可替换信号栈来说都可以满足需求,MINSIGSTKSZ规定了可替换信号栈的最小值。
  如果想要禁用已存在的一个可替换信号栈,可将ss_flags设置为SS_DISABLE。
  而sigaltstack第一个参数为创建的新的可替换信号栈,第二个参数可以设置为NULL,如果不为NULL的话,将会将旧的可替换信号栈的信息保存在里面。函数成功返回0,失败返回-1.
  一般来说,使用可替换信号栈的步骤如下:
  在内存中分配一块区域作为可替换信号栈
  使用sigaltstack()函数通知系统可替换信号栈的存在和内存地址
  使用sigaction()函数建立信号处理函数的时候,通过将sa_flags设置为SA_ONSTACK来告诉系统信号处理函数将在可替换信号栈上面运行。
33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号