Skip to content

Commit 1569ca2

Browse files
authored
Merge pull request #64 from sysprog21/host-syscall
Route sendmsg/recvmsg via shared host_iov helper
2 parents ed1811b + 7a98c58 commit 1569ca2

4 files changed

Lines changed: 138 additions & 79 deletions

File tree

src/syscall/internal.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <fcntl.h>
2525
#include <pthread.h>
2626
#include <stdbool.h>
27+
#include <sys/uio.h>
2728
#include <unistd.h>
2829

2930
#include "syscall/abi.h"
@@ -312,6 +313,51 @@ static inline int64_t host_fd_ref_open_io(guest_fd_t guest_fd,
312313
return 0;
313314
}
314315

316+
/* iov limits shared between readv/writev/preadv/pwritev and sendmsg/recvmsg.
317+
* SYSCALL_IOV_MAX matches the Linux UIO_MAXIOV cap; SYSCALL_IOV_STACK_MAX
318+
* keeps the typical case on the call-site stack.
319+
*/
320+
#define SYSCALL_IOV_MAX 1024
321+
#define SYSCALL_IOV_STACK_MAX 64
322+
323+
/* Resolved host iov vector backed by an inline stack buffer with a heap
324+
* fallback for large iovcnt. Pair host_iov_prepare with host_iov_free.
325+
*/
326+
typedef struct {
327+
struct iovec stack[SYSCALL_IOV_STACK_MAX];
328+
struct iovec *iov;
329+
} host_iov_buf_t;
330+
331+
/* Translate a guest iovec array at iov_gva (iovcnt entries) into the host
332+
* iovec layout in buf->iov, resolving each guest_base to a contiguous host
333+
* pointer with the requested permissions. On a non-contiguous iov entry the
334+
* helper truncates that entry to the contiguous prefix and zeros every
335+
* subsequent entry; the host readv/writev/sendmsg/recvmsg then returns a
336+
* POSIX-compliant short I/O instead of silently packing bytes from the next
337+
* guest buffer into the truncated tail.
338+
*
339+
* iovcnt <= 0 or > SYSCALL_IOV_MAX returns -LINUX_EINVAL.
340+
*
341+
* Returns 0 on success or a negative Linux errno on failure. The caller must
342+
* pair every successful prepare with host_iov_free to release any heap
343+
* spillover.
344+
*/
345+
int64_t host_iov_prepare(guest_t *g,
346+
uint64_t iov_gva,
347+
int iovcnt,
348+
int required_perms,
349+
host_iov_buf_t *buf);
350+
351+
/* sendmsg/recvmsg variant: iovcnt == 0 is legal for ancillary-only messages. */
352+
int64_t host_iov_prepare_msg(guest_t *g,
353+
uint64_t iov_gva,
354+
int iovcnt,
355+
int required_perms,
356+
host_iov_buf_t *buf);
357+
358+
/* Release any heap spillover backing a host_iov_buf_t. Idempotent. */
359+
void host_iov_free(host_iov_buf_t *buf);
360+
315361
/* Read a guest path string with small-buffer optimization.
316362
*
317363
* Tries the stack-allocated short_buf first; falls back to long_buf for

src/syscall/io.c

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
#include "syscall/proc.h"
4444
#include "syscall/signal.h"
4545

46-
#define SYSCALL_IOV_MAX 1024
47-
#define SYSCALL_IOV_STACK_MAX 64
4846
#define URANDOM_CACHE_SIZE 4096
4947

5048
/* Linux terminal struct types. */
@@ -1098,21 +1096,17 @@ static int64_t build_host_iov(guest_t *g,
10981096
return 0;
10991097
}
11001098

