From d0cdc0c4f9b9aefc893566ecaee4a60945ce818b Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sat, 10 Aug 2024 09:00:59 +0200 Subject: [PATCH] fix(udp): use libc::recvmmsg on Android x86 > On x86-32, socketcall() was historically the only entry point for > the sockets API. However, starting in Linux 4.3, direct system > calls are provided on x86-32 for the sockets API. https://man7.org/linux/man-pages/man2/socketcall.2.html Androids x86 even goes as far as automatically dispatching advanced socket calls (e.g. `socket` or `recvmmsg`) through `socketcall` in its Bionic libc: > For example, socket() syscall on i386 actually becomes: > socketcall(__NR_socket, 1, *(rest of args on stack)). https://android.googlesource.com/platform/bionic/+/refs/tags/android-vts-14.0_r5/libc/SYSCALLS.TXT#19 In addition Android enables seccomp filtering since Android 8: https://android-developers.googleblog.com/2017/07/seccomp-filter-in-android-o.html Currently `quinn-udp` calls `recvmmsg` directly on Android x86: ``` rust let ret = libc::syscall(libc::SYS_recvmmsg, sockfd, msgvec, vlen, flags, timeout) as libc::c_int; ``` https://github.com/quinn-rs/quinn/blob/c0b1e281e3167d4f4c8496082cb1117c4a270ad8/quinn-udp/src/unix.rs#L447-L448 A direct `recvmmsg` through `libc::syscall(libc::SYS_recvmmsg` does not trigger the automatic Android x86 Bionic libc dispatch logic through `socketcall`, but instead calls the unimplemented `recvmmsg` syscall. Instead of triggering a `libc::ENOSYS`, seccomp disallows the call, thus leading to a panic of the application. This commit changes `quinn-udp` to use `libc::recvmmsg` on Android x86, thus leveraging Bionic's automatic dispatch of `recvmmsg` through `socketcall`. Note that this commit only uses `libc::recvmmsg` on Android x86, not any other Android or Linux variant. Thus quinn-udp would still support missing `libc::recvmmsg` on those systems. See https://github.com/quinn-rs/quinn/pull/1504. --- quinn-udp/src/unix.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index 8ce5f29f2..cadce6634 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -447,7 +447,11 @@ unsafe fn recvmmsg_with_fallback( let flags = 0; let timeout = ptr::null_mut::(); - #[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))] + #[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + all(target_os = "android", target_arch = "x86") + )))] { let ret = libc::syscall(libc::SYS_recvmmsg, sockfd, msgvec, vlen, flags, timeout) as libc::c_int; @@ -468,6 +472,19 @@ unsafe fn recvmmsg_with_fallback( } } + // On Android x86 seccomp only allows `recvmmsg` dispatched through + // `socketcall`. The bionic libc implementation automatically dispatches + // `recvmmsg` through `socketcall` when calling `libc::recvmmsg`. Calling + // `SYS-recvmmsg` directly (i.e. `syscall(SYS_recvmmsg)`) does not dispatch + // through `socketcall` and is thus disallowed by seccomp. + #[cfg(all(target_os = "android", target_arch = "x86"))] + { + let ret = libc::recvmmsg(sockfd, msgvec, vlen, flags, timeout) as libc::c_int; + if ret != -1 { + return ret; + } + } + let e = io::Error::last_os_error(); match e.raw_os_error() { Some(libc::ENOSYS) => {