Skip to content

Commit c715056

Browse files
authored
implement preadv and pwritev syscalls (#1058)
* implement preadv and pwritev syscalls Adds preadv/pwritev support (syscall 295/296) following the same pattern as the existing readv/writev and pread/pwrite implementations. glibc: replace SYSCALL_CANCEL with MAKE_LEGACY_SYSCALL in preadv64.c and pwritev64.c, using __lind_translate_iov to translate iov_base pointers from guest to host addresses. rawposix: add preadv_syscall and pwritev_syscall handlers. test: preadv_pwritev.c covers scatter/gather at offset 0 and non-zero offset, and verifies the file pointer is not modified. * debug: print pwritev error * debug: always print pwritev return value * debug: compare writev vs pwritev * debug: try raw syscall for preadv/pwritev * debug: print raw args in pwritev handler * fix preadv/pwritev: update non-64 glibc wrappers The 32-bit preadv.c and pwritev.c (used when __OFF_T_MATCHES_OFF64_T is not defined, which is the case on wasm32) were still using SYSCALL_CANCEL which bypasses 3i. Only the 64-bit variants had been updated to use MAKE_LEGACY_SYSCALL. Also reverts debug prints and raw syscall experiments. * implement preadv and pwritev syscalls Adds preadv/pwritev support (syscall 295/296) following the same pattern as the existing readv/writev and pread/pwrite implementations. glibc: replace SYSCALL_CANCEL with MAKE_LEGACY_SYSCALL in preadv.c and pwritev.c (the non-64 versions, which are what wasm32 uses since __OFF_T_MATCHES_OFF64_T is not defined). Uses __lind_translate_iov to translate iov_base pointers from guest to host addresses. rawposix: add preadv_syscall and pwritev_syscall handlers. test: preadv_pwritev.c covers scatter/gather at offset 0 and non-zero offset, and verifies the file pointer is not modified.
1 parent 0c4c82a commit c715056

File tree

7 files changed

+206
-47
lines changed

7 files changed

+206
-47
lines changed

src/glibc/lind_syscall/lind_syscall_num.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@
119119
#define EPOLL_CREATE1_SYSCALL 291
120120
#define DUP3_SYSCALL 292
121121
#define PIPE2_SYSCALL 293
122+
#define PREADV_SYSCALL 295
123+
#define PWRITEV_SYSCALL 296
122124
#define PRLIMIT64_SYSCALL 302
123125
#define GETRANDOM_SYSCALL 318
124126

src/glibc/sysdeps/unix/sysv/linux/preadv.c

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,23 @@
1717

1818
#include <sys/uio.h>
1919
#include <sysdep-cancel.h>
20+
#include <syscall-template.h>
21+
#include <lind_syscall_num.h>
22+
#include <addr_translation.h>
2023

2124
#ifndef __OFF_T_MATCHES_OFF64_T
2225

23-
# ifdef __ASSUME_PREADV
24-
25-
ssize_t
26-
preadv (int fd, const struct iovec *vector, int count, off_t offset)
27-
{
28-
return SYSCALL_CANCEL (preadv, fd, vector, count, LO_HI_LONG (offset));
29-
}
30-
# else
31-
static ssize_t __atomic_preadv_replacement (int, const struct iovec *,
32-
int, off_t);
3326
ssize_t
3427
preadv (int fd, const struct iovec *vector, int count, off_t offset)
3528
{
36-
ssize_t result = SYSCALL_CANCEL (preadv, fd, vector, count,
37-
LO_HI_LONG (offset));
38-
if (result >= 0 || errno != ENOSYS)
39-
return result;
40-
return __atomic_preadv_replacement (fd, vector, count, offset);
29+
struct iovec host_iov[count];
30+
__lind_translate_iov (vector, host_iov, count);
31+
32+
return MAKE_LEGACY_SYSCALL (PREADV_SYSCALL, "syscall|preadv",
33+
(uint64_t) fd,
34+
(uint64_t) TRANSLATE_GUEST_POINTER_TO_HOST ((uintptr_t) host_iov),
35+
(uint64_t) count,
36+
(uint64_t) offset, NOTUSED, NOTUSED, TRANSLATE_ERRNO_ON);
4137
}
42-
# define PREADV static __atomic_preadv_replacement
43-
# define PREAD __pread
44-
# define OFF_T off_t
45-
# include <sysdeps/posix/preadv_common.c>
46-
# endif /* __ASSUME_PREADV */
47-
4838
libc_hidden_def (preadv)
4939
#endif

src/glibc/sysdeps/unix/sysv/linux/pwritev.c

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,23 @@
1717

1818
#include <sys/uio.h>
1919
#include <sysdep-cancel.h>
20+
#include <syscall-template.h>
21+
#include <lind_syscall_num.h>
22+
#include <addr_translation.h>
2023

2124
#ifndef __OFF_T_MATCHES_OFF64_T
2225

23-
# ifdef __ASSUME_PREADV
24-
25-
ssize_t
26-
pwritev (int fd, const struct iovec *vector, int count, off_t offset)
27-
{
28-
return SYSCALL_CANCEL (pwritev, fd, vector, count, LO_HI_LONG (offset));
29-
}
30-
# else
31-
static ssize_t __atomic_pwritev_replacement (int, const struct iovec *,
32-
int, off_t);
3326
ssize_t
3427
pwritev (int fd, const struct iovec *vector, int count, off_t offset)
3528
{
36-
ssize_t result = SYSCALL_CANCEL (pwritev, fd, vector, count,
37-
LO_HI_LONG (offset));
38-
if (result >= 0 || errno != ENOSYS)
39-
return result;
40-
return __atomic_pwritev_replacement (fd, vector, count, offset);
29+
struct iovec host_iov[count];
30+
__lind_translate_iov (vector, host_iov, count);
31+
32+
return MAKE_LEGACY_SYSCALL (PWRITEV_SYSCALL, "syscall|pwritev",
33+
(uint64_t) fd,
34+
(uint64_t) TRANSLATE_GUEST_POINTER_TO_HOST ((uintptr_t) host_iov),
35+
(uint64_t) count,
36+
(uint64_t) offset, NOTUSED, NOTUSED, TRANSLATE_ERRNO_ON);
4137
}
42-
# define PWRITEV static __atomic_pwritev_replacement
43-
# define PWRITE __pwrite
44-
# define OFF_T off_t
45-
# include <sysdeps/posix/pwritev_common.c>
46-
# endif /* __ASSUME_PREADV */
47-
4838
libc_hidden_def (pwritev)
4939
#endif

src/rawposix/src/fs_calls.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2767,6 +2767,112 @@ pub extern "C" fn readv_syscall(
27672767
ret
27682768
}
27692769

2770+
/// Reference to Linux: https://man7.org/linux/man-pages/man2/preadv.2.html
2771+
///
2772+
/// `preadv()` combines the functionality of `readv()` and `pread()`: it performs
2773+
/// scatter input from the file at the given offset without changing the file pointer.
2774+
///
2775+
/// ## Arguments:
2776+
/// - vfd_arg: virtual file descriptor
2777+
/// - iov_arg: pointer to an array of iovec structures
2778+
/// - iovcnt_arg: number of iovec structures
2779+
/// - offset_arg: file offset to read from
2780+
///
2781+
/// ## Returns:
2782+
/// - On success, the number of bytes read.
2783+
/// - On error, -1 with errno set.
2784+
pub extern "C" fn preadv_syscall(
2785+
cageid: u64,
2786+
vfd_arg: u64,
2787+
vfd_cageid: u64,
2788+
iov_arg: u64,
2789+
iov_cageid: u64,
2790+
iovcnt_arg: u64,
2791+
iovcnt_cageid: u64,
2792+
offset_arg: u64,
2793+
offset_cageid: u64,
2794+
arg5: u64,
2795+
arg5_cageid: u64,
2796+
arg6: u64,
2797+
arg6_cageid: u64,
2798+
) -> i32 {
2799+
let kernel_fd = convert_fd_to_host(vfd_arg, vfd_cageid, cageid);
2800+
if kernel_fd < 0 {
2801+
return handle_errno(-kernel_fd, "preadv");
2802+
}
2803+
2804+
let iovcnt = sc_convert_sysarg_to_i32(iovcnt_arg, iovcnt_cageid, cageid);
2805+
let iov_ptr = sc_convert_buf(iov_arg, iov_cageid, cageid);
2806+
let offset = sc_convert_sysarg_to_i64(offset_arg, offset_cageid, cageid);
2807+
2808+
if !(sc_unusedarg(arg5, arg5_cageid) && sc_unusedarg(arg6, arg6_cageid)) {
2809+
panic!(
2810+
"{}: unused arguments contain unexpected values -- security violation",
2811+
"preadv_syscall"
2812+
);
2813+
}
2814+
2815+
let ret =
2816+
unsafe { libc::preadv(kernel_fd, iov_ptr as *const libc::iovec, iovcnt, offset) as i32 };
2817+
if ret < 0 {
2818+
return handle_errno(get_errno(), "preadv");
2819+
}
2820+
ret
2821+
}
2822+
2823+
/// Reference to Linux: https://man7.org/linux/man-pages/man2/pwritev.2.html
2824+
///
2825+
/// `pwritev()` combines the functionality of `writev()` and `pwrite()`: it performs
2826+
/// gather output to the file at the given offset without changing the file pointer.
2827+
///
2828+
/// ## Arguments:
2829+
/// - vfd_arg: virtual file descriptor
2830+
/// - iov_arg: pointer to an array of iovec structures
2831+
/// - iovcnt_arg: number of iovec structures
2832+
/// - offset_arg: file offset to write at
2833+
///
2834+
/// ## Returns:
2835+
/// - On success, the number of bytes written.
2836+
/// - On error, -1 with errno set.
2837+
pub extern "C" fn pwritev_syscall(
2838+
cageid: u64,
2839+
vfd_arg: u64,
2840+
vfd_cageid: u64,
2841+
iov_arg: u64,
2842+
iov_cageid: u64,
2843+
iovcnt_arg: u64,
2844+
iovcnt_cageid: u64,
2845+
offset_arg: u64,
2846+
offset_cageid: u64,
2847+
arg5: u64,
2848+
arg5_cageid: u64,
2849+
arg6: u64,
2850+
arg6_cageid: u64,
2851+
) -> i32 {
2852+
let kernel_fd = convert_fd_to_host(vfd_arg, vfd_cageid, cageid);
2853+
if kernel_fd < 0 {
2854+
return handle_errno(-kernel_fd, "pwritev");
2855+
}
2856+
2857+
let iovcnt = sc_convert_sysarg_to_i32(iovcnt_arg, iovcnt_cageid, cageid);
2858+
let iov_ptr = sc_convert_buf(iov_arg, iov_cageid, cageid);
2859+
let offset = sc_convert_sysarg_to_i64(offset_arg, offset_cageid, cageid);
2860+
2861+
if !(sc_unusedarg(arg5, arg5_cageid) && sc_unusedarg(arg6, arg6_cageid)) {
2862+
panic!(
2863+
"{}: unused arguments contain unexpected values -- security violation",
2864+
"pwritev_syscall"
2865+
);
2866+
}
2867+
2868+
let ret =
2869+
unsafe { libc::pwritev(kernel_fd, iov_ptr as *const libc::iovec, iovcnt, offset) as i32 };
2870+
if ret < 0 {
2871+
return handle_errno(get_errno(), "pwritev");
2872+
}
2873+
ret
2874+
}
2875+
27702876
/// Reference to Linux: https://man7.org/linux/man-pages/man2/fstat.2.html
27712877
///
27722878
/// Linux `fstat()` syscall retrieves information about the file referred to by the open file descriptor `fd`.

src/rawposix/src/syscall_table.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ use super::fs_calls::{
1010
ftruncate_syscall, futex_syscall, getcwd_syscall, getdents_syscall, getrandom_syscall,
1111
ioctl_syscall, link_syscall, lseek_syscall, mkdir_syscall, mknod_syscall, mmap_syscall,
1212
mprotect_syscall, munmap_syscall, nanosleep_time64_syscall, open_syscall, openat_syscall,
13-
pipe2_syscall, pipe_syscall, pread_syscall, pwrite_syscall, read_syscall, readlink_syscall,
14-
readlinkat_syscall, readv_syscall, rename_syscall, rmdir_syscall, shmat_syscall,
15-
shmctl_syscall, shmdt_syscall, shmget_syscall, stat_syscall, statfs_syscall,
16-
sync_file_range_syscall, truncate_syscall, unlink_syscall, unlinkat_syscall, write_syscall,
17-
writev_syscall,
13+
pipe2_syscall, pipe_syscall, pread_syscall, preadv_syscall, pwrite_syscall, pwritev_syscall,
14+
read_syscall, readlink_syscall, readlinkat_syscall, readv_syscall, rename_syscall,
15+
rmdir_syscall, shmat_syscall, shmctl_syscall, shmdt_syscall, shmget_syscall, stat_syscall,
16+
statfs_syscall, sync_file_range_syscall, truncate_syscall, unlink_syscall, unlinkat_syscall,
17+
write_syscall, writev_syscall,
1818
};
1919
use super::init::RawCallFunc;
2020
use super::net_calls::{
@@ -150,6 +150,8 @@ pub const SYSCALL_TABLE: &[(u64, RawCallFunc)] = &[
150150
sync_file_range_syscall,
151151
),
152152
(syscall_const::ACCEPT4_SYSCALL as u64, accept4_syscall),
153+
(syscall_const::PREADV_SYSCALL as u64, preadv_syscall),
154+
(syscall_const::PWRITEV_SYSCALL as u64, pwritev_syscall),
153155
(
154156
syscall_const::EPOLL_CREATE1_SYSCALL as u64,
155157
epoll_create1_syscall,

src/sysdefs/src/constants/syscall_const.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ pub const UNLINKAT_SYSCALL: i32 = 263;
9797
pub const READLINKAT_SYSCALL: i32 = 267;
9898
pub const SYNC_FILE_RANGE_SYSCALL: i32 = 277;
9999
pub const ACCEPT4_SYSCALL: i32 = 288;
100+
pub const PREADV_SYSCALL: i32 = 295;
101+
pub const PWRITEV_SYSCALL: i32 = 296;
100102
pub const PRLIMIT64_SYSCALL: i32 = 302;
101103
pub const EPOLL_CREATE1_SYSCALL: i32 = 291;
102104
pub const DUP3_SYSCALL: i32 = 292;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <assert.h>
5+
#include <errno.h>
6+
#include <fcntl.h>
7+
#include <unistd.h>
8+
#include <sys/uio.h>
9+
10+
int main(void) {
11+
const char *path = "testfiles/preadv_pwritev_test.txt";
12+
int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
13+
assert(fd >= 0);
14+
15+
/* pwritev: write two buffers at offset 0 */
16+
char buf1[] = "hello ";
17+
char buf2[] = "world";
18+
struct iovec wv[2];
19+
wv[0].iov_base = buf1;
20+
wv[0].iov_len = strlen(buf1);
21+
wv[1].iov_base = buf2;
22+
wv[1].iov_len = strlen(buf2);
23+
24+
ssize_t nw = pwritev(fd, wv, 2, 0);
25+
assert(nw == (ssize_t)(strlen(buf1) + strlen(buf2)));
26+
27+
/* preadv: read back into two buffers at offset 0 */
28+
char rbuf1[6] = {0};
29+
char rbuf2[5] = {0};
30+
struct iovec rv[2];
31+
rv[0].iov_base = rbuf1;
32+
rv[0].iov_len = sizeof(rbuf1);
33+
rv[1].iov_base = rbuf2;
34+
rv[1].iov_len = sizeof(rbuf2);
35+
36+
ssize_t nr = preadv(fd, rv, 2, 0);
37+
assert(nr == 11);
38+
assert(memcmp(rbuf1, "hello ", 6) == 0);
39+
assert(memcmp(rbuf2, "world", 5) == 0);
40+
41+
/* pwritev at non-zero offset */
42+
char buf3[] = "LIND";
43+
struct iovec wv2;
44+
wv2.iov_base = buf3;
45+
wv2.iov_len = strlen(buf3);
46+
47+
nw = pwritev(fd, &wv2, 1, 6);
48+
assert(nw == 4);
49+
50+
/* verify file pointer unchanged by preadv/pwritev */
51+
off_t pos = lseek(fd, 0, SEEK_CUR);
52+
assert(pos == 0);
53+
54+
/* read full contents to verify */
55+
char full[12] = {0};
56+
struct iovec fv;
57+
fv.iov_base = full;
58+
fv.iov_len = 11;
59+
nr = preadv(fd, &fv, 1, 0);
60+
assert(nr == 11);
61+
assert(memcmp(full, "hello LINDd", 11) == 0);
62+
63+
close(fd);
64+
unlink(path);
65+
printf("preadv_pwritev: all tests passed\n");
66+
return 0;
67+
}

0 commit comments

Comments
 (0)