Expand description
Compile-time IRQL safety for Windows kernel drivers.
IRQL violations are caught at compile time using Rust’s type system — zero runtime cost, zero binary size overhead.
§Quick start
use irql::{irql, Dispatch, Passive};
#[irql(max = Dispatch)]
fn acquire_spinlock() { /* … */ }
#[irql(at = Passive)]
fn driver_entry() {
call_irql!(acquire_spinlock());
}§The #[irql()] attribute
| Form | Meaning |
|---|---|
#[irql(at = Level)] | Fixed entry point — known IRQL, no generic |
#[irql(max = Level)] | Callable from Level or below |
#[irql(min = A, max = B)] | Callable in the range [A, B] |
max is required unless using at — it defines the ceiling that
call_irql! relies on. min is optional and adds a floor constraint.
at is mutually exclusive with min/max.
Works on functions, inherent impl blocks, and trait impl blocks.
§IRQL levels
| Value | Type | Description |
|---|---|---|
| 0 | Passive | Normal thread execution; paged memory OK |
| 1 | Apc | APC delivery |
| 2 | Dispatch | DPC / spinlock level |
| 3–26 | Dirql | Device interrupt levels |
| 27 | Profile | Profiling timer |
| 28 | Clock | Clock interrupt |
| 29 | Ipi | Inter-processor interrupt |
| 30 | Power | Power failure |
| 31 | High | Highest — machine check |
Each level is a zero-sized marker type implementing IrqlLevel.
§The golden rule
IRQL can only stay the same or be raised, never lowered.
Attempting to call a lower-IRQL function produces a compile error:
use irql::{irql, Dispatch, Passive};
#[irql(max = Passive)]
fn passive_only() {}
#[irql(max = Dispatch)]
fn at_dispatch() {
call_irql!(passive_only()); // ERROR: cannot lower IRQL
}§IRQL-aware allocation (alloc feature)
Enable with irql = { features = ["alloc"] }.
Requires a nightly Rust compiler — depends on unstable
allocator_api, vec_push_within_capacity, auto_traits, and
negative_impls. Add a rust-toolchain.toml with
channel = "nightly" to your project.
Pool allocations automatically use ExAllocatePool2 / ExFreePool
from wdk-sys in WDM/KMDF driver
builds. Outside a WDK build (e.g. testing), the global allocator is
used as a fallback.
IrqlBox and IrqlVec enforce pool rules at compile time:
#[irql(max = Passive)]
fn example() -> Result<(), AllocError> {
let data = call_irql!(IrqlBox::new(42))?; // PagedPool (automatic)
let val = call_irql!(data.get());
let v = irql_vec![1, 2, 3]?;
Ok(())
}§FFI interop
IrqlBox provides raw pointer methods for passing allocations to
kernel APIs and C callbacks:
let b = call_irql!(IrqlBox::new(data))?;
let ptr = b.into_raw(); // pass to kernel API
// later, reconstruct:
let b = unsafe { IrqlBox::<_, PagedPool>::from_raw(ptr) };§Drop safety
Paged-pool containers (IrqlBox<T, PagedPool>, IrqlVec<T, PagedPool>)
must not be dropped at Dispatch or above. This is enforced at compile
time via SafeToDropAt* auto traits (enabled automatically with alloc).
The #[irql] macro injects T: SafeToDropAt<Level> bounds on by-value
parameters, so passing a paged-pool value (or any struct containing one)
by value into code at Dispatch+ is a compile error.
References (&IrqlBox) are not gated — they don’t trigger a drop.
Use IrqlBox::leak() or IrqlBox::into_raw() when you need to transfer
ownership across an IRQL boundary.
§Safety
All checks are compile-time only. You must ensure:
- Entry points (
#[irql(at = …)]) match the actual runtime IRQL. - IRQL-raising operations (spinlocks, etc.) are properly modelled.
Structs§
- Apc
- IRQL level: Apc.
- Clock
- IRQL level: Clock.
- Dirql
- IRQL level: Dirql.
- Dispatch
- IRQL level: Dispatch.
- High
- IRQL level: High.
- Ipi
- IRQL level: Ipi.
- Passive
- IRQL level: Passive.
- Power
- IRQL level: Power.
- Profile
- IRQL level: Profile.
Traits§
- Irql
CanLower To - The current IRQL is at or above
Target. - Irql
CanRaise To - The current IRQL can be raised to
Target. - IrqlFn
- IRQL-safe analogue of
Fn— callable multiple times via shared reference. - Irql
FnMut - IRQL-safe analogue of
FnMut— callable multiple times via mutable reference. - Irql
FnOnce - IRQL-safe analogue of
FnOnce— callable exactly once, consumingself. - Irql
Level - Marker trait implemented by all IRQL level types.
- Safe
ToDrop At Selfcan be safely dropped at IRQLLevel.
Attribute Macros§
- irql
- Compile-time IRQL constraint.