[libc] Add the MSG_ flags (#194375)

I've included all of the flags defined on linux, even those not
documented in POSIX (or the manpage). I've also added tests to exercise
the flags, for cases where this can be done with domain sockets.
This commit is contained in:
Pavel Labath
2026-04-28 13:24:58 +02:00
committed by GitHub
parent 0012deaa1b
commit 412d474f5e
4 changed files with 136 additions and 6 deletions

View File

@@ -52,6 +52,29 @@
#define SCM_RIGHTS 1
#define MSG_OOB 0x01
#define MSG_PEEK 0x02
#define MSG_DONTROUTE 0x04
#define MSG_CTRUNC 0x08
#define MSG_PROXY 0x10
#define MSG_TRUNC 0x20
#define MSG_DONTWAIT 0x40
#define MSG_EOR 0x80
#define MSG_WAITALL 0x100
#define MSG_FIN 0x200
#define MSG_SYN 0x400
#define MSG_CONFIRM 0x800
#define MSG_RST 0x1000
#define MSG_ERRQUEUE 0x2000
#define MSG_NOSIGNAL 0x4000
#define MSG_MORE 0x8000
#define MSG_WAITFORONE 0x10000
#define MSG_BATCH 0x40000
#define MSG_SOCK_DEVMEM 0x2000000
#define MSG_ZEROCOPY 0x4000000
#define MSG_FASTOPEN 0x20000000
#define MSG_CMSG_CLOEXEC 0x40000000
#define CMSG_ALIGN(len) (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1))
#define CMSG_LEN(len) (sizeof(struct cmsghdr) + (len))
#define CMSG_SPACE(len) (sizeof(struct cmsghdr) + CMSG_ALIGN(len))

View File

@@ -125,7 +125,7 @@ add_libc_unittest(
SRCS
send_recv_test.cpp
DEPENDS
libc.include.sys_socket
libc.hdr.sys_socket_macros
libc.src.errno.errno
libc.src.sys.socket.socketpair
libc.src.sys.socket.send
@@ -159,9 +159,11 @@ add_libc_unittest(
SRCS
sendmsg_recvmsg_test.cpp
DEPENDS
libc.include.sys_socket
libc.hdr.fcntl_macros
libc.hdr.sys_socket_macros
libc.hdr.types.struct_cmsghdr
libc.src.errno.errno
libc.src.fcntl.fcntl
libc.src.string.memcpy
libc.src.string.memset
libc.src.sys.socket.getsockopt

View File

@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "hdr/sys_socket_macros.h"
#include "src/sys/socket/recv.h"
#include "src/sys/socket/send.h"
#include "src/sys/socket/socketpair.h"
@@ -16,8 +17,6 @@
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
#include <sys/socket.h> // For AF_UNIX and SOCK_DGRAM
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcSendRecvTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
@@ -46,6 +45,47 @@ TEST_F(LlvmLibcSendRecvTest, SucceedsWithSocketPair) {
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
}
TEST_F(LlvmLibcSendRecvTest, MsgFlagsTest) {
int sockpair[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockpair),
Succeeds(0));
char buffer[256] = {};
// MSG_DONTWAIT on an empty socket returns EAGAIN
ASSERT_THAT(
LIBC_NAMESPACE::recv(sockpair[1], buffer, sizeof(buffer), MSG_DONTWAIT),
Fails<ssize_t>(EAGAIN));
const char TEST_MESSAGE[] = "this is a long message";
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
ASSERT_THAT(LIBC_NAMESPACE::send(sockpair[0], TEST_MESSAGE, MESSAGE_LEN, 0),
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
// MSG_PEEK does not remove the message from the socket
ASSERT_THAT(
LIBC_NAMESPACE::recv(sockpair[1], buffer, sizeof(buffer), MSG_PEEK),
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
ASSERT_STREQ(buffer, TEST_MESSAGE);
// Read the message again, but use a smaller buffer to test MSG_TRUNC. Return
// value should be real length.
char small_buffer[6] = {};
ASSERT_THAT(LIBC_NAMESPACE::recv(sockpair[1], small_buffer,
sizeof(small_buffer) - 1, MSG_TRUNC),
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
ASSERT_STREQ(small_buffer, "this ");
// Sending with MSG_NOSIGNAL to a closed socket should fail with EPIPE
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::send(sockpair[0], "x", 1, MSG_NOSIGNAL),
Fails<ssize_t>(EPIPE));
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
}
TEST_F(LlvmLibcSendRecvTest, SendFails) {
const char TEST_MESSAGE[] = "connection terminated";
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);

View File

@@ -6,8 +6,10 @@
//
//===----------------------------------------------------------------------===//
#include "hdr/fcntl_macros.h"
#include "hdr/sys_socket_macros.h"
#include "hdr/types/struct_cmsghdr.h"
#include "src/fcntl/fcntl.h"
#include "src/string/memcpy.h"
#include "src/string/memset.h"
#include "src/sys/socket/getsockopt.h"
@@ -22,8 +24,6 @@
#include "test/UnitTest/LibcTest.h"
#include "test/UnitTest/Test.h"
#include <sys/socket.h> // For AF_UNIX and SOCK_DGRAM
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcSendMsgRecvMsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
@@ -201,6 +201,71 @@ TEST_F(LlvmLibcSendMsgRecvMsgTest, SendAndReceiveFileDescriptor) {
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, MsgCmsgCloexec) {
int sockpair[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair),
Succeeds(0));
struct iovec iov;
iov.iov_base = reinterpret_cast<void *>(const_cast<char *>("x"));
iov.iov_len = 1;
char control_buf[CMSG_SPACE(sizeof(int))] = {};
struct msghdr msg;
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = control_buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
LIBC_NAMESPACE::memcpy(CMSG_DATA(cmsg), sockpair + 1, sizeof(int));
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(sockpair[0], &msg, 0),
Succeeds(static_cast<ssize_t>(1)));
char buffer[256];
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
LIBC_NAMESPACE::memset(control_buf, 0, sizeof(control_buf));
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buf;
msg.msg_controllen = sizeof(control_buf);
msg.msg_flags = 0;
// Receive with MSG_CMSG_CLOEXEC
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &msg, MSG_CMSG_CLOEXEC),
Succeeds(static_cast<ssize_t>(1)));
cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_TRUE(cmsg != nullptr);
int new_fd;
LIBC_NAMESPACE::memcpy(&new_fd, CMSG_DATA(cmsg), sizeof(int));
// Check FD_CLOEXEC
int flags = LIBC_NAMESPACE::fcntl(new_fd, F_GETFD);
ASSERT_GE(flags, 0);
ASSERT_NE(flags & FD_CLOEXEC, 0);
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, SendFails) {
const char TEST_MESSAGE[] = "connection terminated";
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);