Skip to content

Fix sigaltstack error path leaking sighand lock - #2727

Open
SongXiaoXi wants to merge 1 commit into
ish-app:masterfrom
SongXiaoXi:master
Open

Fix sigaltstack error path leaking sighand lock#2727
SongXiaoXi wants to merge 1 commit into
ish-app:masterfrom
SongXiaoXi:master

Conversation

@SongXiaoXi

Copy link
Copy Markdown

sys_sigaltstack() returns _ENOMEM while still holding sighand->lock when the new alt stack is smaller than MINSIGSTKSZ_, causing a permanent lock leak/deadlock on later signal operations.

A simple reproducer:

#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
int main(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    stack_t ss = {0};
    ss.ss_sp = malloc(1);
    ss.ss_size = 1; // intentionally smaller than MINSIGSTKSZ
    ss.ss_flags = 0;
    printf("[1] calling sigaltstack(tiny stack)...\n");
    errno = 0;
    long r = syscall(SYS_sigaltstack, &ss, NULL);
    printf("[1] ret=%ld errno=%d (%s)\n", r, errno, strerror(errno));
    return 0;
}

When this reproducer is compiled and run inside the iSH app, the whole app becomes stuck after the first failing sigaltstack() call.

@emkey1

emkey1 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Independently reproduced and confirmed. We hit this in the iSH-AOK fork as well and arrived at the identical fix, so this is a second pair of eyes rather than a competing patch.

Reproduced on master (7864dd6) using your reproducer plus one extra step — any later signal operation needs sighand->lock, which the failed call never released:

long r = syscall(SYS_sigaltstack, &ss, NULL);   /* ss.ss_size = 1 */
printf("[1] ret=%ld errno=%d\n", r, errno);
signal(SIGUSR1, h);                              /* needs sighand->lock */
raise(SIGUSR1);
printf("SURVIVED\n");
result
real Linux (i386, same static binary) ENOMEM, handler runs, exits 0
upstream master wedges — no output at all, killed at 20s
master + this PR ENOMEM, handler runs, exits 0 — matches Linux

Worth noting how hard it wedges: on the unpatched build the program produces no output whatsoever, not even the first printf (stdout is unbuffered in the reproducer). The leaked lock takes out enough of the signal path that the process cannot complete a write, which is consistent with the "whole app becomes stuck" you describe.

For whatever it's worth as an independent data point: our fork has carried the same one-line unlock(&sighand->lock) on that error path for a while, reached separately, and we have not seen any fallout from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants