diff mbox series

[v2,15/37] bsd-user: Add os-socket.c with cmsg conversion functions

Message ID 20260518-misc-2026q2-v2-15-6c16fe448301@bsdimp.com
State New
Headers show
Series bsd-user: Upstream most of the remaining system calls | expand

Commit Message

Warner Losh May 18, 2026, 9:27 p.m. UTC
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 <sson@FreeBSD.org>
Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Michal Meloun <mmel@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
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(-)

Comments

Pierrick Bouvier May 22, 2026, 11:31 p.m. UTC | #1
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 <sson@FreeBSD.org>
> Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
> Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
> Signed-off-by: Michal Meloun <mmel@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> 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.

> 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;
> +}
>
Warner Losh May 23, 2026, 4:35 a.m. UTC | #2
On Fri, May 22, 2026 at 5:31 PM Pierrick Bouvier <
pierrick.bouvier@oss.qualcomm.com> 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 <sson@FreeBSD.org>
> > Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
> > Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
> > Signed-off-by: Michal Meloun <mmel@FreeBSD.org>
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > 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;
> > +}
> >
>
>
diff mbox series

Patch

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