0

I'm currently modifying perf in Linux source tree to implement a tool that I need.

perf originally has a signal handler installed(handler for SIGUSR2) but I want to extend its functionality by handling two signals(SIGUSR1 and SIGUSR2)

As shown below, the kernel (not a user process) sends the two signals consecutively.

    asmlinkage long sys_pt_signal_perf(void) //new syscall No.500
    {
            return (long)pt_signal_perf_wrapper();
    }

    int pt_signal_perf_wrapper(void)
    {
            if(signal_perf(2) != -1) { //SIGUSR2
                    /* Some dummy loop */
                    signal_perf(1); //SIGUSR1

                    printk(KERN_EMERG "Marker chunk : %d\n", j);
                    printk(KERN_EMERG "[SEMAPHORE LOCK] Marker Chunk\n");
                    pt_down_interruptible(&decode_semaphore); //perf user process unlocks this semaphore lock
                    printk(KERN_EMERG "Decoding for this syscall seems to be finished\n");

                    return 1;
            }
            else {
                    return 0;
            }
    }
    EXPORT_SYMBOL(pt_signal_perf_wrapper);

    int signal_perf(int sig_type)
    {
            int signal_to_send;
            if(sig_type == 1)
                    signal_to_send = SIGUSR1;
            else
                    signal_to_send = SIGUSR2;


            if(!perf_pid) {
                    return -1;
            }
            if(!target_pid) {
                    return -1;
            }

            struct siginfo info;
            memset(&info, 0, sizeof(struct siginfo));
            info.si_signo = signal_to_send;
            info.si_code = SI_QUEUE;
            info.si_int = SI_USER;

            struct task_struct *t = find_task_by_vpid(perf_pid);
            if(!t) {
                    printk(KERN_EMERG "No task struct for %d!\n", perf_pid);
                    return -1;
            }
            int send_sig_info_result = send_sig_info(signal_to_send, &info, t);
            if(send_sig_info_result < 0)
                    printk(KERN_EMERG "KKM : Error sending signal!!\n");
            else
                    printk(KERN_EMERG "KKM : sending signal successful!!\n");

            return (long)perf_pid;
    }

The perf, the signal receipient, runs as a user process.

    static sigset_t pt_sigset;

... (in main function)

    signal(SIGUSR2, snapshot_sig_handler);
    signal(SIGUSR1, snapshot_sig_handler);

    /* set pt_sigset to block SIGUSR1/2 when they are being processed */
    sigemptyset(&pt_sigset);
    if(sigaddset(&pt_sigset, SIGUSR1) == -1) {
            pr_err("sigaddset for SIGUSR1 returned -1\n");
            return -1;
    }
    if(sigaddset(&pt_sigset, SIGUSR2) == -1) {
            pr_err("sigaddset for SIGUSR2 returned -1\n");
            return -1;
    }

in signal handler, when a signal(either SIGUSR1 or SIGUSR2) is received , it blocks the both SIGUSR1 and SIGUSR2 signals using sigprocmask.

    static void snapshot_sig_handler(int sig __maybe_unused)
    {
            pr_err("Blocking the two signals\n");
            if(sigprocmask(SIG_BLOCK, &pt_sigset, NULL) == -1) {
                    pr_err("sigprocmask returned with error\n");
            }

            if(sig == SIGUSR2) {
                    pr_err("SIG_HANDLER from a non-marker chunk\n");
            }
            else if(sig == SIGUSR1) {
                    pr_err("SIG_HANDLER from a marker chunk\n");
            }
            else {
                    pr_err("NOT A VALID SIGNAL!\n");
                    return;
            }

            /* Real operation which is omitted here */
    }

Unblocking the two signals is not shown but definitely is there.

But when I actually run the code, there are some cases where the signal handler function is called even when the two signals are unblocked. (like below)

Blocking the two signals
SIG_HANDLER from a non-marker chunk
Blocking the two signals
SIG_HANDLER from a marker chunk
Unblocking the two signals
Unblocking the two signals

It seems as if blocking of the two signals never even work, although sigprocmask() doesn't return with error.

Is this because the signal is sent from a higher privilege?(kernel->user) Unlike most cases where signal transactions are done between two user processes?

Thank you very much in advance.

skreamo
  • 9
  • 1
  • AFAICS, `pr_err(...)` is a macro evaluating to `fprintf(stderr,...)`. Since `fprintf()` is not [async-signal-safe](http://man7.org/linux/man-pages/man7/signal.7.html), the order of your outputs might just be an artefact of that. You could try using my [`wrerr()`](http://stackoverflow.com/a/28030139/1475978) instead; it is async-signal-safe, but takes a fixed string (and not a format with parameters) -- but since you only print a fixed string anyway, it shouldn't matter. – Nominal Animal Jul 02 '16 at 10:08
  • Don't use `signal` use `sigaction` in which you can control signal blocking on signal delivery. In your code, there can be race condition, means that signal can be delivered while in between `pr_err` and call to `sigprocmask`. This is why `signal` function is considered as obsolete or unreliable. – Jean-Baptiste Yunès Jul 02 '16 at 10:47
  • @NominalAnimal wow never though of that factor in comprehending this undesired behavior.. I'll definitely try using wrerr() right away. Thank you for your advice. – skreamo Jul 02 '16 at 14:19
  • @Jean-BaptisteYunès Actually, I've already tried using sigaction instead of signal but things were the same. But maybe I have to look into the race condition a little more throughly because I haven't until reading your comment. Thank you for your advice. – skreamo Jul 02 '16 at 15:07
  • Just thinking aloud: `sigprocmask()` is async-signal-safe, and modifies the mask for the current thread. While the above scheme (with `sigaction()` and `.sa_mask` containing both `SIGUSR1` and `SIGUSR2`) looks sane, I suspect that having a dedicated thread looping on [`sigwaitinfo()`](http://man7.org/linux/man-pages/man2/sigwaitinfo.2.html) (with both signals blocked in the main thread(s)) would be more robust, and easier to understand/maintain. For all cases of *sequential signals* I've encountered before (I've used realtime signals with integer payloads) it was practically necessary. – Nominal Animal Jul 02 '16 at 18:53

0 Answers0