Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions kernel/net/tcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,16 @@ __PRIVILEGED_CODE static ssize_t tcp_write(
sync::irq_state irq = sync::spin_lock_irqsave(sock->lock);
if (sock->shut_wr) {
sync::spin_unlock_irqrestore(sock->lock, irq);
return (count > remaining)
? static_cast<ssize_t>(count - remaining)
: resource::ERR_PIPE;
if (count > remaining) {
return static_cast<ssize_t>(count - remaining);
}

// POSIX: writing a shut-down stream raises SIGPIPE, and write
// has no MSG_NOSIGNAL to suppress it.
signals::send_to_task(sched::current(), signals::SIGPIPE);
return resource::ERR_PIPE;
}

tcp_state cur = sock->state;
if (cur != tcp_state::ESTABLISHED && cur != tcp_state::CLOSE_WAIT) {
sync::spin_unlock_irqrestore(sock->lock, irq);
Expand Down
8 changes: 8 additions & 0 deletions kernel/pipe/pipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "common/ring_buffer.h"
#include "fs/fstypes.h"
#include "mm/heap.h"
#include "sched/sched.h"
#include "signals/signal.h"
#include "sync/poll.h"
#include "dynpriv/dynpriv.h"

Expand Down Expand Up @@ -74,6 +76,12 @@ static ssize_t pipe_write(
RUN_ELEVATED({
result = ring_buffer_write(ep->channel->rb,
static_cast<const uint8_t*>(ksrc), count, nonblock);

// POSIX: a write with no reader raises SIGPIPE in the writer,
// EPIPE only surfaces when the signal is ignored or handled
if (result == RB_ERR_PIPE) {
signals::send_to_task(sched::current(), signals::SIGPIPE);
}
});
return result;
}
Expand Down
73 changes: 72 additions & 1 deletion userland/apps/sigtest/src/sigtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ static void test_unblock_delivers(void) {
check("unblock delivers immediately", usr1_count == 1);
}

/* Child mode: a write with no reader must die by default SIGPIPE */
static int pipe_victim_child(void) {
int fds[2];
if (pipe(fds) != 0) {
return 1;
}
close(fds[0]);
char b = 'x';
write(fds[1], &b, 1);
return 1; /* only reached if the signal never fired */
}

static volatile sig_atomic_t eintr_handler_ran = 0;

static void eintr_handler(int sig) {
Expand Down Expand Up @@ -302,7 +314,65 @@ static void test_poll_eintr_despite_restart(void) {
close(fds[1]);
}

int main(void) {
static volatile sig_atomic_t sigpipe_count = 0;

static void sigpipe_handler(int sig) {
(void)sig;
sigpipe_count++;
}

static void test_sigpipe_dispositions(void) {
int fds[2];
char b = 'x';

/* Ignored: the write fails with EPIPE and the process lives */
signal(SIGPIPE, SIG_IGN);
if (pipe(fds) != 0) {
printf(" SKIP: pipe unavailable\n");
return;
}
close(fds[0]);
ssize_t n = write(fds[1], &b, 1);
int saved_errno = errno;
check("ignored SIGPIPE write fails EPIPE", n == -1 && saved_errno == EPIPE);
close(fds[1]);

/* Handled: the handler runs and EPIPE is still returned */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigpipe_handler;
sigaction(SIGPIPE, &sa, NULL);
if (pipe(fds) != 0) {
printf(" SKIP: pipe unavailable\n");
return;
}
close(fds[0]);
n = write(fds[1], &b, 1);
saved_errno = errno;
check("handled SIGPIPE write fails EPIPE", n == -1 && saved_errno == EPIPE);
check("SIGPIPE handler ran", sigpipe_count == 1);
close(fds[1]);
signal(SIGPIPE, SIG_DFL);

/* Default: a child writing with no reader dies by SIGPIPE */
static const char* args[] = { "--pipe-victim", NULL };
int h = proc_create("/bin/sigtest", args);
if (h < 0) {
printf(" SKIP: self exec unavailable\n");
return;
}
proc_start(h);
int status = 0;
proc_wait(h, &status);
check("default SIGPIPE kills the writer",
STLX_WIFSIGNALED(status) && STLX_WTERMSIG(status) == SIGPIPE);
}

int main(int argc, char** argv) {
if (argc >= 2 && strcmp(argv[1], "--pipe-victim") == 0) {
return pipe_victim_child();
}

setvbuf(stdout, NULL, _IONBF, 0);
printf("sigtest: running signal delivery tests\n");

Expand All @@ -313,6 +383,7 @@ int main(void) {
test_read_eintr();
test_read_restart();
test_poll_eintr_despite_restart();
test_sigpipe_dispositions();

printf("sigtest: %d passed, %d failed\n", passed, failed);
return failed > 0 ? 1 : 0;
Expand Down
Loading