Loading...
/*
 * Copyright (c) 2019, 2025 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

/*
 * Test two cases:
 * 1) SIGPIPE is generated by the lack of MSG_NOSIGNAL
 * 2) SIGPIPE is not generated because we passed MSG_NOSIGNAL
 */

#include <darwintest.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdbool.h>
#include <unistd.h>

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif /* MSG_NOSIGNAL */

T_GLOBAL_META(
	T_META_NAMESPACE("xnu.net"),
	T_META_RADAR_COMPONENT_NAME("xnu"),
	T_META_RADAR_COMPONENT_VERSION("networking"),
	T_META_OWNER("vlubet")
	);

static bool got_sigpipe = false;

static void
sig_handler(int sig)
{
	if (sig == SIGPIPE) {
		got_sigpipe = true;
	}
}

static void
test_socket_pair(int domain, int type, bool use_msg_no_signal)
{
	int sv[2] = { -1, -1 };

	got_sigpipe = false;

	T_LOG("Testing domain: %d type: %d, use_msg_no_signal: %d",
	    domain, type, use_msg_no_signal);

	T_ASSERT_POSIX_SUCCESS(socketpair(AF_UNIX, SOCK_STREAM, 0, sv),
	    "socketpair(%d, %d, 0)", domain, type);

	/* First send should succeed */
	T_ASSERT_POSIX_SUCCESS(send(sv[0], "1", 1, 0), "send(1)");

	/* Close read end */
	close(sv[1]);

	/* Second send should fail with EPIPE */
	ssize_t ret = send(sv[0], "2", 1, use_msg_no_signal ? MSG_NOSIGNAL : 0);
	T_EXPECT_EQ(ret, (ssize_t)-1, "send(2) should fail");
	T_EXPECT_EQ(errno, EPIPE, "send(2) should fail with EPIPE");

	close(sv[0]);

	/* Verify SIGPIPE behavior */
	if (use_msg_no_signal) {
		T_EXPECT_FALSE(got_sigpipe, "MSG_NOSIGNAL should suppress SIGPIPE");
	} else {
		T_EXPECT_TRUE(got_sigpipe, "Without MSG_NOSIGNAL should generate SIGPIPE");
	}

	T_LOG("Test domain: %d type: %d, use_msg_no_signal: %d - passed",
	    domain, type, use_msg_no_signal);
}

T_DECL(test_msg_nosignal_dgram_without_flag,
    "test SIGPIPE generated without MSG_NOSIGNAL on SOCK_DGRAM",
    T_META_CHECK_LEAKS(false))
{
	T_ASSERT_NE(signal(SIGPIPE, sig_handler), SIG_ERR, "signal(SIGPIPE)");
	test_socket_pair(AF_UNIX, SOCK_DGRAM, false);
}

T_DECL(test_msg_nosignal_dgram_with_flag,
    "test SIGPIPE suppressed with MSG_NOSIGNAL on SOCK_DGRAM",
    T_META_CHECK_LEAKS(false))
{
	T_ASSERT_NE(signal(SIGPIPE, sig_handler), SIG_ERR, "signal(SIGPIPE)");
	test_socket_pair(AF_UNIX, SOCK_DGRAM, true);
}

T_DECL(test_msg_nosignal_stream_without_flag,
    "test SIGPIPE generated without MSG_NOSIGNAL on SOCK_STREAM",
    T_META_CHECK_LEAKS(false))
{
	T_ASSERT_NE(signal(SIGPIPE, sig_handler), SIG_ERR, "signal(SIGPIPE)");
	test_socket_pair(AF_UNIX, SOCK_STREAM, false);
}

T_DECL(test_msg_nosignal_stream_with_flag,
    "test SIGPIPE suppressed with MSG_NOSIGNAL on SOCK_STREAM",
    T_META_CHECK_LEAKS(false))
{
	T_ASSERT_NE(signal(SIGPIPE, sig_handler), SIG_ERR, "signal(SIGPIPE)");
	test_socket_pair(AF_UNIX, SOCK_STREAM, true);
}