Skip to content

Commit 9fb6b09

Browse files
authored
fix: prevent declarations of blackbox functions outside of the stdlib (#4177)
# Description ## Problem\* Resolves <!-- Link to GitHub Issue --> ## Summary\* I found this [semaphore lib which is redeclaring pedersen for some reason](https://github.com/siosw/semaphore-noir/blob/main/circuits/src/identity.nr). Let's ~break it!~ give a nice error message to tell users that they're doing something wrong here. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent 8f70e57 commit 9fb6b09

7 files changed

Lines changed: 50 additions & 0 deletions

File tree

compiler/noirc_frontend/src/hir/resolution/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pub enum ResolverError {
8484
InvalidTypeForEntryPoint { span: Span },
8585
#[error("Nested slices are not supported")]
8686
NestedSlices { span: Span },
87+
#[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")]
88+
LowLevelFunctionOutsideOfStdlib { ident: Ident },
8789
}
8890

8991
impl ResolverError {
@@ -311,6 +313,11 @@ impl From<ResolverError> for Diagnostic {
311313
"Try to use a constant sized array instead".into(),
312314
span,
313315
),
316+
ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error(
317+
"Definition of low-level function outside of standard library".into(),
318+
"Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(),
319+
ident.span(),
320+
),
314321
}
315322
}
316323
}

compiler/noirc_frontend/src/hir/resolution/resolver.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,13 @@ impl<'a> Resolver<'a> {
908908
position: PubPosition::ReturnType,
909909
});
910910
}
911+
let is_low_level_function =
912+
func.attributes().function.as_ref().map_or(false, |func| func.is_low_level());
913+
if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function {
914+
let error =
915+
ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() };
916+
self.push_err(error);
917+
}
911918

912919
// 'pub' is required on return types for entry point functions
913920
if self.is_entry_point_function(func)

compiler/noirc_frontend/src/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ mod test {
5252
) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) {
5353
let root = std::path::Path::new("/");
5454
let fm = FileManager::new(root);
55+
5556
let mut context = Context::new(fm, Default::default());
5657
context.def_interner.populate_dummy_operator_traits();
5758
let root_file_id = FileId::dummy();
5859
let root_crate_id = context.crate_graph.add_crate_root(root_file_id);
60+
5961
let (program, parser_errors) = parse_program(src);
6062
let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id));
6163
remove_experimental_warnings(&mut errors);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "builtin_function_declaration"
3+
type = "bin"
4+
authors = [""]
5+
compiler_version = ">=0.23.0"
6+
7+
[dependencies]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This test prevents users from trying to create their own builtin functions as these should only exist in the stdlib.
2+
3+
// This would otherwise be a perfectly valid declaration of the `to_le_bits` builtin function
4+
#[builtin(to_le_bits)]
5+
fn to_le_bits(_x: Field, _bit_size: u32) -> [u1] {}
6+
7+
fn main(x: Field) -> pub u1 {
8+
let bits = to_le_bits(x, 100);
9+
bits[0]
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "foreign_function_declaration"
3+
type = "bin"
4+
authors = [""]
5+
compiler_version = ">=0.23.0"
6+
7+
[dependencies]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This test prevents users from trying to create their own blackbox functions as these should only exist in the stdlib.
2+
3+
// This would otherwise be a perfectly valid definition of the `pedersen_hash` black box function,
4+
// however executing the circuit results in an unhelpful ICE.
5+
#[foreign(pedersen_hash)]
6+
fn my_pedersen_hash<N>(_input: [Field; N]) -> Field {}
7+
8+
fn main() -> pub Field {
9+
my_pedersen_hash([1])
10+
}

0 commit comments

Comments
 (0)