aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Cohen <arthur.cohen@embecosm.com>2022-07-26 11:52:12 +0200
committerArthur Cohen <arthur.cohen@embecosm.com>2022-07-29 18:18:54 +0200
commitf742538d13375aa90ccaa787b06e07835bba5887 (patch)
treee2faec377641b1cadb98f4941339a6d6c38a4c17
parentb38dee3d3808d43d5c25dba30259462eed93b2c0 (diff)
downloadgcc-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.cc87
-rw-r--r--gcc/rust/checks/errors/rust-unsafe-checker.h7
-rw-r--r--gcc/testsuite/rust/compile/unsafe6.rs14
-rw-r--r--gcc/testsuite/rust/compile/unsafe7.rs9
-rw-r--r--gcc/testsuite/rust/compile/unsafe8.rs14
-rw-r--r--gcc/testsuite/rust/link/simple_function_0.rs1
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
}