blob: b405136e7b66df152d1ebd2e220874640e29890b [file] [log] [blame] [edit]
/*
* Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_NO_SOCK) && !defined(__DJGPP__)
#include "internal/sockets.h"
#include <openssl/bio.h>
#include <internal/bio.h>
#include <openssl/err.h>
#include "testutil.h"
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t sigpipe_seen = 0;
static void sigpipe_handler(int sig)
{
(void)sig;
sigpipe_seen++;
}
/*
* 0 - normal flow
* 1 - kTLS
* 2 - TFO
*/
static int test_bio_write_triggers_sigpipe(int test)
{
#if defined(MSG_NOSIGNAL)
int fds[2] = { -1, -1 };
BIO *b = NULL;
const char c = 'x';
int ret;
int ok = 0;
struct sigaction sa, oldsa;
/* Install SIGPIPE handler */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigpipe_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (!TEST_int_eq(sigaction(SIGPIPE, &sa, &oldsa), 0))
goto end;
/* Create a pair of connected sockets. */
if (!TEST_int_eq(socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 0))
goto end;
/* Close peer end to make writes hit a broken pipe. */
if (!TEST_int_eq(closesocket(fds[1]), 0))
goto end;
fds[1] = -1;
b = BIO_new_socket(fds[0], BIO_NOCLOSE);
if (!TEST_ptr(b))
goto end;
/*
* Attempt write. We don't care about return value beyond
* "it attempted", the point is SIGPIPE delivery.
*/
ERR_clear_error();
errno = 0;
sigpipe_seen = 0;
if (test == 1) {
#ifndef OPENSSL_NO_KTLS
BIO_set_ktls_ctrl_msg_flag(b);
#else
TEST_skip("OPENSSL_NO_KTLS is defined\n");
ok = 1;
goto end;
#endif
}
if (test == 2) {
#ifdef OSSL_TFO_SENDTO
struct in_addr a4;
BIO_ADDR *peer = BIO_ADDR_new();
if (!TEST_ptr(peer))
goto end;
inet_pton(AF_INET, "127.0.0.1", &a4);
BIO_ADDR_rawmake(peer, AF_INET, &a4, sizeof(a4), 443);
ret = BIO_ctrl(b, BIO_C_SET_CONNECT, 2, peer);
BIO_ADDR_free(peer);
if (!TEST_int_eq(ret, 1))
goto end;
#else
TEST_skip("OSSL_TFO_SENDTO is not defined\n");
ok = 1;
goto end;
#endif
}
if (!TEST_int_eq(BIO_set_send_flags(b, MSG_NOSIGNAL), 1))
goto end;
ret = BIO_write(b, &c, 1);
(void)ret;
/* PASS only if SIGPIPE wasn't delivered. */
if (!TEST_int_eq((int)sigpipe_seen, 0))
goto end;
ok = 1;
end:
BIO_free(b);
if (fds[0] >= 0) {
closesocket(fds[0]);
fds[0] = -1;
}
if (fds[1] >= 0) {
closesocket(fds[1]);
fds[1] = -1;
}
/* Restore previous handler. */
(void)sigaction(SIGPIPE, &oldsa, NULL);
return ok;
#else
/* No MSG_NOSIGNAL on this platform -> skip. */
TEST_skip("MSG_NOSIGNAL is not defined on this platform");
return 1;
#endif
}
int setup_tests(void)
{
ADD_ALL_TESTS(test_bio_write_triggers_sigpipe, 3);
return 1;
}
#endif