Use of SEGV signal

signals

As far as I understand, a SEGV signal is sent by the OS to inform a process that it has attempted an illegal memory access.
But I noticed that it is also possible to send a SEGV signal to a process from another process (e.g. kill -s SEGV pid).

Therefore I was wondering, what is the use of being able to send a SEGV signal to another process?

Futhermore, is there anyway for a process to know whether the SEGV signal was sent by the OS or by another process?

Best Answer

Therefore I was wondering, what is the use of being able to send a SEGV signal to another process?

I suppose someone could send it manually for testing. But even if no use for it was thought of originally, we never know if someone might come up with a use, and then arbitrary restrictions would just get in the way.

Futhermore, is there anyway for a process to know whether the SEGV signal was sent by the OS or by another process?

Yes, as mentioned in Catch which user sends kill signal, signal handlers set with sigaction() with the SA_SIGINFO flag can read a number of details from the siginfo_t structure. Those contain the reason the signal was sent (si_code), and if it applies, the sending user and PID (si_uid, si_pid).

See the man page: https://man7.org/linux/man-pages/man2/sigaction.2.html

Here's a simple program to test that, it sets up a handler for SIGSEGV and sends it to itself, first with kill() and then by triggering an invalid memory access:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void action(int sig, siginfo_t *si, void *p)
{
    printf("sig: %d\n", sig);
    printf("si_signo: %d\n", si->si_signo);
    char *reason = "?";
    switch (si->si_code) {
        case SI_USER: reason = "Signal sent by user"; break;
        case SI_TKILL: reason = "Signal sent by user (tkill)"; break;
        case SI_KERNEL: reason = "Signal sent by kernel"; break;
        case SEGV_MAPERR:
            reason = "SIGSEGV - Address not mapped to object"; break;
        case SEGV_ACCERR: 
            reason = "SIGSEGV - Invalid permissions for mapped object"; break;
    }
    printf("si_code: %d (%s)\n", si->si_code, reason);
    printf("si_pid: %d\n", (int) si->si_pid);
    printf("si_uid: %d\n", (int) si->si_uid);
    printf("si_addr: 0x%016lx\n", (unsigned long) si->si_addr);
    if (si->si_code != SI_USER && si->si_code != SI_TKILL) {
        // if it's an actual error, exit instead of
        // returning to repeat the error 
        _exit(1);
    }
}   

int main(void)
{
    struct sigaction sa = {0}; 
    sa.sa_sigaction = action;
    sa.sa_flags = SA_SIGINFO;
    int ret = sigaction(SIGSEGV, &sa, NULL);
    if (ret == -1) {
        perror("sigaction");
        exit(1);
    }
    printf("raising SIGSEGV manually\n");
    kill(getpid(), SIGSEGV);
    printf("\n");
    printf("trying to trigger SIGSEGV\n");
    volatile int *p = (int *) 0xdeadbeef;
    (void) *p;
}

The output on my system is:

raising SIGSEGV manually
sig: 11
si_signo: 11
si_code: 0 (Signal sent by user)
si_pid: 13868
si_uid: 1000
si_addr: 0x000003e80000362c

trying to trigger SIGSEGV
sig: 11
si_signo: 11
si_code: 1 (SIGSEGV - Address not mapped to object)
si_pid: -559038737
si_uid: 0
si_addr: 0x00000000deadbeef

(Note that some fields that don't apply to the case in question contain garbage.)

Related Question