Skip to content

Commit 2553623

Browse files
committed
Provide custom allocators that ensure that OOM results in an abort
(closes unsafety hole)
1 parent 16f57d1 commit 2553623

File tree

6 files changed

+67
-26
lines changed

6 files changed

+67
-26
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ default = ["builtin-lua"]
1818
# assumptions about how lua is built.
1919
builtin-lua = ["gcc"]
2020

21+
[dependencies]
22+
libc = { version = "0.2" }
23+
2124
[build-dependencies]
2225
gcc = { version = "0.3", optional = true }
2326

src/ffi.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
#![allow(non_snake_case)]
33

44
use std::ptr;
5-
use std::os::raw::*;
5+
use std::os::raw::{c_void, c_char, c_int, c_longlong, c_double};
66

77
pub type lua_Integer = c_longlong;
88
pub type lua_Number = c_double;
99

1010
pub enum lua_State {}
11-
pub type lua_Alloc = extern "C" fn(ud: *mut c_void,
12-
ptr: *mut c_void,
13-
osize: usize,
14-
nsize: usize)
15-
-> *mut c_void;
11+
pub type lua_Alloc = unsafe extern "C" fn(ud: *mut c_void,
12+
ptr: *mut c_void,
13+
osize: usize,
14+
nsize: usize)
15+
-> *mut c_void;
1616
pub type lua_KContext = *mut c_void;
1717
pub type lua_KFunction = unsafe extern "C" fn(state: *mut lua_State,
1818
status: c_int,
@@ -50,6 +50,8 @@ pub const LUA_TTHREAD: c_int = 8;
5050

5151
#[link(name = "lua5.3")]
5252
extern "C" {
53+
pub fn lua_newstate(alloc: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
54+
5355
pub fn lua_close(state: *mut lua_State);
5456
pub fn lua_callk(
5557
state: *mut lua_State,
@@ -132,7 +134,6 @@ extern "C" {
132134
pub fn luaopen_debug(state: *mut lua_State) -> c_int;
133135
pub fn luaopen_package(state: *mut lua_State) -> c_int;
134136

135-
pub fn luaL_newstate() -> *mut lua_State;
136137
pub fn luaL_openlibs(state: *mut lua_State);
137138
pub fn luaL_requiref(
138139
state: *mut lua_State,

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
extern crate libc;
2+
13
// Deny warnings inside doc tests / examples
24
#![doc(test(attr(deny(warnings))))]
35

src/lua.rs

+32-5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ use std::marker::PhantomData;
88
use std::collections::{HashMap, VecDeque};
99
use std::collections::hash_map::Entry as HashMapEntry;
1010
use std::os::raw::{c_char, c_int, c_void};
11+
use std::process;
1112
use std::string::String as StdString;
1213

14+
use libc;
15+
1316
use ffi;
1417
use error::*;
1518
use util::*;
@@ -208,10 +211,12 @@ impl<'lua> String<'lua> {
208211
/// # }
209212
/// ```
210213
pub fn to_str(&self) -> Result<&str> {
211-
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
212-
from: "string",
213-
to: "&str",
214-
message: Some(e.to_string()),
214+
str::from_utf8(self.as_bytes()).map_err(|e| {
215+
Error::FromLuaConversionError {
216+
from: "string",
217+
to: "&str",
218+
message: Some(e.to_string()),
219+
}
215220
})
216221
}
217222

@@ -1321,8 +1326,30 @@ impl Lua {
13211326
///
13221327
/// Also loads the standard library.
13231328
pub fn new() -> Lua {
1329+
unsafe extern "C" fn allocator(
1330+
_: *mut c_void,
1331+
ptr: *mut c_void,
1332+
_: usize,
1333+
nsize: usize,
1334+
) -> *mut c_void {
1335+
if nsize == 0 {
1336+
libc::free(ptr as *mut libc::c_void);
1337+
ptr::null_mut()
1338+
} else {
1339+
let p = libc::realloc(ptr as *mut libc::c_void, nsize);
1340+
if p.is_null() {
1341+
// We must abort on OOM, because otherwise this will result in an unsafe
1342+
// longjmp.
1343+
eprintln!("Out of memory in Lua allocation, aborting!");
1344+
process::abort()
1345+
} else {
1346+
p as *mut c_void
1347+
}
1348+
}
1349+
}
1350+
13241351
unsafe {
1325-
let state = ffi::luaL_newstate();
1352+
let state = ffi::lua_newstate(allocator, ptr::null_mut());
13261353

13271354
stack_guard(state, 0, || {
13281355
// Do not open the debug library, currently it can be used to cause unsafety.

src/tests.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ fn test_eval() {
7070
assert_eq!(lua.eval::<i32>("return 1 + 2", None).unwrap(), 3);
7171
match lua.eval::<()>("if true then", None) {
7272
Err(Error::SyntaxError { incomplete_input: true, .. }) => {}
73-
r => panic!("expected SyntaxError with incomplete_input=true, got {:?}", r),
73+
r => {
74+
panic!(
75+
"expected SyntaxError with incomplete_input=true, got {:?}",
76+
r
77+
)
78+
}
7479
}
7580
}
7681

src/util.rs

+16-13
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,8 @@ pub unsafe fn handle_error(state: *mut ffi::lua_State, err: c_int) -> Result<()>
295295
Error::RuntimeError(err_string)
296296
}
297297
ffi::LUA_ERRMEM => {
298-
// TODO: We should provide lua with custom allocators that guarantee aborting on
299-
// OOM, instead of relying on the system allocator. Currently, this is a
300-
// theoretical soundness bug, because an OOM could trigger a longjmp out of
301-
// arbitrary rust code that is unprotectedly calling a lua function marked as
302-
// potentially causing memory errors, which is most of them.
298+
// This should be impossible, as we set the lua allocator to one that aborts
299+
// instead of failing.
303300
eprintln!("Lua memory error, aborting!");
304301
process::abort()
305302
}
@@ -381,10 +378,13 @@ pub unsafe fn pcall_with_traceback(
381378
let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut()))
382379
.to_string_lossy()
383380
.into_owned();
384-
push_wrapped_error(state, Error::CallbackError {
385-
traceback,
386-
cause: Arc::new(error),
387-
});
381+
push_wrapped_error(
382+
state,
383+
Error::CallbackError {
384+
traceback,
385+
cause: Arc::new(error),
386+
},
387+
);
388388
ffi::lua_remove(state, -2);
389389
} else if !is_wrapped_panic(state, 1) {
390390
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());
@@ -420,10 +420,13 @@ pub unsafe fn resume_with_traceback(
420420
.to_str()
421421
.unwrap_or_else(|_| "<could not capture traceback>")
422422
.to_owned();
423-
push_wrapped_error(state, Error::CallbackError {
424-
traceback,
425-
cause: Arc::new(error),
426-
});
423+
push_wrapped_error(
424+
state,
425+
Error::CallbackError {
426+
traceback,
427+
cause: Arc::new(error),
428+
},
429+
);
427430
ffi::lua_remove(state, -2);
428431
} else if !is_wrapped_panic(state, 1) {
429432
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());

0 commit comments

Comments
 (0)