-
Notifications
You must be signed in to change notification settings - Fork 16
Implement process_vm_readv and process_vm_writev #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
doanbaotrung
wants to merge
1
commit into
sysprog21:main
Choose a base branch
from
open-sources-port:SYS_process_vm
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1730,6 +1730,165 @@ int64_t sys_pwritev2(guest_t *g, | |
| return r; | ||
| } | ||
|
|
||
| static int64_t process_vm_import_iov(guest_t *g, | ||
| uint64_t iov_gva, | ||
| uint64_t iovcnt, | ||
| linux_iovec_t **iov_out) | ||
| { | ||
| *iov_out = NULL; | ||
|
|
||
| if (iovcnt > SYSCALL_IOV_MAX) | ||
| return -LINUX_EINVAL; | ||
| if (iovcnt == 0) | ||
| return 0; | ||
| if (iovcnt > SIZE_MAX / sizeof(linux_iovec_t)) | ||
| return -LINUX_EINVAL; | ||
|
|
||
| size_t bytes = (size_t) iovcnt * sizeof(linux_iovec_t); | ||
| linux_iovec_t *iov = malloc(bytes); | ||
| if (!iov) | ||
| return -LINUX_ENOMEM; | ||
| if (guest_read(g, iov_gva, iov, bytes) < 0) { | ||
| free(iov); | ||
| return -LINUX_EFAULT; | ||
| } | ||
|
|
||
| uint64_t total = 0; | ||
| for (uint64_t i = 0; i < iovcnt; i++) { | ||
| if (iov[i].iov_len > (uint64_t) SSIZE_MAX || | ||
| total > (uint64_t) SSIZE_MAX - iov[i].iov_len) { | ||
| free(iov); | ||
| return -LINUX_EINVAL; | ||
| } | ||
| total += iov[i].iov_len; | ||
| } | ||
|
|
||
| *iov_out = iov; | ||
| return 0; | ||
| } | ||
|
|
||
| static void process_vm_advance_iov(linux_iovec_t *iov, | ||
| uint64_t iovcnt, | ||
| uint64_t *idx, | ||
| uint64_t *off) | ||
| { | ||
| while (*idx < iovcnt && *off >= iov[*idx].iov_len) { | ||
| *off = 0; | ||
| (*idx)++; | ||
| } | ||
| } | ||
|
|
||
| static int64_t process_vm_copy(guest_t *g, | ||
| linux_iovec_t *local_iov, | ||
| uint64_t local_iovcnt, | ||
| linux_iovec_t *remote_iov, | ||
| uint64_t remote_iovcnt, | ||
| bool write_remote) | ||
| { | ||
| uint64_t li = 0, ri = 0, lo = 0, ro = 0; | ||
| uint64_t copied = 0; | ||
|
|
||
| for (;;) { | ||
| process_vm_advance_iov(local_iov, local_iovcnt, &li, &lo); | ||
| process_vm_advance_iov(remote_iov, remote_iovcnt, &ri, &ro); | ||
| if (li >= local_iovcnt || ri >= remote_iovcnt) | ||
| return (int64_t) copied; | ||
|
|
||
| uint64_t local_left = local_iov[li].iov_len - lo; | ||
| uint64_t remote_left = remote_iov[ri].iov_len - ro; | ||
| uint64_t len = local_left < remote_left ? local_left : remote_left; | ||
| if (len == 0) | ||
| continue; | ||
|
|
||
| if (local_iov[li].iov_base > UINT64_MAX - lo || | ||
| remote_iov[ri].iov_base > UINT64_MAX - ro) | ||
| return copied > 0 ? (int64_t) copied : -LINUX_EFAULT; | ||
|
|
||
| uint64_t src_gva = write_remote ? local_iov[li].iov_base + lo | ||
| : remote_iov[ri].iov_base + ro; | ||
| uint64_t dst_gva = write_remote ? remote_iov[ri].iov_base + ro | ||
| : local_iov[li].iov_base + lo; | ||
|
|
||
| uint64_t src_avail = 0, dst_avail = 0; | ||
| void *src = guest_ptr_bound(g, src_gva, &src_avail, MEM_PERM_R, len); | ||
| void *dst = guest_ptr_bound(g, dst_gva, &dst_avail, MEM_PERM_W, len); | ||
| if (!src || !dst) | ||
| return copied > 0 ? (int64_t) copied : -LINUX_EFAULT; | ||
|
|
||
| uint64_t chunk = len; | ||
| if (chunk > src_avail) | ||
| chunk = src_avail; | ||
| if (chunk > dst_avail) | ||
| chunk = dst_avail; | ||
| if (chunk == 0) | ||
| return copied > 0 ? (int64_t) copied : -LINUX_EFAULT; | ||
|
|
||
| memmove(dst, src, (size_t) chunk); | ||
| copied += chunk; | ||
| lo += chunk; | ||
| ro += chunk; | ||
| } | ||
| } | ||
|
|
||
| static int64_t sys_process_vm(guest_t *g, | ||
| int64_t pid, | ||
| uint64_t local_iov_gva, | ||
| uint64_t local_iovcnt, | ||
| uint64_t remote_iov_gva, | ||
| uint64_t remote_iovcnt, | ||
| uint64_t flags, | ||
| bool write_remote) | ||
| { | ||
| if (flags != 0) | ||
| return -LINUX_EINVAL; | ||
| if (pid <= 0 || pid != proc_get_pid()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The PID gate is stricter than the rest of the runtime’s process identity handling and rejects same-process aliases outright. That can turn a same-process memory copy into Prompt for AI agents |
||
| return -LINUX_ESRCH; | ||
| if (local_iovcnt == 0 || remote_iovcnt == 0) | ||
| return 0; | ||
|
|
||
| linux_iovec_t *local_iov = NULL; | ||
| linux_iovec_t *remote_iov = NULL; | ||
| int64_t err = | ||
| process_vm_import_iov(g, local_iov_gva, local_iovcnt, &local_iov); | ||
| if (err < 0) | ||
| return err; | ||
| err = process_vm_import_iov(g, remote_iov_gva, remote_iovcnt, &remote_iov); | ||
| if (err < 0) { | ||
| free(local_iov); | ||
| return err; | ||
| } | ||
|
|
||
| int64_t ret = process_vm_copy(g, local_iov, local_iovcnt, remote_iov, | ||
| remote_iovcnt, write_remote); | ||
| free(remote_iov); | ||
| free(local_iov); | ||
| return ret; | ||
| } | ||
|
|
||
| int64_t sys_process_vm_readv(guest_t *g, | ||
| int64_t pid, | ||
| uint64_t local_iov_gva, | ||
| uint64_t local_iovcnt, | ||
| uint64_t remote_iov_gva, | ||
| uint64_t remote_iovcnt, | ||
| uint64_t flags) | ||
| { | ||
| return sys_process_vm(g, pid, local_iov_gva, local_iovcnt, remote_iov_gva, | ||
| remote_iovcnt, flags, false); | ||
| } | ||
|
|
||
| int64_t sys_process_vm_writev(guest_t *g, | ||
| int64_t pid, | ||
| uint64_t local_iov_gva, | ||
| uint64_t local_iovcnt, | ||
| uint64_t remote_iov_gva, | ||
| uint64_t remote_iovcnt, | ||
| uint64_t flags) | ||
| { | ||
| return sys_process_vm(g, pid, local_iov_gva, local_iovcnt, remote_iov_gva, | ||
| remote_iovcnt, flags, true); | ||
| } | ||
|
|
||
| /* terminal I/O. */ | ||
|
|
||
| int64_t sys_ioctl(guest_t *g, int fd, uint64_t request, uint64_t arg) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| /* | ||
| * process_vm_readv/process_vm_writev tests | ||
| * | ||
| * Copyright 2026 elfuse contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * Syscalls exercised: process_vm_readv(270), process_vm_writev(271) | ||
| */ | ||
|
|
||
| #include <errno.h> | ||
| #include <stdint.h> | ||
| #include <string.h> | ||
| #include <sys/uio.h> | ||
| #include <unistd.h> | ||
|
|
||
| #include "test-harness.h" | ||
| #include "raw-syscall.h" | ||
|
|
||
| int passes = 0, fails = 0; | ||
|
|
||
| #ifndef SYS_process_vm_readv | ||
| #define SYS_process_vm_readv 270 | ||
| #endif | ||
|
|
||
| #ifndef SYS_process_vm_writev | ||
| #define SYS_process_vm_writev 271 | ||
| #endif | ||
|
|
||
| static long raw_process_vm_readv(pid_t pid, | ||
| const struct iovec *local_iov, | ||
| unsigned long local_iovcnt, | ||
| const struct iovec *remote_iov, | ||
| unsigned long remote_iovcnt, | ||
| unsigned long flags) | ||
| { | ||
| return raw_syscall6(SYS_process_vm_readv, (long) pid, (long) local_iov, | ||
| (long) local_iovcnt, (long) remote_iov, | ||
| (long) remote_iovcnt, (long) flags); | ||
| } | ||
|
|
||
| static long raw_process_vm_writev(pid_t pid, | ||
| const struct iovec *local_iov, | ||
| unsigned long local_iovcnt, | ||
| const struct iovec *remote_iov, | ||
| unsigned long remote_iovcnt, | ||
| unsigned long flags) | ||
| { | ||
| return raw_syscall6(SYS_process_vm_writev, (long) pid, (long) local_iov, | ||
| (long) local_iovcnt, (long) remote_iov, | ||
| (long) remote_iovcnt, (long) flags); | ||
| } | ||
|
|
||
| static void test_readv_scatter(void) | ||
| { | ||
| TEST("process_vm_readv scatter"); | ||
|
|
||
| char remote[] = "hello-world"; | ||
| char a[6] = {0}; | ||
| char b[6] = {0}; | ||
| struct iovec local[2] = { | ||
| {.iov_base = a, .iov_len = 5}, | ||
| {.iov_base = b, .iov_len = 5}, | ||
| }; | ||
| struct iovec remote_iov[2] = { | ||
| {.iov_base = remote, .iov_len = 5}, | ||
| {.iov_base = remote + 6, .iov_len = 5}, | ||
| }; | ||
|
|
||
| long n = raw_process_vm_readv(getpid(), local, 2, remote_iov, 2, 0); | ||
| EXPECT_TRUE(n == 10 && !strcmp(a, "hello") && !strcmp(b, "world"), | ||
| "scatter read failed"); | ||
| } | ||
|
|
||
| static void test_writev_scatter(void) | ||
| { | ||
| TEST("process_vm_writev scatter"); | ||
|
|
||
| char src0[] = "ab"; | ||
| char src1[] = "cd"; | ||
| char remote[] = "...."; | ||
| struct iovec local[2] = { | ||
| {.iov_base = src0, .iov_len = 2}, | ||
| {.iov_base = src1, .iov_len = 2}, | ||
| }; | ||
| struct iovec remote_iov[2] = { | ||
| {.iov_base = remote, .iov_len = 2}, | ||
| {.iov_base = remote + 2, .iov_len = 2}, | ||
| }; | ||
|
|
||
| long n = raw_process_vm_writev(getpid(), local, 2, remote_iov, 2, 0); | ||
| EXPECT_TRUE(n == 4 && !memcmp(remote, "abcd", 4), "scatter write failed"); | ||
| } | ||
|
|
||
| static void test_short_copy_on_remote_fault(void) | ||
| { | ||
| TEST("process_vm_readv short remote fault"); | ||
|
|
||
| char remote[] = "abcd"; | ||
| char dst[8] = {0}; | ||
| struct iovec local = {.iov_base = dst, .iov_len = sizeof(dst)}; | ||
| struct iovec remote_iov[2] = { | ||
| {.iov_base = remote, .iov_len = 4}, | ||
| {.iov_base = (void *) 1, .iov_len = 4}, | ||
| }; | ||
|
|
||
| long n = raw_process_vm_readv(getpid(), &local, 1, remote_iov, 2, 0); | ||
| EXPECT_TRUE(n == 4 && !memcmp(dst, "abcd", 4), "short read fault"); | ||
| } | ||
|
|
||
| static void test_short_copy_on_local_fault(void) | ||
| { | ||
| TEST("process_vm_writev short local fault"); | ||
|
|
||
| char src[] = "wxyz"; | ||
| char remote[8] = {0}; | ||
| struct iovec local[2] = { | ||
| {.iov_base = src, .iov_len = 4}, | ||
| {.iov_base = (void *) 1, .iov_len = 4}, | ||
| }; | ||
| struct iovec remote_iov = {.iov_base = remote, .iov_len = sizeof(remote)}; | ||
|
|
||
| long n = raw_process_vm_writev(getpid(), local, 2, &remote_iov, 1, 0); | ||
| EXPECT_TRUE(n == 4 && !memcmp(remote, "wxyz", 4), "short write fault"); | ||
| } | ||
|
|
||
| static void test_error_paths(void) | ||
| { | ||
| char src[] = "x"; | ||
| char dst[] = "."; | ||
| struct iovec src_iov = {.iov_base = src, .iov_len = 1}; | ||
| struct iovec dst_iov = {.iov_base = dst, .iov_len = 1}; | ||
|
|
||
| TEST("process_vm flags EINVAL"); | ||
| EXPECT_RAW_ERRNO( | ||
| raw_process_vm_readv(getpid(), &dst_iov, 1, &src_iov, 1, 1), -EINVAL, | ||
| "flags accepted"); | ||
|
|
||
| TEST("process_vm foreign pid ESRCH"); | ||
| EXPECT_RAW_ERRNO( | ||
| raw_process_vm_writev(getpid() + 99999, &src_iov, 1, &dst_iov, 1, 0), | ||
| -ESRCH, "foreign pid accepted"); | ||
|
|
||
| TEST("process_vm bad iov pointer EFAULT"); | ||
| EXPECT_RAW_ERRNO(raw_process_vm_readv(getpid(), (const struct iovec *) 1, 1, | ||
| &src_iov, 1, 0), | ||
| -EFAULT, "bad local iov accepted"); | ||
|
|
||
| TEST("process_vm iovcnt EINVAL"); | ||
| EXPECT_RAW_ERRNO( | ||
| raw_process_vm_readv(getpid(), &dst_iov, 1025, &src_iov, 1, 0), -EINVAL, | ||
| "oversized iovcnt accepted"); | ||
|
|
||
| TEST("process_vm zero count"); | ||
| EXPECT_EQ(raw_process_vm_readv(getpid(), NULL, 0, NULL, 0, 0), 0, | ||
| "zero count failed"); | ||
| } | ||
|
|
||
| int main(void) | ||
| { | ||
| printf("test-process-vm: process_vm_readv/writev tests\n\n"); | ||
|
|
||
| test_readv_scatter(); | ||
| test_writev_scatter(); | ||
| test_short_copy_on_remote_fault(); | ||
| test_short_copy_on_local_fault(); | ||
| test_error_paths(); | ||
|
|
||
| SUMMARY("test-process-vm"); | ||
| return fails > 0 ? 1 : 0; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: PID values with nonzero upper register bits incorrectly return ESRCH instead of addressing the low-32-bit
pid_t. Narrowpidbefore validation, matching the syscall ABI and other PID handlers.Prompt for AI agents