0
votes

I would like to block all signals in a function using sigprocmask in assembly.

The following code works in C:

#include <stdio.h>
#include <signal.h>
int main() {
    sigset_t n={(unsigned long int) 0xffffffff};
    sigprocmask (SIG_BLOCK, &n, 0);

    for (int i=0; i<0x8ffff; i++) printf(".");
}

When the code executes and starts printing dots on the terminal, I cannot interrupt it with Ctrl+C. So far so good.

The value of SIG_BLOCK is 0, apparently; and the syscall number for sys_rt_sigprocmask is 14: http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64

So I write:

[BITS 64]
    [section .text align=1]

    global main

        main:

            mov r10, 32
            mov rdx, 0
            mov rsi, newmask
            mov rdi, 0

            mov rax, 14
            syscall


            dotPrintLoop:
                mov rdi, dotstring
                mov rax, 0
                syscall
                jmp dotPrintLoop


    [section .data align=1]

        dotstring:  db ".",0

        newmask:    dd 0xffffffff
                    dd 0xffffffff
                    dd 0xffffffff
                    ...

And it does not work. gdb reveals that rax has the value -22 (EINVAL - "invalid parameter") after the first syscall; whereas the second syscall (of sys_write) works just fine.

What am I doing wrong?

1

1 Answers

0
votes

Apparently r10 should hold the value 8 instead of 32.

I encountered 4 different definitions of sigset_t within the kernel code; and for each of them the sizeof() function returns a different result (32, 128, 4 and 8 as I recall). Only the final one is relevant to the syscall.

The kernel first checks that $r10 == sizeof(sigset_t); and returns EINVAL (-22) if that does not hold. The value of sizeof(sigset_t) is equal to 8 for both 32-bit and 64-bit versions.