[libc-commits] [libc] [libc] Fix sendfile argument order (PR #223059)
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Fri Sep 11 13:53:25 PDT 2026
https://github.com/michaelrj-google created https://github.com/llvm/llvm-project/pull/223059
The previous implementation had the arguments swapped. This PR fixes it
and also adds a test to ensure we don't regress.
>From 658a440087de7b0fb6680d39579369795885e230 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Fri, 11 Sep 2026 20:50:25 +0000
Subject: [PATCH] [libc] Fix sendfile argument order
The previous implementation had the arguments swapped. This PR fixes it
and also adds a test to ensure we don't regress.
---
libc/src/sys/sendfile/linux/sendfile.cpp | 8 +-
libc/test/src/sys/sendfile/CMakeLists.txt | 1 +
libc/test/src/sys/sendfile/sendfile_test.cpp | 78 +++++++++++++++++++-
3 files changed, 82 insertions(+), 5 deletions(-)
diff --git a/libc/src/sys/sendfile/linux/sendfile.cpp b/libc/src/sys/sendfile/linux/sendfile.cpp
index fffbc97495635..f7321c3eff305 100644
--- a/libc/src/sys/sendfile/linux/sendfile.cpp
+++ b/libc/src/sys/sendfile/linux/sendfile.cpp
@@ -20,13 +20,13 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(ssize_t, sendfile,
(int out_fd, int in_fd, off_t *offset, size_t count)) {
#ifdef SYS_sendfile
- ssize_t ret = LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_sendfile, in_fd,
- out_fd, offset, count);
+ ssize_t ret = LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_sendfile, out_fd,
+ in_fd, offset, count);
#elif defined(SYS_sendfile64)
// Same as sendfile but can handle large offsets
static_assert(sizeof(off_t) == 8);
- ssize_t ret = LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_sendfile64, in_fd,
- out_fd, offset, count);
+ ssize_t ret = LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_sendfile64, out_fd,
+ in_fd, offset, count);
#else
#error "sendfile and sendfile64 syscalls not available."
#endif
diff --git a/libc/test/src/sys/sendfile/CMakeLists.txt b/libc/test/src/sys/sendfile/CMakeLists.txt
index 4fbf436596021..18b792b2333eb 100644
--- a/libc/test/src/sys/sendfile/CMakeLists.txt
+++ b/libc/test/src/sys/sendfile/CMakeLists.txt
@@ -9,6 +9,7 @@ add_libc_test(
SRCS
sendfile_test.cpp
DEPENDS
+ libc.hdr.errno_macros
libc.hdr.fcntl_macros
libc.hdr.sys_stat_macros
libc.src.errno.errno
diff --git a/libc/test/src/sys/sendfile/sendfile_test.cpp b/libc/test/src/sys/sendfile/sendfile_test.cpp
index 7eea3eeca23ef..c018bde5022d1 100644
--- a/libc/test/src/sys/sendfile/sendfile_test.cpp
+++ b/libc/test/src/sys/sendfile/sendfile_test.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
#include "hdr/fcntl_macros.h"
#include "hdr/sys_stat_macros.h"
#include "src/__support/CPP/string_view.h"
@@ -46,7 +47,7 @@ TEST_F(LlvmLibcSendfileTest, CreateAndTransfer) {
int out_fd = LIBC_NAMESPACE::open(OUT_FILE, O_CREAT | O_WRONLY, S_IRWXU);
ASSERT_GT(out_fd, 0);
ASSERT_ERRNO_SUCCESS();
- ssize_t size = LIBC_NAMESPACE::sendfile(in_fd, out_fd, nullptr, IN_SIZE);
+ ssize_t size = LIBC_NAMESPACE::sendfile(out_fd, in_fd, nullptr, IN_SIZE);
ASSERT_EQ(size, IN_SIZE);
ASSERT_THAT(LIBC_NAMESPACE::close(in_fd), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(out_fd), Succeeds(0));
@@ -61,3 +62,78 @@ TEST_F(LlvmLibcSendfileTest, CreateAndTransfer) {
ASSERT_THAT(LIBC_NAMESPACE::unlink(IN_FILE), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::unlink(OUT_FILE), Succeeds(0));
}
+
+TEST_F(LlvmLibcSendfileTest, WrongDirectionFails) {
+ constexpr const char *IN_FILE = "testdata/sendfile_wrong_in.test";
+ constexpr const char *OUT_FILE = "testdata/sendfile_wrong_out.test";
+ const char IN_DATA[] = "test data";
+ constexpr ssize_t IN_SIZE = ssize_t(sizeof(IN_DATA));
+
+ int in_fd = LIBC_NAMESPACE::open(IN_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(in_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(LIBC_NAMESPACE::write(in_fd, IN_DATA, IN_SIZE), IN_SIZE);
+ ASSERT_THAT(LIBC_NAMESPACE::close(in_fd), Succeeds(0));
+
+ // in_fd is read-only, out_fd is write-only.
+ in_fd = LIBC_NAMESPACE::open(IN_FILE, O_RDONLY);
+ ASSERT_GT(in_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ int out_fd = LIBC_NAMESPACE::open(OUT_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(out_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+
+ // Swapping the arguments (passing in_fd as out_fd and out_fd as in_fd) must
+ // fail with EBADF because in_fd cannot be written to and out_fd cannot be
+ // read from.
+ ASSERT_THAT(LIBC_NAMESPACE::sendfile(in_fd, out_fd, nullptr, IN_SIZE),
+ Fails<ssize_t>(EBADF));
+
+ ASSERT_THAT(LIBC_NAMESPACE::close(in_fd), Succeeds(0));
+ ASSERT_THAT(LIBC_NAMESPACE::close(out_fd), Succeeds(0));
+
+ ASSERT_THAT(LIBC_NAMESPACE::unlink(IN_FILE), Succeeds(0));
+ ASSERT_THAT(LIBC_NAMESPACE::unlink(OUT_FILE), Succeeds(0));
+}
+
+TEST_F(LlvmLibcSendfileTest, OffsetTransfer) {
+ constexpr const char *IN_FILE = "testdata/sendfile_offset_in.test";
+ constexpr const char *OUT_FILE = "testdata/sendfile_offset_out.test";
+ const char IN_DATA[] = "sendfile offset test";
+ constexpr ssize_t IN_SIZE = ssize_t(sizeof(IN_DATA));
+ constexpr off_t OFFSET = 9;
+ constexpr ssize_t TRANSFER_SIZE = IN_SIZE - OFFSET;
+
+ int in_fd = LIBC_NAMESPACE::open(IN_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(in_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(LIBC_NAMESPACE::write(in_fd, IN_DATA, IN_SIZE), IN_SIZE);
+ ASSERT_THAT(LIBC_NAMESPACE::close(in_fd), Succeeds(0));
+
+ in_fd = LIBC_NAMESPACE::open(IN_FILE, O_RDONLY);
+ ASSERT_GT(in_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ int out_fd = LIBC_NAMESPACE::open(OUT_FILE, O_CREAT | O_WRONLY, S_IRWXU);
+ ASSERT_GT(out_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+
+ off_t offset = OFFSET;
+ ssize_t size =
+ LIBC_NAMESPACE::sendfile(out_fd, in_fd, &offset, TRANSFER_SIZE);
+ ASSERT_EQ(size, TRANSFER_SIZE);
+ ASSERT_EQ(offset, off_t(OFFSET + TRANSFER_SIZE));
+ ASSERT_THAT(LIBC_NAMESPACE::close(in_fd), Succeeds(0));
+ ASSERT_THAT(LIBC_NAMESPACE::close(out_fd), Succeeds(0));
+
+ out_fd = LIBC_NAMESPACE::open(OUT_FILE, O_RDONLY);
+ ASSERT_GT(out_fd, 0);
+ ASSERT_ERRNO_SUCCESS();
+ char buf[TRANSFER_SIZE];
+ ASSERT_EQ(TRANSFER_SIZE, LIBC_NAMESPACE::read(out_fd, buf, TRANSFER_SIZE));
+ ASSERT_EQ(cpp::string_view(buf, TRANSFER_SIZE),
+ cpp::string_view(IN_DATA + OFFSET, TRANSFER_SIZE));
+ ASSERT_THAT(LIBC_NAMESPACE::close(out_fd), Succeeds(0));
+
+ ASSERT_THAT(LIBC_NAMESPACE::unlink(IN_FILE), Succeeds(0));
+ ASSERT_THAT(LIBC_NAMESPACE::unlink(OUT_FILE), Succeeds(0));
+}
More information about the libc-commits
mailing list