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;
>> > +}
>> >
>>
>>

Reply via email to