-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Accepting Incoming TCP Connections fails on Android #82400
Comments
Is the x86 thing an emulator? From the strace output it would seem that an entirely wrong syscall is being invoked. Can this issue reproduce if you call |
Happens both on an Emulator and a real x86 device (Zebra ET50). I will try to whip up a minimal reproducer using the socket4 API. Edit: use std::mem;
use std::net::SocketAddr;
fn main() {
unsafe {
let bind_to: SocketAddr = "0.0.0.0:8080".parse().unwrap();
println!("Trying to bind to {}", bind_to);
let sock_addr =
nix::sys::socket::SockAddr::new_inet(nix::sys::socket::InetAddr::from_std(&bind_to));
let fd = libc::socket(libc::AF_INET, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0);
let (addrp, len) = sock_addr.as_ffi_pair();
assert_eq!(libc::bind(fd, addrp, len as _), 0);
assert_eq!(libc::listen(fd, 128), 0);
let mut storage: libc::sockaddr_storage = mem::zeroed();
let mut len = mem::size_of_val(&storage) as libc::socklen_t;
if libc::accept4(
fd,
&mut storage as *mut _ as *mut _,
&mut len,
libc::SOCK_CLOEXEC,
) < 0
{
println!("error on accept: {}", nix::errno::errno())
} else {
println!("read {} from socket", len);
}
}
} This works fine on my linux machine, but running it on an Android API 23 x86 emulator yields:
This also fails when using the generic syscall with errno 38: libc::syscall(
libc::SYS_accept4,
fd,
&mut storage as *mut _ as *mut _,
&mut len,
libc::SOCK_CLOEXEC,
) Seems my guess on why that started happening is wrong; now I wonder why that ever worked .. 😕 Just for reference:
|
@de-vri-es and @rustbot ping libs any insights to share about this? |
Error: This team ( Please let |
Hmm, maybe this has something to do with it: https://android.googlesource.com/platform/bionic.git/+/master/libc/SYSCALLS.TXT#264
I've never heard of the "socketcall syscall" before, but apparently, on x86 Linux (before 4.3), you have to call A bit annoying, because there is no documentation on what args should be. It "points to a block containing the actual arguments". The Linux man page specifically says this:
Linux 4.3 also added the regular syscalls for x86, but that's not something we can rely on for the android target :( So I think there are two options:
|
Note: although the same weird syscall is required for Linux < 4.3 (which is still a tier 1 platform), it shouldn't affect the Linux target because there rust libc is just calling |
It would be nice if Android's seccomp filter allowed the direct syscalls on x86 too, not just on arm; that'd be worth trying to get added to Android. But in the meantime, it looks like Android targets on 32-bit x86 will need to invoke |
It may not be the seccomp filter though. Older x86 kernels really do not have the accept4 syscall. Anyway, I'm working on a PR for libc to switch to |
PR opened: rust-lang/libc#2079 |
thank you @de-vri-es and @joshtriplett |
@rustbot label -I-prioritize -E-needs-bisection |
Note: back when accept4 was used on more platforms, std couldn't update to the latest libc. So it currently has the same workaround. I submitted #82473 to switch to |
Implement accept4 on x86 android with `socketcall` syscall. Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
Implement accept4 on x86 android with `socketcall` syscall. Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
Implement accept4 on x86 android with `socketcall` syscall. Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
…ohnTitor Implement accept4 on x86 android with `socketcall` syscall. Linux x86 kernels before 4.3 only support the `socketcall` syscall rather than individual syscalls for socket operations. Since `libc` does a raw syscall for `accept4` on Android, it doesn't work on x86 systems. This PR instead implements `accept4` for x86 android using `socketcall`. The value for `SYS_ACCEPT4` (in contrast to `SYS_accept4` 👀) is taken from the `linux/net.h` header. Also note that the `socketcall` syscall takes all arguments as array of long ints. I've double checked with `glibc` to check how they pass arguments, since the Linux man page only says this: "args points to a block containing the actual arguments" and "only standard library implementors and kernel hackers need to know about socketcall()". This should fix rust-lang/rust#82400
This isn't actually fixed yet, my bad for accidentally linking the issue. Might be good to re-open until it really is solved in std too. |
@de-vri-es no worries, I'll reopen it |
This comment has been minimized.
This comment has been minimized.
Since the Is that going to be on time for 1.49.0, or should someone be pinged to ask for a |
Bump up libc version to 0.2.87 r? `@ghost` In order to unblock rust-lang/rust#82400. This also closes #2065.
Bump up libc version to 0.2.87 r? `@ghost` In order to unblock rust-lang/rust#82400. This also closes #2065.
Bump up libc version to 0.2.87 r? `@ghost` In order to unblock rust-lang/rust#82400. This also closes #2065.
@de-vri-es Released libc 0.2.87, you can now update the |
Thanks! Opened #82731 :) |
…=joshtriplett [beta] Fix TcpListener::accept() on x86 Android on beta by disabling the use of accept4. This is the same as rust-lang#82475, but for beta. In a nutshell: `TcpListener::accept` is broken on Android x86 on stable and beta because it performs a raw `accept4` syscall, which doesn't exist on that platform. This was originally reported in rust-lang#82400, so you can find more details there. `@rustbot` label +O-android r? `@Mark-Simulacrum`
…-Simulacrum Bump libc dependency of std to 0.2.88. This PR bumps the `libc` dependency of `std` to 0.2.88. This will fix `TcpListener::accept` for Android on x86 platforms (rust-lang/libc@31a2777). This will really finally fix rust-lang#82400 for the main branch :) r? `@JohnTitor`
With Rust 1.49.0, accepting incoming connections on tcp sockets failed in different ways: Starting with Android Oreo (8), Android started using a seccomp based filter approach to syscalls, explicitly allowing syscalls, see https://android-developers.googleblog.com/2017/07/seccomp-filter-in-android-o.html. https://android.googlesource.com/platform/bionic.git/+/master/libc/SYSCALLS.TXT enumerates the allowed syscalls. rust-lang/rust#78572 refactored the way `std::net::TcpListener` accepts incoming connections on tcp sockets. With the seccomp profile above, doing a generic syscall will result in a panic: ``` [..] 02-22 13:14:23.288 6015 6041 F my.app.DEBUG: signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr -------- 02-22 13:14:23.288 6015 6041 F my.app.DEBUG: Cause: seccomp prevented call to disallowed x86 system call 364 02-22 13:14:23.289 6015 6041 F my.app.DEBUG: Abort message: 'Fatal signal 31 (SIGSYS), code 1 (SYS_SECCOMP) in tid 4784 (tokio-runtime-w), pid 4735 (ground_services)' ``` On top of that, I found that older versions of Android, such as Android 6 (our Zebra ET50), will return Function not implemented (os error 38) for this syscall. My tests showed that this only happens on x86, although I can't explain why. Relevant strace: ``` [pid 10918] syscall_364(0x34, 0x9d5c9cf8, 0x9d5c9ca0, 0x80800, 0x9fda9dc8, 0x9fda9dc8 <unfinished ...> [pid 10918] <... syscall_364 resumed> ) = -1 (errno 38) ``` I have tested this with both real devices as well as Android emulators. We have been using the `async-io` based `libp2p::tcp::TcpConfig` so far, which used `std::net::TcpListener` under the hood. This commit also switches to using `libp2p::tcp::TokioTcpConfig`. Now, tokio uses mio, which doesn't use `std::net::TcpListener` but raw sockets directly. Recently, a workaround for the erroneous behaviour described above was merged to mio, which is still pending to be released on crates.io (tokio-rs/mio#1462). Once tokio uses the updated mio version, we should move back to the crates.io provided version. For tracking the issue in `std::net::TcpListener`, I created rust-lang/rust#82400.
Starting with Android Oreo (8), Android started using a seccomp based filter approach to syscalls, explicitly allowing syscalls, see https://android-developers.googleblog.com/2017/07/seccomp-filter-in-android-o.html.
https://android.googlesource.com/platform/bionic.git/+/master/libc/SYSCALLS.TXT enumerates the allowed syscalls.
This means, that dispatching a generic syscall mechanism as introduced with this PR #78572 will result in a panic.
On top of that, I found that older versions of Android, such as Android 6, will return
Function not implemented (os error 38)
for this syscall.My tests showed that this only happens on x86, although I can't explain why.
As there's no way to detect the target android API level, the safest way would probably be to always use the
accept
syscall, and remove the special handling of theaccept4
syscall. This could also be just guarded for x86 -- but would be happy to get an explanation on why it's only on that architecture (see tokio-rs/mio#1446).I have tested this with both real devices as well as Android emulators.
Code
I tried this code:
and poked it with
telnet <android_host> 8080
.I expected to see this happen:
accept
returnsOk(_)
Instead, this happened:
Which translate to Function not implemented.
Version it worked on
It most recently worked on: Rust 1.48
Version with regression
rustc --version --verbose
:Backtrace
Backtrace
The text was updated successfully, but these errors were encountered: