Skip to content
Open
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
2 changes: 1 addition & 1 deletion scripts/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ expect_success "umask-test" \
echo ""
echo "--- Guest test programs ---"

for test_prog in dup-test clock-test signal-test signal-safety-test path-escape-test errno-test; do
for test_prog in dup-test clock-test signal-test signal-safety-test path-escape-test errno-test sendfile-test; do
if guest_has_test "$test_prog"; then
expect_success "$test_prog" \
"$KBOX" -S "$ROOTFS" -- "/opt/tests/${test_prog}"
Expand Down
23 changes: 22 additions & 1 deletion src/seccomp-dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -2423,6 +2423,17 @@ static struct kbox_dispatch forward_sendfile(
if (vfd >= 0)
out_lkl = kbox_fd_table_get_lkl(ctx->fd_table, vfd);
}
/* If out_fd carries a writeback shadow, direct writes must go to
* shadow_sp so that the close-time sync sees the new data.
*/
int out_shadow_sp = -1;
{
struct kbox_fd_entry *out_entry = fd_table_entry(ctx->fd_table, out_fd);
if (out_entry && out_entry->shadow_writeback &&
out_entry->shadow_sp >= 0)
out_shadow_sp = out_entry->shadow_sp;
}


/* Both FDs have no LKL backing: the host kernel handles sendfile
* if both are known host FDs. Deny if either is in a denied range.
Expand Down Expand Up @@ -2494,7 +2505,17 @@ static struct kbox_dispatch forward_sendfile(
/* Write to destination, looping on short writes. */
size_t written = 0;
while (written < n) {
if (out_lkl >= 0) {
if (out_shadow_sp >= 0) {
ssize_t wr =
write(out_shadow_sp, scratch + written, n - written);
if (wr <= 0) {
if (total + written == 0)
return kbox_dispatch_errno(wr < 0 ? errno : EIO);
total += written;
goto done;
}
written += (size_t) wr;
} else if (out_lkl >= 0) {
long wr =
kbox_lkl_write(ctx->sysnrs, out_lkl, scratch + written,
(long) (n - written));
Expand Down
91 changes: 91 additions & 0 deletions tests/guest/sendfile-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: MIT */
/* Guest test: verify sendfile with a shadow-memfd input FD.
* sendfile must resolve the host fd through that shadow path via
* find_by_host_fd(); this test confirms data integrity end-to-end.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sendfile.h>
#include <unistd.h>

#define TEST_DATA "kbox shadow sendfile integration test data"
#define TEST_FILE "/opt/sendfile_test_data.txt"
#define OUT_FILE "/opt/sendfile_out_test.txt"

#define CHECK(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL: %s (errno: %d - %s)\n", msg, errno, \
strerror(errno)); \
exit(1); \
} \
} while (0)

int main(void)
{
size_t test_len = strlen(TEST_DATA);

/* Create input file in LKL rootfs with test data. */
int setup_fd =
open(TEST_FILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644);
CHECK(setup_fd >= 0, "create input file");
CHECK(write(setup_fd, TEST_DATA, test_len) == (ssize_t) test_len,
"write test data to input file");
close(setup_fd);

/* Open O_RDONLY to trigger shadow memfd creation; sendfile must resolve
* the LKL fd via find_by_host_fd() through that shadow path. */
int in_fd = open(TEST_FILE, O_RDONLY | O_CLOEXEC);
CHECK(in_fd >= 0, "open input file as O_RDONLY (creates shadow memfd)");

/* Regular file output — pipes are not valid sendfile targets (EINVAL). */
int out_fd = open(OUT_FILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644);
CHECK(out_fd >= 0, "create regular output file for sendfile target");

size_t remaining = test_len;
size_t total_sent = 0;
while (remaining > 0) {
ssize_t sent = sendfile(out_fd, in_fd, NULL, remaining);
CHECK(sent >= 0, "sendfile from shadow in_fd to regular out_fd");
if (sent == 0)
break;
total_sent += (size_t) sent;
remaining -= (size_t) sent;
}

CHECK(total_sent == test_len, "transferred all data via sendfile");
close(out_fd);

/* Verify content written by sendfile. */
int verify_fd = open(OUT_FILE, O_RDONLY | O_CLOEXEC);
CHECK(verify_fd >= 0, "re-open output file for verification");

char verify_buf[256] = {0};
size_t file_received = 0;
while (file_received < total_sent) {
ssize_t nread = read(verify_fd, verify_buf + file_received,
total_sent - file_received);
CHECK(nread >= 0, "read from output file");
if (nread == 0)
break;
file_received += (size_t) nread;
}

CHECK(file_received == total_sent, "received all data from output file");
verify_buf[file_received] = '\0';
CHECK(strcmp(verify_buf, TEST_DATA) == 0, "data matches TEST_DATA");

close(verify_fd);
close(in_fd);
unlink(TEST_FILE);
unlink(OUT_FILE);

printf("PASS: sendfile_test\n");
return 0;
}
Loading