【问题标题】:Memory access error sys_rt_sigaction (signal handler)内存访问错误 sys_rt_sigaction(信号处理程序)
【发布时间】:2017-07-10 10:18:05
【问题描述】:

根据这篇Interfacing Linux Signals 的文章,我一直在尝试在amd64 中使用sys_rt_sigaction,但在发送信号时总是出现内存访问错误struct sigaction 在使用 C/C++ 函数 sigaction 时有效。

sys_rt_sigaction 通话有什么问题?

带有 ASM 代码的 C/C++:

#include<signal.h>
#include<stdio.h>
#include<time.h>

void handler(int){printf("handler\n");}
void restorer(){asm volatile("mov $15,%%rax\nsyscall":::"rax");}

struct sigaction act{handler};
timespec ts{10,0};

int main(){
 act.sa_flags=0x04000000;
 act.sa_restorer=&restorer;
 //*
 asm volatile("\
 mov $13,%%rax\n\
 mov %0,%%rdi\n\
 mov %1,%%rsi\n\
 mov %2,%%rdx\n\
 mov $8,%%r10\n\
 syscall\n\
 mov %%rax,%%rdi\n\
 mov $60,%%rax\n\
#syscall\n\
 "::"i"(7),"p"(&act),"p"(0):"rax","rdi","rsi","rdx","r10");
 /**/

 /*
 sigaction(7,&act,0);
 /**/

 nanosleep(&ts,0);
}

编译

g++ -o bin -std=c++11
g++ -o bin -std=c++11 -no-pie

发送信号

kill -7 `pidof bin`

【问题讨论】:

  • 不是linux的syscallclobber rcx和r11吗?
  • 确实如此,好点。

标签: linux assembly x86-64 system-calls


【解决方案1】:

在 x86-64 linux 中,必须提供 sa_restorer 而您还没有这样做。

relevant part of kernel source

            /* x86-64 should always use SA_RESTORER. */
            if (ksig->ka.sa.sa_flags & SA_RESTORER) {
                    put_user_ex(ksig->ka.sa.sa_restorer, &frame->pretcode);
            } else {
                    /* could use a vstub here */
                    err |= -EFAULT;
            }

C library wrapper 为您执行此操作:

  kact.sa_flags = act->sa_flags | SA_RESTORER;

  kact.sa_restorer = &restore_rt;

使用更新的代码,您确实有一个恢复器,但是您有两个问题:它已损坏并且您传递错误。看上面提到的C库源码可以find this comment

/* The difference here is that the sigaction structure used in the
   kernel is not the same as we use in the libc.  Therefore we must
   translate it here.  */

此外,由于函数序言,您不能将 C++ 函数用作恢复器。此外,不支持从信号处理程序调用printf(但在此处有效)。最后,正如 David Wohlferd 指出的那样,你的破坏者是错误的。总而言之,以下可能是重新设计的版本:

#include<stdio.h>
#include<unistd.h>
#include<time.h>

void handler(int){
    const char msg[] = "handler\n";
    write(0, msg, sizeof(msg));
}

extern "C" void restorer();
asm volatile("restorer:mov $15,%rax\nsyscall");

struct kernel_sigaction {
        void (*k_sa_handler) (int);
        unsigned long sa_flags;
        void (*sa_restorer) (void);
        unsigned long sa_mask;
};

struct kernel_sigaction act{handler};
timespec ts{10,0};

int main(){
 act.sa_flags=0x04000000;
 act.sa_restorer=&restorer;

 asm volatile("\
 mov $13,%%rax\n\
 mov %0,%%rdi\n\
 mov %1,%%rsi\n\
 mov %2,%%rdx\n\
 mov $8,%%r10\n\
 syscall\n\
 "::"i"(7),"p"(&act),"p"(0):"rax","rcx", "rdi","rsi","rdx","r8", "r9", "r10", "r11");

 nanosleep(&ts,0);
}

它仍然很老套,显然你不应该这样做。

【讨论】:

  • 嗨,非常感谢您的帮助 :D 在发布这个问题之前,我已经尝试以多种方式使用restorer 并获得相同的结果:(也许除了restorer,还缺少什么?我怀疑与maskflags 相关,但从未找到。
  • 我无法评论未显示的代码。你确实需要一个恢复器,否则你会打到EFAULT。大概你写错了恢复器,当你使用它时。
  • 好的,我会在稍作停顿后继续工作,让头脑休息一下,呵呵 :D 谢谢
  • AFAIK,内核可能会破坏它们。所有文档都说,除了rcx 更改和添加的r11 clobber 之外,相同的约定适用于用户空间。至少在我的系统上,该值似乎保留了下来,但比抱歉更安全:)
  • 其实我的立场是正确的。我删除了最后两个 cmets。当我阅读 ABI 中的异常部分(以前)时,我假设 而不是(异常)是寄存器保存的常规约定 - RCXR11 i> (与返回值的 RAX 一起)是唯一没有保留的。 ABI 有点模棱两可,但在查看 Linux 内核源代码后,很明显所有其他调用者保留的寄存器(易失性寄存器)似乎都适用于常规用户陆地调用约定。
猜你喜欢
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多