bytecodealliance / bytecodealliance/rustix

Properly support `ioctl` that takes the last argument as an integer not a pointer

Open
#1,051 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2.1k
Forks
294
Avg merge
4h 7m
Merged PRs (30d)
2

Description

The ioctl `TUNSETOFFLOAD` takes an `unsigned int` as input rather than `unsigned int*`, so currently none of the standard `Ioctl` type work and must be worked-around via an awkward int-to-pointer cast.

Proof of concept

```rust
#!/usr/bin/env -S cargo +nightly -Zscript
---
[dependencies]
rustix = { version = "0.38.34", features = ["fs"] }
---

// Note: require root permission to run.

use rustix::{
fs::{open, Mode, OFlags},
io::Result,
ioctl::{ioctl, CompileTimeOpcode, Ioctl, IoctlOutput, Opcode, Setter, WriteOpcode},
};
use std::{
ffi::{c_int, c_uint, c_void},
marker::PhantomData,
};

// define the ioctl constants
const TUN_F_CSUM: c_uint = 0x01;
type TUNSETIFF = WriteOpcode;
type TUNSETOFFLOAD = WriteOpcode;

fn main() -> Result<()> {
let fd = open(
c"/dev/net/tun",
OFlags::RDWR | OFlags::CLOEXEC,
Mode::empty(),
)?;
println!("created fd: {:?}", fd);
unsafe {
ioctl(
&fd,
Setter::::new(*b"example0\0\0\0\0\0\0\0\0\x01\x10"),
)?;
println!("tunsetiff successful");
// ioctl(&fd, Setter::::new(TUN_F_CSUM))?; // will throw "InvalidInput" here!
ioctl(&fd, UintSetter::::new(TUN_F_CSUM))?; // this works!
println!("tunsetoffload successful");
}
Ok(())
}

// we need to unsafe impl a non-standard Ioctl to make that work :/

struct UintSetter {
input: c_uint,
_opcode: PhantomData,
}

impl UintSetter {
pub fn new(input: c_uint) -> Self {
Self {
input,
_opcode: PhantomData,
}
}
}

unsafe impl Ioctl for UintSetter {
type Output = ();
const IS_MUTATING: bool = true;
const OPCODE: Opcode = Oc::OPCODE;

fn as_ptr(&mut self) -> *mut c_void {
// // with strict provenance:
// std::ptr::invalid_mut(self.input as _)
self.input as _
}

unsafe fn output_from_ptr(_: IoctlOutput, _: *mut c_void) -> Result {
Ok(())
}
}
```

As of Linux v6.8 at least the following ioctls take the integer input directly rather than through a pointer:

* REISERFS_IOC_UNPACK (boolean)
* CACHEFILES_IOC_READ_COMPLETE (msg_id)
* BTRFS_IOC_BALANCE_CTL (`BTRFS_BALANCE_CTL_{PAUSE,CANCEL}`)
* EVIOCRMFF (effect_id)
* EVIOCGRAB (boolean)
* PTP_ENABLE_PPS (boolean)
* PTP_ENABLE_PPS2 (boolean)
* HIDIOCAPPLICATION (application index)
* SNAPSHOT_PREF_IMAGE_SIZE (size)
* SNAPSHOT_PLATFORM_SUPPORT (boolean)
* USB_RAW_IOCTL_EP_DISABLE (endpoint index)
* USB_RAW_IOCTL_VBUS_DRAW (multiples of 2 mA)
* USB_RAW_IOCTL_EP_SET_HALT (endpoint index)
* USB_RAW_IOCTL_EP_CLEAR_HALT (endpoint index)
* USB_RAW_IOCTL_EP_SET_WEDGE (endpoint index)
* TUNSETNOCSUM (boolean)
* TUNSETPERSIST (boolean)
* TUNSETOWNER (uid)
* TUNSETGROUP (gid)
* TUNSETLINK (`ARPHRD_*`)
* TUNSETDEBUG (u32)
* TUNSETOFFLOAD (flags)
* ...

On `nix` these are supported via [`ioctl_write_int`](https://docs.rs/nix/latest/nix/macro.ioctl_write_int.html), in contrast to [`ioctl_write_ptr`](https://docs.rs/nix/latest/nix/macro.ioctl_write_ptr.html) that corresponds to `rustix::ioctl::Setter`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.