#ioctl #linux-kernel #linux #api-bindings

iocuddle

用于构建运行时安全的 Linux ioctl() 接口的库

2 个版本

0.1.1 2020 年 9 月 4 日
0.1.0 2020 年 8 月 19 日

147操作系统 中排名

Download history 5613/week @ 2024-03-14 3848/week @ 2024-03-21 2765/week @ 2024-03-28 2507/week @ 2024-04-04 3007/week @ 2024-04-11 2943/week @ 2024-04-18 3077/week @ 2024-04-25 3428/week @ 2024-05-02 2443/week @ 2024-05-09 2911/week @ 2024-05-16 2490/week @ 2024-05-23 3041/week @ 2024-05-30 2767/week @ 2024-06-06 3387/week @ 2024-06-13 3837/week @ 2024-06-20 2450/week @ 2024-06-27

每月 12,878 次下载
12 crate 中使用 (直接使用 4 个)

Apache-2.0

24KB
114

Workflow Status Average time to resolve an issue Percentage of issues still open Maintenance

iocuddle

iocuddle 是一个用于构建运行时安全的 ioctl() 接口的库。

现有方法从 Rust 与 ioctl 交互依赖于在调用站点进行类型转换和/或不安全的代码声明。这把安全的负担转移给了 ioctl 接口的使用者,这并不理想。

相比之下,iocuddle 尝试将不安全的代码负担转移到 ioctl 定义。一旦定义了 ioctl,就可以在安全代码中执行该 ioctl 的所有执行。

接口

iocuddle 旨在处理内核 >=99% 的 ioctl 接口。然而,我们并不旨在处理所有可能的 ioctl 接口。以下将概述不同的 ioctl 接口。

经典接口

经典 ioctl 接口是那些在我们下面将要看到的现代接口之前创建的 ioctl。它们基本上允许完全使用 ioctl libc 函数,该函数定义为如下

use std::os::raw::{c_int, c_ulong};
extern "C" { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }

此接口可以接受任何数量和类型的参数,并可以返回任何正整数(-1 保留用于与 errno 结合表示错误)。

此接口的一个主要缺点是它完全忽略了编译器对类型安全性的检查。特定的 request 隐式关联到一或多个类型,通常列在相关的 ioctl man 页面上。如果程序员类型错误,最终会导致内存损坏。

这种接口的问题被早期认识到。因此,大多数 ioctl 只支持单个参数以减少复杂性。但这并没有解决缺乏编译器强制类型安全性的问题。

iocuddle 目前不支持具有多个参数的 ioctl。否则,可以通过 Ioctl::classic() 构造函数定义并使用经典的 ioctl 接口,如下所示

use std::os::raw::{c_void, c_int, c_uint};
use iocuddle::*;

let mut file = std::fs::File::open("/dev/tty").unwrap_or_else(|_| std::process::exit(0));

// This is the simplest ioctl call. The request number is provided via the
// Ioctl::classic() constructor. This ioctl reads a C integer from the
// kernel by internally passing a reference to a c_int as the argument to
// the ioctl. This c_int is returned in the Ok status of the ioctl Result.
//
// Notice that since the state of the file descriptor is not modified via
// this ioctl, we define it using the Read parameter.
const TIOCINQ: Ioctl<Read, &c_int> = unsafe { Ioctl::classic(0x541B) };
assert_eq!(TIOCINQ.ioctl(&file).unwrap(), (0 as c_uint, 0 as c_int));

// This ioctl is similar to the previous one. It differs in two important
// respects. First, this raw ioctl takes an input argument rather than an
// output argument. This raw argument is a C integer *NOT* a reference to
// a C integer. Second, since this ioctl modifies the state of the file
// descriptor we use Write instead of Read.
//
// Notice that the return value of the TCSBRK.ioctl() call is the positive
// integer returned from the raw ioctl(), unlike the previous example. It
// is not the input argument type.
const TCSBRK: Ioctl<Write, c_int> = unsafe { Ioctl::classic(0x5409) };
assert_eq!(TCSBRK.ioctl(&mut file, 0).unwrap(), 0 as c_uint);

// `iocuddle` can also support classic ioctls with no argument. These
// always modify the file descriptor state, so the Write parameter is
// used.
const TIOCSBRK: Ioctl<Write, c_void> = unsafe { Ioctl::classic(0x5427) };
const TIOCCBRK: Ioctl<Write, c_void> = unsafe { Ioctl::classic(0x5428) };
assert_eq!(TIOCSBRK.ioctl(&mut file).unwrap(), 0);
assert_eq!(TIOCCBRK.ioctl(&mut file).unwrap(), 0);

现代接口

为了减轻经典接口中的类型安全问题,Linux 内核开发了一套新的规范来开发 ioctl。我们称这些规范为现代接口。

现代 ioctl 接口始终接受一个结构体或整数的引用,并在失败时返回 -1,在成功时返回 0(或偶尔另一个正整数)。ioctl 请求号由四个参数构建

  • 一个 group(在内核宏中混乱地称为 type
  • 一个 nr(数字)
  • 一个 direction
  • (类型的大小)type

group 参数用作命名空间,以分组相关的 ioctl。它是一个整数值。

nr 参数是一个整数判别器,用于在 group 内唯一标识 ioctl

direction 参数标识数据流动的方向。如果数据从用户空间流向内核,这是 write 方向。如果数据从内核流向用户空间,这是 read 方向。双向流动的数据被标记为 write/read 方向。

type 参数标识应与该 ioctl 一起使用的类型。在内核 C 代码中,此类型仅直接用于使用类型的大小调整 ioctl 请求号。iocuddle 还使用此参数来提供类型安全。

使用 iocuddle 定义现代 ioctl 的样子如下

use iocuddle::*;

// Define the Group of KVM ioctls.
const KVM: Group = Group::new(0xAE);

// Define ioctls within the KVM group.
//
// The nr is passed to the direction-specific constructor.
const KVM_PPC_ALLOCATE_HTAB: Ioctl<WriteRead, &u32> = unsafe { KVM.write_read(0xa7) };
const KVM_X86_GET_MCE_CAP_SUPPORTED: Ioctl<Read, &u64> = unsafe { KVM.read(0x9d) };
const KVM_X86_SETUP_MCE: Ioctl<Write, &u64> = unsafe { KVM.write(0x9c) };

内核文档

有关 ioctl 流程的内核文档,请参阅内核源树中的以下文件:Documentation/userspace-api/ioctl/ioctl-number.rst

许可证:Apache-2.0

无运行时依赖