va_list
in Rust
(and why you never should)
rust / GitHub
Created by Dan Robertson / GitHub
unsafe
va_list
in C
Type used as a parameter to retrieve a variable arguments passed to a function.
Defined in section 7.16 of the C Standard.
Available in <stdarg.h>
.
va_start()
- Used to initialize the list
va_arg()
- Retrieve the next argument
of the given type from the list
va_end()
- End using the list
va_copy()
- Copy the lists state to a new list
add_n
that will return the sum
of up to n
integers passed to the function.
add_n(3, 10, 15, 17) == 42
add_n
vadd_n
va_list
in RustRust can currently call any possible C interface, and export almost any interface for C to call. Variadic functions represent one of the last remaining gaps in the latter.
fcntl()
printf()
exec()
va_list
's are VERY unsafe
/// The argument list of a C-compatible variadic function,
/// corresponding to the underlying C `va_list`. Opaque.
pub struct VaList<'a>(/* fields omitted */)
// Note: the lifetime on VaList is invariant
impl<'a> VaList<'a> {
/// Extract the next argument from the argument list. T must
/// have a type usable in an FFI interface.
pub unsafe fn arg<T>(&mut self) -> T;
/// Copy the argument list. Destroys the copy after the closure
/// returns.
pub unsafe fn copy<F, R>(&mut self, f: F) -> R
where F: for<'copy> FnOnce(VaList<'copy>) -> R;
}
vadd_n
in Rust
The type of an argument in a variable argument list will never be an integer type smaller than int, nor will it ever be float.
va_arg(ap, float);
Result: garbage or program abort()
va_start()
and va_end()
injected
automagically by compiler.
VaList
should not be able to escape the
function.
unsafe fn bad_idea(
fixed: u32,
mut ap: ...
) -> VaList {
ap
}
Result: Compiler Error
unsafe fn bad_idea(
mut ap: VaList
) {
let new_copy = ap.copy(|mut ap| {
ap
});
}
Result: Compiler Error
va_list
implementationsExpectation vs. Reality
Some OSes always use Character/Void Pointer
va_list ap
contains 10, (1ULL << 63)
First Arg Second Arg
type: int type: unsigned long long
value: 10 value: 0x8000000000000000 (1 << 63)
0xXXXXXXXXXX00 0xXXXXXXXXXX04
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
let x = ap.arg::<i32>(); // <--
let y = ap.arg::<u64>();
let bad = ap.arg::<i32>();
First Arg Second Arg
type: int type: unsigned long long
value: 10 value: 0x8000000000000000 (1 << 63)
0xXXXXXXXXXX00 0xXXXXXXXXXX04
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<u64>(); // <--
let bad = ap.arg::<i32>();
First Arg Second Arg
type: int type: unsigned long long
value: 10 value: 0x8000000000000000 (1 << 63)
0xXXXXXXXXXX00 0xXXXXXXXXXX04
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<u64>(); // y == 0x8000000000000000
let bad = ap.arg::<i32>(); // <--
First Arg Second Arg
type: int type: unsigned long long
value: 10 value: 0x8000000000000000 (1 << 63)
0xXXXXXXXXXX00 0xXXXXXXXXXX04
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<u64>(); // y == 0x8000000000000000
let bad = ap.arg::<i32>(); // OOB
First Arg Second Arg
type: int type: unsigned long long
value: 10 value: 0x8000000000000000 (1 << 63)
0xXXXXXXXXXX00 0xXXXXXXXXXX04 0xXXXXXXXXXX0c
| | | |
|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
| | | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 OOB
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<f64>(); // <--
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<f64>(); // y == -0.00
| | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<f64>(); // <--
gr_top | | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
vr_top | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| OOB |
^
|
location
let x = ap.arg::<i32>(); // x == 10
let y = ap.arg::<f64>(); // OOB
gr_top | | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| | |
0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80
^
|
location
vr_top | |
|----|----|----|----|----|----|----|----|----|----|----|----|
| OOB |
^
|
location
Be nice to your computer. Be careful with
va_list
's
Initial implementation:
Follow-up work: