Skip to content
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

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

Open
kennytm opened this issue Apr 24, 2024 · 2 comments

Comments

@kennytm
Copy link

kennytm commented Apr 24, 2024

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
#!/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<b'T', 202, c_int>;
type TUNSETOFFLOAD = WriteOpcode<b'T', 208, c_uint>;

fn main() -> Result<()> {
    let fd = open(
        c"/dev/net/tun",
        OFlags::RDWR | OFlags::CLOEXEC,
        Mode::empty(),
    )?;
    println!("created fd: {:?}", fd);
    unsafe {
        ioctl(
            &fd,
            Setter::<TUNSETIFF, _>::new(*b"example0\0\0\0\0\0\0\0\0\x01\x10"),
        )?;
        println!("tunsetiff successful");
        // ioctl(&fd, Setter::<TUNSETOFFLOAD, _>::new(TUN_F_CSUM))?; // will throw "InvalidInput" here!
        ioctl(&fd, UintSetter::<TUNSETOFFLOAD>::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<Oc> {
    input: c_uint,
    _opcode: PhantomData<Oc>,
}

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

unsafe impl<Oc: CompileTimeOpcode> Ioctl for UintSetter<Oc> {
    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<Self::Output> {
        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, in contrast to ioctl_write_ptr that corresponds to rustix::ioctl::Setter.

@notgull
Copy link
Contributor

notgull commented May 21, 2024

During my design of this trait the intention was to involve an int-to-pointer cast here. Those are relatively rare in the ecosystem, so it should be somewhat awkward to use as a way of making sure that's what you really want.

Although, if it's common I think it would be good to add something similar to rustix::ioctl::Setter that does a number instead of a pointer.

@kennytm
Copy link
Author

kennytm commented May 21, 2024

The issue is not whether it's rare, but that for these ioctls passing values directly are the only way to communicate with the kernel. If you want to use TUNSETOFFLOAD with rustix the only way is to define that UintSetter somewhere, there are no workarounds. And I prefer that type to be placed in rustix itself rather than my crate 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants