From 05d4383daa0040165a7c93db7f59527e3c06bb21 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 27 Jul 2020 12:16:30 +0200 Subject: [PATCH] linux: fix i386 sendmmsg/recvmmsg support Android/i386 doesn't have separate sendmmsg/recvmmsg system calls, they're multiplexed through the socketcall system call. (More precisely, the system calls may be present but the standard seccomp filter rejects them, whereas socketcall is whitelisted.) This commit removes the flags and timeout arguments from libuv's internal system call wrappers because they're always zero and it makes EINVAL/ENOSYS detection after a failed socketcall() easier. Fixes: https://github.com/libuv/libuv/issues/2923 PR-URL: https://github.com/libuv/libuv/pull/2925 Reviewed-By: Colin Ihrig --- src/unix/freebsd.c | 15 +++-------- src/unix/internal.h | 11 ++------ src/unix/linux-syscalls.c | 56 +++++++++++++++++++++++++++------------ src/unix/udp.c | 8 +++--- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index 406eb158..fe795a0e 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -264,25 +264,18 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags) { +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { #if __FreeBSD__ >= 11 - return sendmmsg(fd, mmsg, vlen, flags); + return sendmmsg(fd, mmsg, vlen, /* flags */ 0); #else return errno = ENOSYS, -1; #endif } -int uv__recvmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags, - struct timespec* timeout) { +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { #if __FreeBSD__ >= 11 - return recvmmsg(fd, mmsg, vlen, flags, timeout); + return recvmmsg(fd, mmsg, vlen, 0 /* flags */, NULL /* timeout */); #else return errno = ENOSYS, -1; #endif diff --git a/src/unix/internal.h b/src/unix/internal.h index 9d3c2297..570274ed 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -334,15 +334,8 @@ struct uv__mmsghdr { unsigned int msg_len; }; -int uv__recvmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags, - struct timespec* timeout); -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags); +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); #else #define HAVE_MMSG 0 #endif diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c index 160056b4..44daaf12 100644 --- a/src/unix/linux-syscalls.c +++ b/src/unix/linux-syscalls.c @@ -37,8 +37,6 @@ #ifndef __NR_recvmmsg # if defined(__x86_64__) # define __NR_recvmmsg 299 -# elif defined(__i386__) -# define __NR_recvmmsg 337 # elif defined(__arm__) # define __NR_recvmmsg (UV_SYSCALL_BASE + 365) # endif @@ -47,8 +45,6 @@ #ifndef __NR_sendmmsg # if defined(__x86_64__) # define __NR_sendmmsg 307 -# elif defined(__i386__) -# define __NR_sendmmsg 345 # elif defined(__arm__) # define __NR_sendmmsg (UV_SYSCALL_BASE + 374) # endif @@ -146,25 +142,51 @@ struct uv__mmsghdr; -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags) { -#if defined(__NR_sendmmsg) - return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags); +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if defined(__i386__) + unsigned long args[4]; + int rc; + + args[0] = (unsigned long) fd; + args[1] = (unsigned long) mmsg; + args[2] = (unsigned long) vlen; + args[3] = /* flags */ 0; + + /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */ + rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args); + if (rc == -1) + if (errno == EINVAL) + errno = ENOSYS; + + return rc; +#elif defined(__NR_sendmmsg) + return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0); #else return errno = ENOSYS, -1; #endif } -int uv__recvmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags, - struct timespec* timeout) { -#if defined(__NR_recvmmsg) - return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout); +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if defined(__i386__) + unsigned long args[5]; + int rc; + + args[0] = (unsigned long) fd; + args[1] = (unsigned long) mmsg; + args[2] = (unsigned long) vlen; + args[3] = /* flags */ 0; + args[4] = /* timeout */ 0; + + /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */ + rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args); + if (rc == -1) + if (errno == EINVAL) + errno = ENOSYS; + + return rc; +#elif defined(__NR_recvmmsg) + return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0); #else return errno = ENOSYS, -1; #endif diff --git a/src/unix/udp.c b/src/unix/udp.c index 5ca54cef..a3ef558a 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -73,12 +73,12 @@ static void uv__udp_mmsg_init(void) { s = uv__socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return; - ret = uv__sendmmsg(s, NULL, 0, 0); + ret = uv__sendmmsg(s, NULL, 0); if (ret == 0 || errno != ENOSYS) { uv__sendmmsg_avail = 1; uv__recvmmsg_avail = 1; } else { - ret = uv__recvmmsg(s, NULL, 0, 0, NULL); + ret = uv__recvmmsg(s, NULL, 0); if (ret == 0 || errno != ENOSYS) uv__recvmmsg_avail = 1; } @@ -213,7 +213,7 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { } do - nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL); + nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks); while (nread == -1 && errno == EINTR); if (nread < 1) { @@ -356,7 +356,7 @@ write_queue_drain: } do - npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts, 0); + npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts); while (npkts == -1 && errno == EINTR); if (npkts < 1) {