Bug 197216 - incorrect signal delivery for linux apps
Summary: incorrect signal delivery for linux apps
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: Dmitry Chagin
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-01-31 00:12 UTC by Henry Hu
Modified: 2016-01-09 21:27 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Henry Hu 2015-01-31 00:12:25 UTC
FreeBSD's Linux emulation tries to translate between Linux signals and FreeBSD signals. However, this is not a one-to-one mapping, which results in some incorrect behavior.

For example, if an application sets a signal handler for SIGPWR(30), and sends SIGPWR to itself, then the signal handler would be called, but the signal number passed in is not 30, but 23(SIGURG).
The reason is that FreeBSD translates Linux's SIGPWR(30) to FreeBSD's SIGURG(16), but FreeBSD's SIGURG(16) is translated back to Linux's SIGURG(23).
A simple test program:

#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

const int SIGNAL_TO_TEST = SIGPWR;

void handler(int signum) {
    if (signum != SIGNAL_TO_TEST) {
        printf("Error! got signal %d expected %d\n", signum, SIGNAL_TO_TEST);
    } else {
        printf("Got signal %d as expected\n", signum);
    }
}

int main() {
    struct sigaction sigact;
    sigact.sa_handler = handler;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    if (sigaction(SIGNAL_TO_TEST, &sigact, NULL) == -1) {
        perror("fail to call sigaction");
    }

    kill(getpid(), SIGNAL_TO_TEST);

    sleep(1);
}

It should output

Got signal 30 as expected

but on FreeBSD it says

Error! got signal 23 expected 30

instead.

A real example can be found at 
https://github.com/mono/mono/blob/master/libgc/pthread_stop_world.c
where it uses SIG_SUSPEND to pause threads. SIG_SUSPEND is defined to be SIGPWR.
The signal handler checks the value of signal passed in:
  if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");

A reasonable fix would be mapping SIGPWR to some currently unmapped FreeBSD signal, like SIGEMT. Although it's not completely correct in semantics, at least it works for signal delivery between Linux applications.

For the uncommon signals like SIGURG and SIGPWR, usually they are not sent between FreeBSD and Linux applications. I think that we'd better keep them working as expected when being sent between Linux applications.
Comment 1 Jilles Tjoelker freebsd_committer freebsd_triage 2015-02-01 21:51:26 UTC
The FreeBSD kernel supports signals 1..126. I think it would be acceptable to use some of the unused FreeBSD signal numbers for mapping Linux signals that FreeBSD does not have. Note that all the unused signals have default action terminate without core dump; this matches SIGPWR but may cause subtle breakage for signals that default to ignore in Linux.
Comment 2 Dmitry Chagin freebsd_committer freebsd_triage 2015-02-02 06:39:16 UTC
agree with you, Jilles. i will fix this
Comment 3 Dmitry Chagin freebsd_committer freebsd_triage 2015-02-02 06:39:32 UTC
agree with you, Jilles. i will fix this
Comment 4 commit-hook freebsd_committer freebsd_triage 2015-02-21 20:41:14 UTC
A commit references this bug:

Author: dchagin
Date: Sat Feb 21 20:40:44 UTC 2015
New revision: 279107
URL: https://svnweb.freebsd.org/changeset/base/279107

Log:
  Rework signal code to allow using it by other modules, like linprocfs:

  1. Linux sigset always 64 bit on all platforms. In order to move Linux
  sigset code to the linux_common module define it as 64 bit int. Move
  Linux sigset manipulation routines to the MI path.

  2. Move Linux signal number definitions to the MI path. In geneeral they
  are the same on all platforms except for a few signals.

  3. Map Linux RT signals to the FreeBSD RT signals and hide signal conversion
  tables to avoid conversion errors.

  4. Emulate Linux SIGPWR signal via FreeBSD SIGRTMIN signal which is outside
  of allowed on Linux signal numbers.

  PR:             197216

Changes:
  user/dchagin/lemul/sys/amd64/linux/linux.h
  user/dchagin/lemul/sys/amd64/linux/linux_machdep.c
  user/dchagin/lemul/sys/amd64/linux/linux_sysvec.c
  user/dchagin/lemul/sys/amd64/linux32/linux.h
  user/dchagin/lemul/sys/amd64/linux32/linux32_machdep.c
  user/dchagin/lemul/sys/amd64/linux32/linux32_sysvec.c
  user/dchagin/lemul/sys/compat/linux/linux.c
  user/dchagin/lemul/sys/compat/linux/linux.h
  user/dchagin/lemul/sys/compat/linux/linux_fork.c
  user/dchagin/lemul/sys/compat/linux/linux_misc.c
  user/dchagin/lemul/sys/compat/linux/linux_signal.c
  user/dchagin/lemul/sys/compat/linux/linux_signal.h
  user/dchagin/lemul/sys/i386/linux/linux.h
  user/dchagin/lemul/sys/i386/linux/linux_sysvec.c
  user/dchagin/lemul/sys/modules/linux/Makefile
  user/dchagin/lemul/sys/modules/linux_common/Makefile
