diff options
author | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-07-26 11:52:12 +0200 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-07-29 18:18:54 +0200 |
commit | f742538d13375aa90ccaa787b06e07835bba5887 (patch) | |
tree | e2faec377641b1cadb98f4941339a6d6c38a4c17 | |
parent | b38dee3d3808d43d5c25dba30259462eed93b2c0 (diff) | |
download | gcc-f742538d13375aa90ccaa787b06e07835bba5887.zip gcc-f742538d13375aa90ccaa787b06e07835bba5887.tar.gz gcc-f742538d13375aa90ccaa787b06e07835bba5887.tar.bz2 |
unsafe: Report errors on calls to unsafe or extern functions and methods
-rw-r--r-- | gcc/rust/checks/errors/rust-unsafe-checker.cc | 87 | ||||
-rw-r--r-- | gcc/rust/checks/errors/rust-unsafe-checker.h | 7 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/unsafe6.rs | 14 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/unsafe7.rs | 9 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/unsafe8.rs | 14 | ||||
-rw-r--r-- | gcc/testsuite/rust/link/simple_function_0.rs | 1 |
6 files changed, 131 insertions, 1 deletions
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc index 9642baa..174901f0 100644 --- a/gcc/rust/checks/errors/rust-unsafe-checker.cc +++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc @@ -69,6 +69,7 @@ UnsafeChecker::check_use_of_static (HirId node_id, Location locus) return; auto maybe_static_mut = mappings.lookup_hir_item (node_id); + HirId extern_block; auto maybe_extern_static = mappings.lookup_hir_extern_item (node_id, &extern_block); @@ -81,6 +82,52 @@ UnsafeChecker::check_use_of_static (HirId node_id, Location locus) locus); } +static void +check_unsafe_call (HIR::Function *fn, Location locus, const std::string &kind) +{ + if (fn->get_qualifiers ().is_unsafe ()) + rust_error_at (locus, "call to unsafe %s requires unsafe function or block", + kind.c_str ()); +} + +static void +check_extern_call (HIR::ExternalItem *maybe_fn, HIR::ExternBlock *parent_block, + Location locus) +{ + // We have multiple operations to perform here + // 1. Is the item an actual function we're calling + // 2. Is the block it's defined in an FFI block or an `extern crate` block + // + // It is not unsafe to call into other crates, so items defined in an `extern + // crate` must be callable without being in an unsafe context. On the other + // hand, any function defined in a block with a specific ABI (even `extern + // "Rust"` blocks) is unsafe to call + + if (maybe_fn->get_extern_kind () == ExternalItem::ExternKind::Function) + rust_error_at (locus, + "call to extern function requires unsafe function or block"); +} + +void +UnsafeChecker::check_function_call (HirId node_id, Location locus) +{ + if (unsafe_context.is_in_context ()) + return; + + HirId parent_extern_block; + auto maybe_fn = mappings.lookup_hir_item (node_id); + auto maybe_extern + = mappings.lookup_hir_extern_item (node_id, &parent_extern_block); + + if (maybe_fn && maybe_fn->get_item_kind () == Item::ItemKind::Function) + check_unsafe_call (static_cast<Function *> (maybe_fn), locus, "function"); + + if (maybe_extern) + check_extern_call (static_cast<ExternalItem *> (maybe_extern), + mappings.lookup_hir_extern_block (parent_extern_block), + locus); +} + void UnsafeChecker::visit (IdentifierExpr &ident_expr) { @@ -299,6 +346,28 @@ UnsafeChecker::visit (StructExprStructBase &expr) void UnsafeChecker::visit (CallExpr &expr) { + auto fn = expr.get_fnexpr (); + if (!fn) + return; + + NodeId ast_node_id = fn->get_mappings ().get_nodeid (); + NodeId ref_node_id; + HirId definition_id; + + // There are no unsafe types, and functions are defined in the name resolver. + // If we can't find the name, then we're dealing with a type and should return + // early. + if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + + rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id)); + + // At this point we have the function's HIR Id. There are two checks we + // must perform: + // 1. The function is an unsafe one + // 2. The function is an extern one + check_function_call (definition_id, expr.get_locus ()); + if (expr.has_params ()) for (auto &arg : expr.get_arguments ()) arg->accept_vis (*this); @@ -306,7 +375,23 @@ UnsafeChecker::visit (CallExpr &expr) void UnsafeChecker::visit (MethodCallExpr &expr) -{} +{ + TyTy::BaseType *method_type; + context.lookup_type (expr.get_method_name ().get_mappings ().get_hirid (), + &method_type); + + auto fn = *static_cast<TyTy::FnType *> (method_type); + auto method = mappings.lookup_hir_implitem (fn.get_ref (), nullptr); + + if (!unsafe_context.is_in_context () && method) + check_unsafe_call (static_cast<Function *> (method), expr.get_locus (), + "method"); + + expr.get_receiver ()->accept_vis (*this); + + for (auto &arg : expr.get_arguments ()) + arg->accept_vis (*this); +} void UnsafeChecker::visit (FieldAccessExpr &expr) diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h index b9d06ef..087bdb7 100644 --- a/gcc/rust/checks/errors/rust-unsafe-checker.h +++ b/gcc/rust/checks/errors/rust-unsafe-checker.h @@ -40,7 +40,14 @@ private: */ void check_use_of_static (HirId node_id, Location locus); + /** + * Check if a call to an unsafe or external function is outside of an unsafe + * context + */ + void check_function_call (HirId node_id, Location locus); + StackedContexts<HirId> unsafe_context; + Resolver::TypeCheckContext &context; Resolver::Resolver &resolver; Analysis::Mappings &mappings; diff --git a/gcc/testsuite/rust/compile/unsafe6.rs b/gcc/testsuite/rust/compile/unsafe6.rs new file mode 100644 index 0000000..cf4b754 --- /dev/null +++ b/gcc/testsuite/rust/compile/unsafe6.rs @@ -0,0 +1,14 @@ +unsafe fn foo() {} +unsafe fn bar() { + foo(); +} + +fn main() { + foo(); // { dg-error "call to unsafe function" } + bar(); // { dg-error "call to unsafe function" } + + unsafe { + foo(); + bar(); + } +} diff --git a/gcc/testsuite/rust/compile/unsafe7.rs b/gcc/testsuite/rust/compile/unsafe7.rs new file mode 100644 index 0000000..a6b69e1 --- /dev/null +++ b/gcc/testsuite/rust/compile/unsafe7.rs @@ -0,0 +1,9 @@ +extern "C" { + fn printf(fmt: *const i8, ...); +} + +fn main() { + let s = "hey\0"; + + printf(s as *const str as *const i8); // { dg-error "call to extern function" } +} diff --git a/gcc/testsuite/rust/compile/unsafe8.rs b/gcc/testsuite/rust/compile/unsafe8.rs new file mode 100644 index 0000000..03fe491 --- /dev/null +++ b/gcc/testsuite/rust/compile/unsafe8.rs @@ -0,0 +1,14 @@ +struct S; + +impl S { + unsafe fn foo(self) {} +} + +fn main() { + let s = S; + s.foo(); // { dg-error "call to unsafe method" } + + unsafe { + s.foo(); + } +} diff --git a/gcc/testsuite/rust/link/simple_function_0.rs b/gcc/testsuite/rust/link/simple_function_0.rs index 5bd3238..5bd4926 100644 --- a/gcc/testsuite/rust/link/simple_function_0.rs +++ b/gcc/testsuite/rust/link/simple_function_0.rs @@ -3,5 +3,6 @@ use simple_function_1::test_func; fn main() -> i32 { let a = test_func(123); + // { dg-bogus "call to extern function" "" { xfail *-*-* } .-1 } a - 124 } |