Skip to content
Merged
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
25 changes: 25 additions & 0 deletions src/core/guest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,31 @@ int guest_write(guest_t *g, uint64_t gva, const void *src, size_t len)
return guest_copy(g, gva, NULL, src, len, MEM_PERM_W);
}

size_t guest_write_partial(guest_t *g,
uint64_t gva,
const void *src,
size_t len)
{
size_t done = 0;
while (done < len) {
uint64_t avail;
void *dst = gva_resolve_perm(g, gva + done, &avail, MEM_PERM_W,
(uint64_t) (len - done));
if (!dst)
return done;

size_t chunk = len - done;
if (chunk > avail)
chunk = avail;
size_t moved =
guest_host_copy_partial(dst, (const uint8_t *) src + done, chunk);
done += moved;
if (moved < chunk)
return done;
}
return done;
}

int guest_write_small(guest_t *g, uint64_t gva, const void *src, size_t len)
{
uint64_t avail = 0;
Expand Down
14 changes: 14 additions & 0 deletions src/core/guest.h
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,20 @@ int guest_read_small(const guest_t *g, uint64_t gva, void *dst, size_t len);
*/
int guest_write(guest_t *g, uint64_t gva, const void *src, size_t len);

/* Bounds-checked copy from host buffer into guest memory, reporting how many
* bytes landed.
*
* A caller whose return value is a byte count uses this instead of
* guest_write(), which reports only whether the whole copy survived: a copy
* that faults or leaves the mapping partway still places the bytes before that
* point, and they are in guest memory whatever the caller reports. The count is
* exact to the bound guest_host_copy_partial() documents.
*/
size_t guest_write_partial(guest_t *g,
uint64_t gva,
const void *src,
size_t len);

/* Optimized host-to-guest copy for small fixed-size outputs. Uses a direct
* guest pointer when the full range is contiguous and writable, otherwise falls
* back to guest_write() for boundary-crossing safety.
Expand Down
19 changes: 18 additions & 1 deletion src/syscall/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,15 @@ int64_t sys_write(guest_t *g, int fd, uint64_t buf_gva, uint64_t count)
if (type == FD_EVENTFD)
return eventfd_write(fd, g, buf_gva, count);

/* Linux accepts write() on a netlink socket as sendto() with no explicit
* destination, and iproute2-style senders use it in place of sendto. The
* host fd behind a netlink guest fd is the read end of the readiness pipe,
* so falling through would write to a read-only pipe end and report EBADF
* for a request the emulation can answer.
*/
if (type == FD_NETLINK)
return netlink_send(fd, g, buf_gva, count);
Comment thread
xalestar marked this conversation as resolved.

host_fd_ref_t host_ref;
int64_t err = host_fd_ref_open_checked(fd, &host_ref, true);
if (err < 0)
Expand Down Expand Up @@ -1411,6 +1420,8 @@ int64_t sys_readv(guest_t *g, int fd, uint64_t iov_gva, int iovcnt)
{
if (iovcnt == 0)
return vec_zero_iovcnt(fd, false, false);
if (fd_get_type(fd) == FD_NETLINK)
return netlink_readv(fd, g, iov_gva, iovcnt);
if (iovcnt == 1) {
linux_iovec_t giov;
int64_t err = single_guest_iov(g, iov_gva, &giov);
Expand Down Expand Up @@ -1564,6 +1575,11 @@ int64_t sys_writev(guest_t *g, int fd, uint64_t iov_gva, int iovcnt)
{
if (iovcnt == 0)
return vec_zero_iovcnt(fd, true, false);
/* Ahead of the single-entry shortcut: a one-entry writev of nothing reports
* 0, while the write(2) the shortcut would reach reports ENODATA.
*/
if (fd_get_type(fd) == FD_NETLINK)
return netlink_writev(fd, g, iov_gva, iovcnt);
if (iovcnt == 1) {
linux_iovec_t giov;
int64_t err = single_guest_iov(g, iov_gva, &giov);
Expand All @@ -1577,7 +1593,8 @@ int64_t sys_writev(guest_t *g, int fd, uint64_t iov_gva, int iovcnt)
* iovs) because the data is at giov.iov_base which is only giov.iov_len
* bytes. eventfd expects exactly 8 bytes.
*/
if (fd_get_type(fd) == FD_EVENTFD) {
int wtype = fd_get_type(fd);
if (wtype == FD_EVENTFD) {
if (iovcnt <= 0)
return -LINUX_EINVAL;
linux_iovec_t giov;
Expand Down
11 changes: 11 additions & 0 deletions src/syscall/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <stdint.h>
#include "core/guest.h"
#include "syscall/linux-wire.h" /* linux_iovec_t */

/* Linux address families. */
#define LINUX_AF_UNSPEC 0
Expand Down Expand Up @@ -227,6 +228,16 @@ int64_t netlink_read(int guest_fd,

int64_t netlink_send(int guest_fd, guest_t *g, uint64_t buf_gva, uint64_t len);

/* Vectored forms of the two above, taking the guest iovec array they stage.
* One netlink request, and one response, spans the whole iovec: the send
* gathers every entry into one request and the receive fills every entry from
* one response, so neither stops at the first entry the way the scalar special
* fds do.
*/
int64_t netlink_writev(int guest_fd, guest_t *g, uint64_t iov_gva, int iovcnt);

int64_t netlink_readv(int guest_fd, guest_t *g, uint64_t iov_gva, int iovcnt);

int64_t netlink_recv(int guest_fd,
guest_t *g,
uint64_t buf_gva,
Expand Down
Loading
Loading