On Fri, May 22, 2026 at 5:31 PM Pierrick Bouvier < [email protected]> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote: > > Add target-to-host and host-to-target control message conversion > > functions (t2h_freebsd_cmsg and h2t_freebsd_cmsg) for sendmsg/recvmsg > > support, and add os-socket.c to the FreeBSD build. > > > > Signed-off-by: Stacey Son <[email protected]> > > Signed-off-by: Mikaël Urankar <[email protected]> > > Signed-off-by: Kyle Evans <[email protected]> > > Signed-off-by: Michal Meloun <[email protected]> > > Signed-off-by: Warner Losh <[email protected]> > > Assisted-by: Claude Opus 4.6 (1M context) > > --- > > bsd-user/freebsd/meson.build | 8 +- > > bsd-user/freebsd/os-socket.c | 234 > +++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 240 insertions(+), 2 deletions(-) > > > > diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build > > index 38f2debf7e..0fc779749d 100644 > > --- a/bsd-user/freebsd/meson.build > > +++ b/bsd-user/freebsd/meson.build > > @@ -4,9 +4,13 @@ bsd_syscall_nr = custom_target('bsd-syscall-h', > > command: [sh, meson.current_source_dir() / 'scripts/syscallhdr.sh', > '@INPUT@', '@OUTPUT@', 'FREEBSD']) > > > > bsd_user_ss.add(files( > > - 'os-stat.c', > > + 'os-extattr.c', > > 'os-proc.c', > > + 'os-socket.c', > > + 'os-stat.c', > > 'os-sys.c', > > - 'os-syscall.c'), > > + 'os-syscall.c', > > + 'os-thread.c', > > + 'os-time.c'), > > bsd_syscall_nr > > ) > > Most of additions here seem to belong to previous patches. > I'll try to move these backwards in the series. I may have to reorder some things as well, since this is a known good compile point. It's worth trying. Warner > > diff --git a/bsd-user/freebsd/os-socket.c b/bsd-user/freebsd/os-socket.c > > new file mode 100644 > > index 0000000000..8eb728240d > > --- /dev/null > > +++ b/bsd-user/freebsd/os-socket.c > > @@ -0,0 +1,234 @@ > > +/* > > + * FreeBSD socket related system call helpers > > + * > > + * Copyright (c) 2013 Stacey D. Son > > + * > > + * SPDX-License-Identifier: GPL-2.0-or-later > > + */ > > +#include "qemu/osdep.h" > > + > > +#include <sys/types.h> > > +#include <sys/socket.h> > > +#include <netinet/in.h> > > + > > +#include "qemu.h" > > +#include "qemu-os.h" > > + > > +abi_long t2h_freebsd_cmsg(struct msghdr *msgh, > > + struct target_msghdr *target_msgh) > > +{ > > + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); > > + socklen_t msg_controllen; > > + abi_ulong target_cmsg_addr; > > + struct target_cmsghdr *target_cmsg, *target_cmsg_start; > > + socklen_t space = 0; > > + > > + msg_controllen = tswap32(target_msgh->msg_controllen); > > + if (msg_controllen < sizeof(struct target_cmsghdr)) { > > + goto the_end; > > + } > > + target_cmsg_addr = tswapal(target_msgh->msg_control); > > + target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, > msg_controllen, 1); > > + target_cmsg_start = target_cmsg; > > + if (!target_cmsg) { > > + return -TARGET_EFAULT; > > + } > > + > > + while (cmsg && target_cmsg) { > > + void *data = CMSG_DATA(cmsg); > > + void *target_data = TARGET_CMSG_DATA(target_cmsg); > > + int len = (unsigned char *)(target_cmsg) + > > + tswap32(target_cmsg->cmsg_len) - (unsigned char > *)target_data; > > + > > + space += CMSG_SPACE(len); > > + if (space > msgh->msg_controllen) { > > + space -= CMSG_SPACE(len); > > + /* > > + * This is a QEMU bug, since we allocated the payload area > ourselves > > + * (unlike overflow in host-to-target conversion, which is > just the > > + * guest giving us a buffer that's too small). It can't > happen for > > + * the payload types we currently support; if it becomes an > issue in > > + * future we would need to improve our allocation strategy > to > > + * something more intelligent than "twice the size of the > target > > + * buffer we're reading from". > > + */ > > + gemu_log("Host cmsg overflow\n"); > > + break; > > + } > > + > > + if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) { > > + cmsg->cmsg_level = SOL_SOCKET; > > + } else { > > + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); > > + } > > + cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type); > > + cmsg->cmsg_len = CMSG_LEN(len); > > + > > + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == > SCM_RIGHTS) { > > + int *fd = (int *)data; > > + int *target_fd = (int *)target_data; > > + int i, numfds = len / sizeof(int); > > + > > + for (i = 0; i < numfds; i++) { > > + __get_user(fd[i], target_fd + i); > > + } > > + } else if ((cmsg->cmsg_level == SOL_SOCKET) && > > + (cmsg->cmsg_type == SCM_TIMESTAMP) && > > + (len == sizeof(struct target_freebsd_timeval))) { > > + /* copy struct timeval to host */ > > + struct timeval *tv = (struct timeval *)data; > > + struct target_freebsd_timeval *target_tv = > > + (struct target_freebsd_timeval *)target_data; > > + __get_user(tv->tv_sec, &target_tv->tv_sec); > > + __get_user(tv->tv_usec, &target_tv->tv_usec); > > + } else { > > + gemu_log("Unsupported target ancillary data: %d/%d\n", > > + cmsg->cmsg_level, cmsg->cmsg_type); > > + memcpy(data, target_data, len); > > + } > > + > > + cmsg = CMSG_NXTHDR(msgh, cmsg); > > + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, > > + target_cmsg_start); > > + } > > + unlock_user(target_cmsg_start, target_cmsg_addr, 0); > > +the_end: > > + msgh->msg_controllen = space; > > + return 0; > > +} > > + > > +abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh, > > + struct msghdr *msgh) > > +{ > > + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); > > + socklen_t msg_controllen; > > + abi_ulong target_cmsg_addr; > > + struct target_cmsghdr *target_cmsg, *target_cmsg_start; > > + socklen_t space = 0; > > + > > + msg_controllen = tswap32(target_msgh->msg_controllen); > > + if (msg_controllen < sizeof(struct target_cmsghdr)) { > > + goto the_end; > > + } > > + target_cmsg_addr = tswapal(target_msgh->msg_control); > > + target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, > msg_controllen, 0); > > + target_cmsg_start = target_cmsg; > > + if (!target_cmsg) { > > + return -TARGET_EFAULT; > > + } > > + > > + while (cmsg && target_cmsg) { > > + void *data = CMSG_DATA(cmsg); > > + void *target_data = TARGET_CMSG_DATA(target_cmsg); > > + int len = (unsigned char *)(cmsg) + cmsg->cmsg_len - > > + (unsigned char *)data; > > + > > + int tgt_len, tgt_space; > > + > > + /* > > + * We never copy a half-header but may copy half-data; this is > Linux's > > + * behaviour in put_cmsg(). Note that truncation here is a > guest problem > > + * (which we report to the guest via the CTRUNC bit), unlike > truncation > > + * in target_to_host_cmsg, which is a QEMU bug. > > + */ > > + if (msg_controllen < sizeof(struct target_cmsghdr)) { > > + target_msgh->msg_flags |= tswap32(MSG_CTRUNC); > > + break; > > + } > > + > > + if (cmsg->cmsg_level == SOL_SOCKET) { > > + target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET); > > + } else { > > + target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); > > + } > > + target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); > > + > > + /* > > + * Payload types which need a different size of payload on the > target > > + * must adjust tgt_len here. > > + */ > > + tgt_len = len; > > + switch (cmsg->cmsg_level) { > > + case SOL_SOCKET: > > + switch (cmsg->cmsg_type) { > > + case SCM_TIMESTAMP: > > + tgt_len = sizeof(struct target_freebsd_timeval); > > + break; > > + default: > > + break; > > + } > > + break; > > + default: > > + break; > > + } > > + > > + if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) { > > + target_msgh->msg_flags |= tswap32(MSG_CTRUNC); > > + tgt_len = msg_controllen - sizeof(struct target_cmsghdr); > > + } > > + > > + /* > > + * We must now copy-and-convert len bytes of payload into > tgt_len bytes > > + * of destination space. Bear in mind that in both source and > > + * destination we may be dealing with a truncated value! > > + */ > > + switch (cmsg->cmsg_level) { > > + case SOL_SOCKET: > > + switch (cmsg->cmsg_type) { > > + case SCM_RIGHTS: > > + { > > + int *fd = (int *)data; > > + int *target_fd = (int *)target_data; > > + int i, numfds = tgt_len / sizeof(int); > > + > > + for (i = 0; i < numfds; i++) { > > + __put_user(fd[i], target_fd + i); > > + } > > + break; > > + } > > + case SCM_TIMESTAMP: > > + { > > + struct timeval *tv = (struct timeval *)data; > > + struct target_freebsd_timeval *target_tv = > > + (struct target_freebsd_timeval *)target_data; > > + > > + if (len != sizeof(struct timeval) || > > + tgt_len != sizeof(struct target_freebsd_timeval)) { > > + goto unimplemented; > > + } > > + > > + /* copy struct timeval to target */ > > + __put_user(tv->tv_sec, &target_tv->tv_sec); > > + __put_user(tv->tv_usec, &target_tv->tv_usec); > > + break; > > + } > > + default: > > + goto unimplemented; > > + } > > + break; > > + default: > > + unimplemented: > > + gemu_log("Unsupported host ancillary data: %d/%d\n", > > + cmsg->cmsg_level, cmsg->cmsg_type); > > + memcpy(target_data, data, MIN(len, tgt_len)); > > + if (tgt_len > len) { > > + memset(target_data + len, 0, tgt_len - len); > > + } > > + } > > + > > + target_cmsg->cmsg_len = tswap32(TARGET_CMSG_LEN(tgt_len)); > > + tgt_space = TARGET_CMSG_SPACE(tgt_len); > > + if (msg_controllen < tgt_space) { > > + tgt_space = msg_controllen; > > + } > > + msg_controllen -= tgt_space; > > + space += tgt_space; > > + cmsg = CMSG_NXTHDR(msgh, cmsg); > > + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, > > + target_cmsg_start); > > + } > > + unlock_user(target_cmsg_start, target_cmsg_addr, space); > > +the_end: > > + target_msgh->msg_controllen = tswap32(space); > > + return 0; > > +} > > > >