Comment 5 commit-hook freebsd_committer freebsd_triage 2015-05-24 17:47:30 UTC
A commit references this bug:

Author: dchagin
Date: Sun May 24 17:47:24 UTC 2015
New revision: 283474
URL: https://svnweb.freebsd.org/changeset/base/283474

Log:
  Rework signal code to allow using it by other modules, like linprocfs:

  1. Linux sigset always 64 bit on all platforms. In order to move Linux
  sigset code to the linux_common module define it as 64 bit int. Move
  Linux sigset manipulation routines to the MI path.

  2. Move Linux signal number definitions to the MI path. In general, they
  are the same on all platforms except for a few signals.

  3. Map Linux RT signals to the FreeBSD RT signals and hide signal conversion
  tables to avoid conversion errors.

  4. Emulate Linux SIGPWR signal via FreeBSD SIGRTMIN signal which is outside
  of allowed on Linux signal numbers.

  PR:		197216

Changes:
  head/sys/amd64/linux/linux.h
  head/sys/amd64/linux/linux_machdep.c
  head/sys/amd64/linux/linux_sysvec.c
  head/sys/amd64/linux32/linux.h
  head/sys/amd64/linux32/linux32_machdep.c
  head/sys/amd64/linux32/linux32_sysvec.c
  head/sys/compat/linux/linux.c
  head/sys/compat/linux/linux.h
  head/sys/compat/linux/linux_fork.c
  head/sys/compat/linux/linux_misc.c
  head/sys/compat/linux/linux_signal.c
  head/sys/compat/linux/linux_signal.h
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/i386/linux/linux.h
  head/sys/i386/linux/linux_machdep.c
  head/sys/i386/linux/linux_ptrace.c
  head/sys/i386/linux/linux_sysvec.c
  head/sys/modules/linux/Makefile
  head/sys/modules/linux_common/Makefile
Comment 6 commit-hook freebsd_committer freebsd_triage 2016-01-09 17:29:43 UTC
A commit references this bug:

Author: dchagin
Date: Sat Jan  9 17:29:09 UTC 2016
New revision: 293575
URL: https://svnweb.freebsd.org/changeset/base/293575

Log:
  MFC r283474:

  Rework signal code to allow using it by other modules, like linprocfs:

  1. Linux sigset always 64 bit on all platforms. In order to move Linux
  sigset code to the linux_common module define it as 64 bit int. Move
  Linux sigset manipulation routines to the MI path.

  2. Move Linux signal number definitions to the MI path. In general, they
  are the same on all platforms except for a few signals.

  3. Map Linux RT signals to the FreeBSD RT signals and hide signal conversion
  tables to avoid conversion errors.

  4. Emulate Linux SIGPWR signal via FreeBSD SIGRTMIN signal which is outside
  of allowed on Linux signal numbers.

  PR:             197216

Changes:
_U  stable/10/
  stable/10/sys/amd64/linux/linux.h
  stable/10/sys/amd64/linux/linux_machdep.c
  stable/10/sys/amd64/linux/linux_sysvec.c
  stable/10/sys/amd64/linux32/linux.h
  stable/10/sys/amd64/linux32/linux32_machdep.c
  stable/10/sys/amd64/linux32/linux32_sysvec.c
  stable/10/sys/compat/linux/linux.c
  stable/10/sys/compat/linux/linux.h
  stable/10/sys/compat/linux/linux_fork.c
  stable/10/sys/compat/linux/linux_misc.c
  stable/10/sys/compat/linux/linux_signal.c
  stable/10/sys/compat/linux/linux_signal.h
  stable/10/sys/conf/files.amd64
  stable/10/sys/conf/files.i386
  stable/10/sys/i386/linux/linux.h
  stable/10/sys/i386/linux/linux_machdep.c
  stable/10/sys/i386/linux/linux_ptrace.c
  stable/10/sys/i386/linux/linux_sysvec.c
  stable/10/sys/modules/linux/Makefile
  stable/10/sys/modules/linux_common/Makefile