blob: b5a400a8bf2557af46b39727c6e7dba97055328f [file] [log] [blame] [edit]
//@ build-fail
#![feature(c_variadic)]
#![feature(const_c_variadic)]
#![feature(const_trait_impl)]
#![feature(const_destruct)]
#![feature(const_clone)]
use std::ffi::VaList;
use std::mem::MaybeUninit;
const unsafe extern "C" fn read_n<const N: usize>(mut ap: ...) {
let mut i = N;
while i > 0 {
i -= 1;
let _ = ap.arg::<i32>();
}
}
unsafe fn read_too_many() {
// None passed, none read.
const { read_n::<0>() }
// One passed, none read. Ignoring arguments is fine.
const { read_n::<0>(1) }
// None passed, one read.
const { read_n::<1>() }
//~^ ERROR more C-variadic arguments read than were passed
// One passed, two read.
const { read_n::<2>(1) }
//~^ ERROR more C-variadic arguments read than were passed
}
const unsafe extern "C" fn read_as<T: core::ffi::VaArgSafe>(mut ap: ...) -> T {
ap.arg::<T>()
}
unsafe fn read_cast() {
const { read_as::<i32>(1i32) };
const { read_as::<u32>(1u32) };
const { read_as::<i32>(1i32, 2u64, 3.0f64) };
const { read_as::<u32>(1u32, 2u64, 3.0f64) };
const { read_as::<i64>(1i64) };
const { read_as::<u64>(1u64) };
const { read_as::<u32>(1i32) };
//~^ ERROR va_arg type mismatch: requested `u32`, but next argument is `i32`
const { read_as::<i32>(1u32) };
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u32`
const { read_as::<i32>(1u64) };
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u64`
const { read_as::<f64>(1i32) };
//~^ ERROR va_arg type mismatch: requested `f64`, but next argument is `i32`
const { read_as::<*const u8>(1i32) };
//~^ ERROR va_arg type mismatch: requested `*const u8`, but next argument is `i32`
}
fn use_after_free() {
const unsafe extern "C" fn helper(ap: ...) -> [u8; size_of::<VaList>()] {
unsafe { std::mem::transmute(ap) }
}
const {
unsafe {
let ap = helper(1, 2, 3);
let mut ap = std::mem::transmute::<_, VaList>(ap);
ap.arg::<i32>();
//~^ ERROR memory access failed: ALLOC0 has been freed, so this pointer is dangling [E0080]
}
};
}
fn manual_copy_drop() {
const unsafe extern "C" fn helper(ap: ...) {
// A copy created using Clone is valid, and can be used to read arguments.
let mut copy = ap.clone();
assert!(copy.arg::<i32>() == 1i32);
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
// Using the copy is actually fine.
let _ = copy.arg::<i32>();
drop(copy);
// But then using the original is UB.
drop(ap);
}
const { unsafe { helper(1, 2, 3) } };
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
}
fn manual_copy_forget() {
const unsafe extern "C" fn helper(ap: ...) {
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
// Using the copy is actually fine.
let _ = copy.arg::<i32>();
std::mem::forget(copy);
// The read (via `copy`) deallocated the original allocation.
drop(ap);
}
const { unsafe { helper(1, 2, 3) } };
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
}
fn manual_copy_read() {
const unsafe extern "C" fn helper(mut ap: ...) {
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
// Reading from `ap` after reading from `copy` is UB.
let _ = copy.arg::<i32>();
let _ = ap.arg::<i32>();
}
const { unsafe { helper(1, 2, 3) } };
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
}
fn drop_of_invalid() {
const {
let mut invalid: MaybeUninit<VaList> = MaybeUninit::zeroed();
let ap = unsafe { invalid.assume_init() };
}
//~^ ERROR pointer not dereferenceable: pointer must point to some allocation, but got null pointer [E0080]
}
fn main() {
unsafe {
read_too_many();
read_cast();
manual_copy_read();
manual_copy_drop();
manual_copy_forget();
drop_of_invalid();
}
}