Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1325ec8c authored by Ryan Prichard's avatar Ryan Prichard
Browse files

init: use signalfd to catch SIGCHLD

Previously, if init received too many SIGCHLD signals, then the write to
signal_write_fd could fail with EAGAIN. The handler tried to log the
EAGAIN error, and init deadlocked if the interrupted init process had
already acquired a logging-related lock.

Bug: b/77867680
Test: manual
Change-Id: Ief0b5e94d8517827a5a7d03773391ba3ba9447c4
parent af15fbf9
Loading
Loading
Loading
Loading
+50 −27
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ static char qemu[32];
std::string default_console = "/dev/console";

static int epoll_fd = -1;
static int sigterm_signal_fd = -1;
static int signal_fd = -1;

static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
@@ -492,14 +492,7 @@ static void InstallRebootSignalHandlers() {
    sigaction(SIGTRAP, &action, nullptr);
}

static void HandleSigtermSignal() {
    signalfd_siginfo siginfo;
    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
    if (bytes_read != sizeof(siginfo)) {
        PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
        return;
    }

static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
    if (siginfo.ssi_pid != 0) {
        // Drop any userspace SIGTERM requests.
        LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
@@ -509,37 +502,73 @@ static void HandleSigtermSignal() {
    HandlePowerctlMessage("shutdown,container");
}

static void UnblockSigterm() {
static void HandleSignalFd() {
    signalfd_siginfo siginfo;
    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
    if (bytes_read != sizeof(siginfo)) {
        PLOG(ERROR) << "Failed to read siginfo from signal_fd";
        return;
    }

    switch (siginfo.ssi_signo) {
        case SIGCHLD:
            ReapAnyOutstandingChildren();
            break;
        case SIGTERM:
            HandleSigtermSignal(siginfo);
            break;
        default:
            PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
            break;
    }
}

static void UnblockSignals() {
    const struct sigaction act { .sa_handler = SIG_DFL };
    sigaction(SIGCHLD, &act, nullptr);

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigaddset(&mask, SIGTERM);

    if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
        PLOG(FATAL) << "failed to unblock SIGTERM for PID " << getpid();
        PLOG(FATAL) << "failed to unblock signals for PID " << getpid();
    }
}

static void InstallSigtermHandler() {
static void InstallSignalFdHandler() {
    // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
    // SIGCHLD when a child process stops or continues (b/77867680#comment9).
    const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
    sigaction(SIGCHLD, &act, nullptr);

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);

    if (!IsRebootCapable()) {
        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
        // In that case, receiving SIGTERM will cause the system to shut down.
        sigaddset(&mask, SIGTERM);
    }

    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
        PLOG(FATAL) << "failed to block SIGTERM";
        PLOG(FATAL) << "failed to block signals";
    }

    // Register a handler to unblock SIGTERM in the child processes.
    const int result = pthread_atfork(nullptr, nullptr, &UnblockSigterm);
    // Register a handler to unblock signals in the child processes.
    const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
    if (result != 0) {
        LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
    }

    sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
    if (sigterm_signal_fd == -1) {
        PLOG(FATAL) << "failed to create signalfd for SIGTERM";
    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
    if (signal_fd == -1) {
        PLOG(FATAL) << "failed to create signalfd";
    }

    register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
    register_epoll_handler(signal_fd, HandleSignalFd);
}

int main(int argc, char** argv) {
@@ -682,13 +711,7 @@ int main(int argc, char** argv) {
        PLOG(FATAL) << "epoll_create1 failed";
    }

    sigchld_handler_init();

    if (!IsRebootCapable()) {
        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
        // In that case, receiving SIGTERM will cause the system to shut down.
        InstallSigtermHandler();
    }
    InstallSignalFdHandler();

    property_load_boot_defaults();
    export_oem_lock_status();
+0 −39
Original line number Diff line number Diff line
@@ -39,9 +39,6 @@ using android::base::make_scope_guard;
namespace android {
namespace init {

static int signal_write_fd = -1;
static int signal_read_fd = -1;

static bool ReapOneProcess() {
    siginfo_t siginfo = {};
    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
@@ -102,46 +99,10 @@ static bool ReapOneProcess() {
    return true;
}

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    ReapAnyOutstandingChildren();
}

static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        PLOG(ERROR) << "write(signal_write_fd) failed";
    }
}

void ReapAnyOutstandingChildren() {
    while (ReapOneProcess()) {
    }
}

void sigchld_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    ReapAnyOutstandingChildren();

    register_epoll_handler(signal_read_fd, handle_signal);
}

}  // namespace init
}  // namespace android
+0 −2
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ namespace init {

void ReapAnyOutstandingChildren();

void sigchld_handler_init(void);

}  // namespace init
}  // namespace android