Fix undefined behavior

Transmuting from an immutable to a mutable reference is undefined
behavior in Rust, with the exception of UnsafeCell which tells the
optimizer to not draw too many conclusions. Sadly UnsafeCell::get cannot
yet be used in statics (see https://github.com/rust-lang/rust/issues/51718),
so this works around that by making the statics into functions and
making C macros to call those.
This commit is contained in:
jD91mZM2
2018-06-22 21:00:52 +02:00
parent 3034114c14
commit ea24699798
4 changed files with 44 additions and 28 deletions
+3
View File
@@ -2,6 +2,9 @@
#define _BITS_STDIO_H
#define EOF (-1)
#define stdin __stdin()
#define stdout __stdout()
#define stderr __stderr()
int fprintf(FILE * stream, const char * fmt, ...);
int printf(const char * fmt, ...);
+28 -16
View File
@@ -1,12 +1,25 @@
use core::cell::UnsafeCell;
use core::sync::atomic::AtomicBool;
use core::ptr;
use super::{constants, internal, BUFSIZ, FILE, UNGET};
struct GlobalFile(UnsafeCell<FILE>);
impl GlobalFile {
const fn new(file: FILE) -> Self {
GlobalFile(UnsafeCell::new(file))
}
fn get(&self) -> *mut FILE {
self.0.get()
}
}
// statics need to be Sync
unsafe impl Sync for GlobalFile {}
#[allow(non_upper_case_globals)]
static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET];
#[allow(non_upper_case_globals)]
static mut default_stdin: FILE = FILE {
static mut default_stdin: GlobalFile = GlobalFile::new(FILE {
flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ,
rpos: ptr::null_mut(),
rend: ptr::null_mut(),
@@ -19,13 +32,13 @@ static mut default_stdin: FILE = FILE {
buf_char: -1,
unget: UNGET,
lock: AtomicBool::new(false),
};
});
#[allow(non_upper_case_globals)]
static mut default_stdout_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize];
#[allow(non_upper_case_globals)]
static mut default_stdout: FILE = FILE {
static mut default_stdout: GlobalFile = GlobalFile::new(FILE {
flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ,
rpos: ptr::null_mut(),
rend: ptr::null_mut(),
@@ -38,13 +51,13 @@ static mut default_stdout: FILE = FILE {
buf_char: b'\n' as i8,
unget: 0,
lock: AtomicBool::new(false),
};
});
#[allow(non_upper_case_globals)]
static mut default_stderr_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize];
#[allow(non_upper_case_globals)]
static mut default_stderr: FILE = FILE {
static mut default_stderr: GlobalFile = GlobalFile::new(FILE {
flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ,
rpos: ptr::null_mut(),
rend: ptr::null_mut(),
@@ -57,20 +70,19 @@ static mut default_stderr: FILE = FILE {
buf_char: -1,
unget: 0,
lock: AtomicBool::new(false),
};
});
// Don't ask me how the casting below works, I have no idea
// " as *const FILE as *mut FILE" rust pls
//
// -- Tommoa
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut stdin: *mut FILE = unsafe { &default_stdin } as *const FILE as *mut FILE;
pub extern "C" fn __stdin() -> *mut FILE {
unsafe { default_stdin.get() }
}
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut stdout: *mut FILE = unsafe { &default_stdout } as *const FILE as *mut FILE;
pub extern "C" fn __stdout() -> *mut FILE {
unsafe { default_stdout.get() }
}
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut stderr: *mut FILE = unsafe { &default_stderr } as *const FILE as *mut FILE;
pub extern "C" fn __stderr() -> *mut FILE {
unsafe { default_stderr.get() }
}
+9 -8
View File
@@ -2,6 +2,7 @@
#![no_std]
#![feature(alloc)]
#![feature(const_fn)]
extern crate alloc;
extern crate errno;
@@ -619,7 +620,7 @@ pub extern "C" fn getc(stream: &mut FILE) -> c_int {
/// Get a single char from `stdin`
#[no_mangle]
pub unsafe extern "C" fn getchar() -> c_int {
fgetc(&mut *stdin)
fgetc(&mut *__stdin())
}
/// Get a char from a stream without locking the stream
@@ -644,14 +645,14 @@ pub extern "C" fn getc_unlocked(stream: &mut FILE) -> c_int {
/// Get a char from `stdin` without locking `stdin`
#[no_mangle]
pub unsafe extern "C" fn getchar_unlocked() -> c_int {
getc_unlocked(&mut *stdin)
getc_unlocked(&mut *__stdin())
}
/// Get a string from `stdin`
#[no_mangle]
pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
use core::i32;
fgets(s, i32::MAX, &mut *stdin)
fgets(s, i32::MAX, &mut *__stdin())
}
/// Get an integer from `stream`
@@ -706,7 +707,7 @@ pub extern "C" fn putc(c: c_int, stream: &mut FILE) -> c_int {
/// Put a character `c` into `stdout`
#[no_mangle]
pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
fputc(c, &mut *stdout)
fputc(c, &mut *__stdout())
}
/// Put a character `c` into `stream` without locking `stream`
@@ -738,13 +739,13 @@ pub extern "C" fn putc_unlocked(c: c_int, stream: &mut FILE) -> c_int {
/// Put a character `c` into `stdout` without locking `stdout`
#[no_mangle]
pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
putc_unlocked(c, &mut *stdout)
putc_unlocked(c, &mut *__stdout())
}
/// Put a string `s` into `stdout`
#[no_mangle]
pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
let ret = (fputs(s, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
let ret = (fputs(s, &mut *__stdout()) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
if ret {
0
} else {
@@ -872,7 +873,7 @@ pub unsafe extern "C" fn vfprintf(file: &mut FILE, format: *const c_char, ap: va
#[no_mangle]
pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int {
vfprintf(&mut *stdout, format, ap)
vfprintf(&mut *__stdout(), format, ap)
}
#[no_mangle]
@@ -901,7 +902,7 @@ pub unsafe extern "C" fn vfscanf(file: &mut FILE, format: *const c_char, ap: va_
#[no_mangle]
pub unsafe extern "C" fn vscanf(format: *const c_char, ap: va_list) -> c_int {
vfscanf(&mut *stdin, format, ap)
vfscanf(&mut *__stdin(), format, ap)
}
#[no_mangle]
+4 -4
View File
@@ -69,10 +69,10 @@ unsafe fn parse_arg(
let print_error = |desc: &[u8]| {
// NOTE: we don't use fprintf to get around the usage of va_list
stdio::fputs(*argv as _, &mut *stdio::stderr);
stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
stdio::fputc(*current_arg as _, &mut *stdio::stderr);
stdio::fputc(b'\n' as _, &mut *stdio::stderr);
stdio::fputs(*argv as _, &mut *stdio::__stderr());
stdio::fputs(desc.as_ptr() as _, &mut *stdio::__stderr());
stdio::fputc(*current_arg as _, &mut *stdio::__stderr());
stdio::fputc(b'\n' as _, &mut *stdio::__stderr());
};
match find_option(*current_arg, optstring) {