1101-
typedef struct {
1102-
struct iovec stack[SYSCALL_IOV_STACK_MAX];
1103-
struct iovec *iov;
1104-
} host_iov_buf_t;
1105-
1106-
static int64_t host_iov_prepare(guest_t *g,
1107-
uint64_t iov_gva,
1108-
int iovcnt,
1109-
int required_perms,
1110-
host_iov_buf_t *buf)
1099+
int64_t host_iov_prepare(guest_t *g,
1100+
uint64_t iov_gva,
1101+
int iovcnt,
1102+
int required_perms,
1103+
host_iov_buf_t *buf)
11111104
{
11121105
if (iovcnt <= 0 || iovcnt > SYSCALL_IOV_MAX)
11131106
return -LINUX_EINVAL;
11141107

11151108
buf->iov = buf->stack;
1109+
11161110
if (iovcnt > SYSCALL_IOV_STACK_MAX) {
11171111
buf->iov = malloc((size_t) iovcnt * sizeof(*buf->iov));
11181112
if (!buf->iov)
@@ -1130,7 +1124,20 @@ static int64_t host_iov_prepare(guest_t *g,
11301124
return 0;
11311125
}
11321126

1133-
static void host_iov_free(host_iov_buf_t *buf)
1127+
int64_t host_iov_prepare_msg(guest_t *g,
1128+
uint64_t iov_gva,
1129+
int iovcnt,
1130+
int required_perms,
1131+
host_iov_buf_t *buf)
1132+
{
1133+
if (iovcnt == 0) {
1134+
buf->iov = buf->stack;
1135+
return 0;
1136+
}
1137+
return host_iov_prepare(g, iov_gva, iovcnt, required_perms, buf);
1138+
}
1139+
1140+
void host_iov_free(host_iov_buf_t *buf)
11341141
{
11351142
if (buf->iov != buf->stack)
11361143
free(buf->iov);

src/syscall/net-msg.c

Lines changed: 41 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -174,41 +174,22 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
174174
dest_len = (socklen_t) ml;
175175
}
176176

177-
if (lmsg.msg_iovlen > 64) {
177+
/* msg_iovlen is uint64_t on Linux; bound it against SYSCALL_IOV_MAX
178+
* before the int narrowing below so a 64-bit value whose low 32 bits
179+
* fall inside [0, SYSCALL_IOV_MAX] cannot slip past the cap.
180+
*/
181+
if (lmsg.msg_iovlen > SYSCALL_IOV_MAX) {
178182
host_fd_ref_close(&host_ref);
179183
return -LINUX_EINVAL;
180184
}
185+
int send_iovcnt = (int) lmsg.msg_iovlen;
181186

182-
struct {
183-
uint64_t iov_base, iov_len;
184-
} guest_iov[64];
185-
186-
if (lmsg.msg_iovlen > 0) {
187-
if (guest_read(g, lmsg.msg_iov, guest_iov, lmsg.msg_iovlen * 16) < 0) {
188-
host_fd_ref_close(&host_ref);
189-
return -LINUX_EFAULT;
190-
}
191-
}
192-
193-
struct iovec host_iov[64];
194-
for (uint64_t i = 0; i < lmsg.msg_iovlen; i++) {
195-
if (guest_iov[i].iov_len == 0) {
196-
host_iov[i].iov_base = NULL;
197-
host_iov[i].iov_len = 0;
198-
continue;
199-
}
200-
uint64_t avail = 0;
201-
void *base = guest_ptr_bound(g, guest_iov[i].iov_base, &avail,
202-
MEM_PERM_R, guest_iov[i].iov_len);
203-
if (!base) {
204-
host_fd_ref_close(&host_ref);
205-
return -LINUX_EFAULT;
206-
}
207-
uint64_t len = guest_iov[i].iov_len;
208-
if (len > avail)
209-
len = avail;
210-
host_iov[i].iov_base = base;
211-
host_iov[i].iov_len = len;
187+
host_iov_buf_t host_iov;
188+
int64_t iov_err = host_iov_prepare_msg(g, lmsg.msg_iov, send_iovcnt,
189+
MEM_PERM_R, &host_iov);
190+
if (iov_err < 0) {
191+
host_fd_ref_close(&host_ref);
192+
return iov_err;
212193
}
213194

214195
uint8_t linux_ctrl_stack[512], mac_ctrl_stack[512];
@@ -223,6 +204,7 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
223204
if (lmsg.msg_control && lmsg.msg_controllen > 0) {
224205
size_t clen = lmsg.msg_controllen;
225206
if (clen > 65536) {
207+
host_iov_free(&host_iov);
226208
host_fd_ref_close(&host_ref);
227209
return -LINUX_EINVAL;
228210
}
@@ -232,6 +214,7 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
232214
if (!linux_ctrl_heap || !mac_ctrl_heap) {
233215
free(linux_ctrl_heap);
234216
free(mac_ctrl_heap);
217+
host_iov_free(&host_iov);
235218
host_fd_ref_close(&host_ref);
236219
return -LINUX_ENOMEM;
237220
}
@@ -242,6 +225,7 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
242225
if (guest_read(g, lmsg.msg_control, linux_ctrl, clen) < 0) {
243226
free(linux_ctrl_heap);
244227
free(mac_ctrl_heap);
228+
host_iov_free(&host_iov);
245229
host_fd_ref_close(&host_ref);
246230
return -LINUX_EFAULT;
247231
}
@@ -300,6 +284,7 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
300284
if (rc < 0) {
301285
free(linux_ctrl_heap);
302286
free(mac_ctrl_heap);
287+
host_iov_free(&host_iov);
303288
host_fd_ref_close(&host_ref);
304289
return rc;
305290
}
@@ -318,8 +303,8 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
318303
struct msghdr msg = {
319304
.msg_name = dest_sa,
320305
.msg_namelen = dest_len,
321-
.msg_iov = host_iov,
322-
.msg_iovlen = (int) lmsg.msg_iovlen,
306+
.msg_iov = host_iov.iov,
307+
.msg_iovlen = send_iovcnt,
323308
.msg_control = ctrl_ptr,
324309
.msg_controllen = ctrl_len,
325310
.msg_flags = 0,
@@ -328,6 +313,7 @@ int64_t sys_sendmsg(guest_t *g, int fd, uint64_t msg_gva, int linux_flags)
328313
ssize_t ret = sendmsg(host_ref.fd, &msg, mac_flags);
329314
free(linux_ctrl_heap);
330315
free(mac_ctrl_heap);
316+
host_iov_free(&host_iov);
331317
host_fd_ref_close(&host_ref);
332318
if (ret < 0) {
333319
if (errno == EPIPE && !suppress_sigpipe)
@@ -410,41 +396,19 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
410396
return ret;
411397
}
412398

413-
if (lmsg.msg_iovlen > 64) {
399+
/* See sys_sendmsg above: bound msg_iovlen before the int narrowing. */
400+
if (lmsg.msg_iovlen > SYSCALL_IOV_MAX) {
414401
host_fd_ref_close(&host_ref);
415402
return -LINUX_EINVAL;
416403
}
404+
int recv_iovcnt = (int) lmsg.msg_iovlen;
417405

418-
struct {
419-
uint64_t iov_base, iov_len;
420-
} guest_iov[64];
421-
422-
if (lmsg.msg_iovlen > 0) {
423-
if (guest_read(g, lmsg.msg_iov, guest_iov, lmsg.msg_iovlen * 16) < 0) {
424-
host_fd_ref_close(&host_ref);
425-
return -LINUX_EFAULT;
426-
}
427-
}
428-
429-
struct iovec host_iov[64];
430-
for (uint64_t i = 0; i < lmsg.msg_iovlen; i++) {
431-
if (guest_iov[i].iov_len == 0) {
432-
host_iov[i].iov_base = NULL;
433-
host_iov[i].iov_len = 0;
434-
continue;
435-
}
436-
uint64_t avail = 0;
437-
void *base = guest_ptr_bound(g, guest_iov[i].iov_base, &avail,
438-
MEM_PERM_W, guest_iov[i].iov_len);
439-
if (!base) {
440-
host_fd_ref_close(&host_ref);
441-
return -LINUX_EFAULT;
442-
}
443-
uint64_t len = guest_iov[i].iov_len;
444-
if (len > avail)
445-
len = avail;
446-
host_iov[i].iov_base = base;
447-
host_iov[i].iov_len = len;
406+
host_iov_buf_t host_iov;
407+
int64_t iov_err = host_iov_prepare_msg(g, lmsg.msg_iov, recv_iovcnt,
408+
MEM_PERM_W, &host_iov);
409+
if (iov_err < 0) {
410+
host_fd_ref_close(&host_ref);
411+
return iov_err;
448412
}
449413

450414
struct sockaddr_storage mac_sa;
@@ -477,8 +441,8 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
477441
struct msghdr msg = {
478442
.msg_name = lmsg.msg_name ? &mac_sa : NULL,
479443
.msg_namelen = lmsg.msg_name ? sa_len : 0,
480-
.msg_iov = host_iov,
481-
.msg_iovlen = (int) lmsg.msg_iovlen,
444+
.msg_iov = host_iov.iov,
445+
.msg_iovlen = recv_iovcnt,
482446
.msg_control = ctrl_alloc > 0 ? mac_ctrl : NULL,
483447
.msg_controllen = ctrl_alloc,
484448
.msg_flags = 0,
@@ -491,6 +455,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
491455
ssize_t ret = recvmsg(host_ref.fd, &msg, mac_flags);
492456
if (ret < 0) {
493457
free(mac_ctrl_heap);
458+
host_iov_free(&host_iov);
494459
host_fd_ref_close(&host_ref);
495460
return linux_errno();
496461
}
@@ -508,6 +473,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
508473
if (guest_write_small(g, lmsg.msg_name, linux_sa, write_len) <
509474
0) {
510475
free(mac_ctrl_heap);
476+
host_iov_free(&host_iov);
511477
host_fd_ref_close(&host_ref);
512478
return -LINUX_EFAULT;
513479
}
@@ -518,6 +484,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
518484
msg_gva + offsetof(linux_msghdr_t, msg_namelen),
519485
&nl, sizeof(nl)) < 0) {
520486
free(mac_ctrl_heap);
487+
host_iov_free(&host_iov);
521488
host_fd_ref_close(&host_ref);
522489
return -LINUX_EFAULT;
523490
}
@@ -534,6 +501,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
534501
lctrl_heap = malloc(lctrl_size);
535502
if (!lctrl_heap) {
536503
free(mac_ctrl_heap);
504+
host_iov_free(&host_iov);
537505
host_fd_ref_close(&host_ref);
538506
return -LINUX_ENOMEM;
539507
}
@@ -581,6 +549,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
581549
recvmsg_cleanup_scm_rights(scm_gfds, scm_hfds, scm_nfds);
582550
free(lctrl_heap);
583551
free(mac_ctrl_heap);
552+
host_iov_free(&host_iov);
584553
host_fd_ref_close(&host_ref);
585554
return -LINUX_EINVAL;
586555
}
@@ -654,6 +623,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
654623
recvmsg_cleanup_scm_rights(scm_gfds, scm_hfds, scm_nfds);
655624
free(lctrl_heap);
656625
free(mac_ctrl_heap);
626+
host_iov_free(&host_iov);
657627
host_fd_ref_close(&host_ref);
658628
return -LINUX_EFAULT;
659629
}
@@ -664,6 +634,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
664634
recvmsg_cleanup_scm_rights(scm_gfds, scm_hfds, scm_nfds);
665635
free(lctrl_heap);
666636
free(mac_ctrl_heap);
637+
host_iov_free(&host_iov);
667638
host_fd_ref_close(&host_ref);
668639
return -LINUX_EFAULT;
669640
}
@@ -674,6 +645,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
674645
&zero64, sizeof(zero64)) < 0) {
675646
free(lctrl_heap);
676647
free(mac_ctrl_heap);
648+
host_iov_free(&host_iov);
677649
host_fd_ref_close(&host_ref);
678650
return -LINUX_EFAULT;
679651
}
@@ -722,6 +694,7 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
722694
g, msg_gva + offsetof(linux_msghdr_t, msg_controllen), &zero64,
723695
sizeof(zero64)) < 0) {
724696
free(mac_ctrl_heap);
697+
host_iov_free(&host_iov);
725698
host_fd_ref_close(&host_ref);
726699
return -LINUX_EFAULT;
727700
}
@@ -734,11 +707,13 @@ int64_t sys_recvmsg(guest_t *g, int fd, uint64_t msg_gva, int flags)
734707
&mflags, sizeof(mflags)) < 0) {
735708
recvmsg_cleanup_scm_rights(scm_gfds, scm_hfds, scm_nfds);
736709
free(mac_ctrl_heap);
710+
host_iov_free(&host_iov);
737711
host_fd_ref_close(&host_ref);
738712
return -LINUX_EFAULT;
739713
}
740714

741715
free(mac_ctrl_heap);
716+
host_iov_free(&host_iov);
742717
host_fd_ref_close(&host_ref);
743718
return ret;
744719
}

0 commit comments

Comments
 (0)