lib: catch unwinds in RepoCheckoutFilter

This commit is contained in:
Felix Krull 2019-06-13 19:31:37 +02:00 committed by Colin Walters
parent 315cd5394e
commit 87b34be855
2 changed files with 36 additions and 4 deletions

View File

@ -91,7 +91,7 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
if let Some(filter) = &self.filter {
options.filter_user_data = filter.to_glib_none().0;
options.filter = Some(repo_checkout_filter::filter_trampoline);
options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe);
}
Stash(
@ -187,7 +187,7 @@ mod tests {
);
assert_eq!((*ptr).unused_ints, [0; 6]);
assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline));
assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe));
assert_eq!(
(*ptr).filter_user_data,
options.filter.as_ref().unwrap().to_glib_none().0,

View File

@ -3,7 +3,10 @@ use glib::translate::*;
use glib_sys::gpointer;
use libc::c_char;
use ostree_sys::*;
use std::any::Any;
use std::panic::catch_unwind;
use std::path::{Path, PathBuf};
use std::process::abort;
/// A filter callback to decide which files to checkout from a [Repo](struct.Repo.html). The
/// function is called for every directory and file in the dirtree.
@ -62,13 +65,12 @@ impl FromGlibPtrNone<gpointer> for &RepoCheckoutFilter {
///
/// # Panics
/// If any parameter is a null pointer, the function panics.
pub(super) unsafe extern "C" fn filter_trampoline(
unsafe extern "C" fn filter_trampoline(
repo: *mut OstreeRepo,
path: *const c_char,
stat: *mut libc::stat,
user_data: gpointer,
) -> OstreeRepoCheckoutFilterResult {
// TODO: handle unwinding
// We can't guarantee it's a valid pointer, but we can make sure it's not null.
assert!(!stat.is_null());
let stat = &*stat;
@ -85,6 +87,36 @@ pub(super) unsafe extern "C" fn filter_trampoline(
result.to_glib()
}
pub(super) unsafe extern "C" fn filter_trampoline_unwindsafe(
repo: *mut OstreeRepo,
path: *const c_char,
stat: *mut libc::stat,
user_data: gpointer,
) -> OstreeRepoCheckoutFilterResult {
// Unwinding across an FFI boundary is Undefined Behavior and we have no other way to communicate
// the error. We abort() safely to avoid further problems.
let result = catch_unwind(move || filter_trampoline(repo, path, stat, user_data));
result.unwrap_or_else(|panic| {
print_panic(panic);
abort()
})
}
fn print_panic(panic: Box<dyn Any>) {
eprintln!("A Rust callback invoked by C code panicked.");
eprintln!("Unwinding across FFI boundaries is Undefined Behavior so abort() will be called.");
let msg = {
if let Some(s) = panic.as_ref().downcast_ref::<&str>() {
s
} else if let Some(s) = panic.as_ref().downcast_ref::<String>() {
s
} else {
"UNABLE TO SHOW VALUE OF PANIC"
}
};
eprintln!("Panic value: {}", msg);
}
#[cfg(test)]
mod tests {
use super::*;