From 65cfbaaa4b6194937478e98c773ed6ed56a6d70f Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 31 Jan 2024 22:24:51 +0100 Subject: [PATCH] Safer Rust (catch panic with catch_unwind()) Crossing boundaries of multiple languages is tricky, but we can do at least something about this, in particular, use catch_unwind() [1] to catch possible panic!()s. [1]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html Signed-off-by: Azat Khuzhin --- rust/prql/src/lib.rs | 21 +++++++++++++++++++-- rust/skim/src/lib.rs | 22 +++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/rust/prql/src/lib.rs b/rust/prql/src/lib.rs index fb71d62d527..d51acfbd485 100644 --- a/rust/prql/src/lib.rs +++ b/rust/prql/src/lib.rs @@ -2,6 +2,7 @@ use prql_compiler::sql::Dialect; use prql_compiler::{Options, Target}; use std::ffi::{c_char, CString}; use std::slice; +use std::panic; fn set_output(result: String, out: *mut *mut u8, out_size: *mut u64) { assert!(!out_size.is_null()); @@ -13,8 +14,7 @@ fn set_output(result: String, out: *mut *mut u8, out_size: *mut u64) { *out_ptr = CString::new(result).unwrap().into_raw() as *mut u8; } -#[no_mangle] -pub unsafe extern "C" fn prql_to_sql( +pub unsafe extern "C" fn prql_to_sql_impl( query: *const u8, size: u64, out: *mut *mut u8, @@ -50,6 +50,23 @@ pub unsafe extern "C" fn prql_to_sql( } } +#[no_mangle] +pub unsafe extern "C" fn prql_to_sql( + query: *const u8, + size: u64, + out: *mut *mut u8, + out_size: *mut u64, +) -> i64 { + let ret = panic::catch_unwind(|| { + return prql_to_sql_impl(query, size, out, out_size); + }); + return match ret { + // NOTE: using cxxbridge we can return proper Result<> type. + Err(_err) => 1, + Ok(res) => res, + } +} + #[no_mangle] pub unsafe extern "C" fn prql_free_pointer(ptr_to_free: *mut u8) { std::mem::drop(CString::from_raw(ptr_to_free as *mut c_char)); diff --git a/rust/skim/src/lib.rs b/rust/skim/src/lib.rs index 2221ed63df4..a20b1b35033 100644 --- a/rust/skim/src/lib.rs +++ b/rust/skim/src/lib.rs @@ -1,6 +1,7 @@ use skim::prelude::*; use term::terminfo::TermInfo; use cxx::{CxxString, CxxVector}; +use std::panic; #[cxx::bridge] mod ffi { @@ -36,7 +37,7 @@ impl SkimItem for Item { } } -fn skim(prefix: &CxxString, words: &CxxVector) -> Result { +fn skim_impl(prefix: &CxxString, words: &CxxVector) -> Result { // Let's check is terminal available. To avoid panic. if let Err(err) = TermInfo::from_env() { return Err(format!("{}", err)); @@ -89,3 +90,22 @@ fn skim(prefix: &CxxString, words: &CxxVector) -> Result) -> Result { + let ret = panic::catch_unwind(|| { + return skim_impl(prefix, words); + }); + return match ret { + Err(err) => { + let e = if let Some(s) = err.downcast_ref::() { + format!("{}", s) + } else if let Some(s) = err.downcast_ref::<&str>() { + format!("{}", s) + } else { + format!("Unknown panic type: {:?}", err.type_id()) + }; + Err(format!("Rust panic: {:?}", e)) + }, + Ok(res) => res, + } +}