On Sat, May 23, 2026 at 12:35 AM Warner Losh <[email protected]> wrote:
> > > 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. > Ah, it turns out most of them belong to later patches. I've moved them. Warner > 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; >> > +} >> > >> >>
