在 2022-10-28星期五的 16:12 +0800,Icenowy Zheng写道:
> It's possible that a message contains both normal payload and
> ancillary
> data in the same message, and even if no ancillary data is available
> this information should be passed to the target, otherwise the target
> cmsghdr will be left uninitialized and the target is going to access
> uninitialized memory if it expects cmsg.

Here attaches an full example that utilizes RTNL and NETLINK_PKTINFO
cmsg:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

struct rtnlreq {
        struct nlmsghdr nmh;
        struct rtgenmsg msg;
};

int main()
{
        int fd, len, sockopt;
        struct sockaddr_nl local, remote;
        struct rtnlreq req;
        struct nlmsghdr *resp;
        struct cmsghdr *cmh;
        struct iovec iov;
        struct msghdr mh;
        char respbuf[8192],cmsgbuf[32];

        fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

        memset(&local, 0, sizeof(local));
        local.nl_family = AF_NETLINK;
        local.nl_pid = getpid();
        local.nl_groups = 0;

        if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
                abort();

        sockopt = 1;
        setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, &sockopt,
        sizeof(sockopt));

        memset(&remote, 0, sizeof(remote));
        remote.nl_family = AF_NETLINK;
        remote.nl_groups = 0;

        memset(&req, 0, sizeof(req));
        req.nmh.nlmsg_len = NLMSG_LENGTH(sizeof(req.msg));
        req.nmh.nlmsg_type = RTM_GETLINK;
        req.nmh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 
        req.nmh.nlmsg_seq = 1;
        req.nmh.nlmsg_pid = getpid();
        req.msg.rtgen_family = AF_INET;

        iov.iov_base = &req;
        iov.iov_len = req.nmh.nlmsg_len;

        memset(&mh, 0, sizeof(mh));
        mh.msg_iov = &iov;
        mh.msg_iovlen = 1;
        mh.msg_name = &remote;
        mh.msg_namelen = sizeof(remote);

        sendmsg(fd, &mh, 0);

        iov.iov_base = respbuf;
        iov.iov_len = 8192;

        memset(&mh, 0, sizeof(mh));
        mh.msg_iov = &iov;
        mh.msg_iovlen = 1;
        mh.msg_name = &remote;
        mh.msg_namelen = sizeof(remote);
        mh.msg_control = cmsgbuf;
        mh.msg_controllen = 32;

        len = recvmsg(fd, &mh, 0);
        if (!len)
                abort();

        resp = (struct nlmsghdr *)respbuf;
        cmh = CMSG_FIRSTHDR(&mh);
        if (NLMSG_OK(resp, len)) {
                printf("resp type %d len %zd\n",
                       resp->nlmsg_type, resp->nlmsg_len);
        }
        if (cmh) {
                printf("cmsg level %d type %d len %zd\n",
                       cmh->cmsg_level, cmh->cmsg_type, cmh->cmsg_len);
        }
}

On my machine, when it runs natively or runs under patched qemu, the
printed info is:
```
resp type 16 len 1392
cmsg level 270 type 3 len 20
```

However, with unpatched qemu it is:
```
resp type 16 len 1392
cmsg level 0 type 0 len 0
```

> 
> Always call the function that translate cmsg when recvmsg, because
> that
> function should be empty-cmsg-safe (it creates an empty cmsg in the
> target).
> 
> Signed-off-by: Icenowy Zheng <[email protected]>
> ---
>  linux-user/syscall.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 8402c1399d..029a4e8b42 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -3346,7 +3346,8 @@ static abi_long do_sendrecvmsg_locked(int fd,
> struct target_msghdr *msgp,
>              if (fd_trans_host_to_target_data(fd)) {
>                  ret = fd_trans_host_to_target_data(fd)(msg.msg_iov-
> >iov_base,
>                                                 MIN(msg.msg_iov-
> >iov_len, len));
> -            } else {
> +            }
> +            if (!is_error(ret)) {
>                  ret = host_to_target_cmsg(msgp, &msg);
>              }
>              if (!is_error(ret)) {


Reply via email to