aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cortex-m-rt/.travis.yml5
-rw-r--r--cortex-m-rt/CHANGELOG.md9
-rw-r--r--cortex-m-rt/Cargo.toml9
-rwxr-xr-xcortex-m-rt/ci/script.sh1
-rw-r--r--cortex-m-rt/examples/rand.rs24
-rw-r--r--cortex-m-rt/macros/Cargo.toml5
-rw-r--r--cortex-m-rt/macros/src/lib.rs288
-rw-r--r--cortex-m-rt/tests/compile-fail/interrupt-invalid.rs2
-rw-r--r--cortex-m-rt/tests/compile-fail/non-static-resource.rs43
-rw-r--r--cortex-m-rt/tests/compile-fail/unsafe-init-static.rs45
10 files changed, 244 insertions, 187 deletions
diff --git a/cortex-m-rt/.travis.yml b/cortex-m-rt/.travis.yml
index 90cafba..9bb1fc2 100644
--- a/cortex-m-rt/.travis.yml
+++ b/cortex-m-rt/.travis.yml
@@ -63,11 +63,6 @@ script:
after_script: set +e
-cache: cargo
-
-before_cache:
- - chmod -R a+r $HOME/.cargo;
-
branches:
only:
- master
diff --git a/cortex-m-rt/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md
index 5059d3d..9325bc9 100644
--- a/cortex-m-rt/CHANGELOG.md
+++ b/cortex-m-rt/CHANGELOG.md
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+### Changed
+
+- Macros now generate a second trampoline function instead of randomizing the
+ function's symbol name. This makes the build deterministic.
+- [breaking-change] `static mut` resources no longer have `'static` lifetime
+ except in the `#[entry]` function (this is a soundness fix; see [#212]).
+
+[#212]: https://github.com/rust-embedded/cortex-m-rt/issues/212
+
## [v0.6.10] - 2019-07-25
### Fixed
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml
index 7bf2283..3b03993 100644
--- a/cortex-m-rt/Cargo.toml
+++ b/cortex-m-rt/Cargo.toml
@@ -24,11 +24,6 @@ cortex-m = "0.6"
panic-halt = "0.2.0"
cortex-m-semihosting = "0.3"
-[dev-dependencies.rand]
-default-features = false
-features = ["small_rng"]
-version = "0.7"
-
[target.'cfg(not(target_os = "none"))'.dev-dependencies]
compiletest_rs = "0.4.0"
@@ -36,6 +31,10 @@ compiletest_rs = "0.4.0"
name = "device"
required-features = ["device"]
+[[test]]
+name = "compiletest"
+required-features = ["device"]
+
[features]
device = []
diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh
index 6b4c759..40ff22a 100755
--- a/cortex-m-rt/ci/script.sh
+++ b/cortex-m-rt/ci/script.sh
@@ -23,7 +23,6 @@ main() {
override-exception
pre_init
qemu
- rand
state
unsafe-default-handler
unsafe-entry
diff --git a/cortex-m-rt/examples/rand.rs b/cortex-m-rt/examples/rand.rs
deleted file mode 100644
index 358868b..0000000
--- a/cortex-m-rt/examples/rand.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-//! Use rand crate to ensure it's configured for no_std compatbility
-
-#![deny(warnings)]
-#![no_main]
-#![no_std]
-
-extern crate cortex_m_rt as rt;
-use rt::entry;
-
-extern crate panic_halt;
-
-extern crate rand;
-use rand::Rng;
-use rand::SeedableRng;
-
-// the program entry point
-#[entry]
-fn main() -> ! {
- let seed: [u8; 16] = [0; 16];
- let mut rng = rand::rngs::SmallRng::from_seed(seed);
- let _ = rng.gen::<u32>();
-
- loop {}
-}
diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml
index 4e46aaa..62a3836 100644
--- a/cortex-m-rt/macros/Cargo.toml
+++ b/cortex-m-rt/macros/Cargo.toml
@@ -21,10 +21,5 @@ proc-macro2 = "1.0"
features = ["extra-traits", "full"]
version = "1.0"
-[dependencies.rand]
-default-features = false
-features = ["small_rng"]
-version = "0.7"
-
[dev-dependencies]
cortex-m-rt = { path = "..", version = "0.6" }
diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs
index bf930d0..b6b0fe4 100644
--- a/cortex-m-rt/macros/src/lib.rs
+++ b/cortex-m-rt/macros/src/lib.rs
@@ -4,17 +4,13 @@ extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
-use rand::{Rng, SeedableRng};
+use quote::quote;
use std::collections::HashSet;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::time::{SystemTime, UNIX_EPOCH};
+use std::iter;
use syn::{
parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn,
ItemStatic, ReturnType, Stmt, Type, Visibility,
};
-use quote::quote;
-
-static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
/// Attribute to declare the entry point of the program
///
@@ -75,7 +71,7 @@ static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
/// ```
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
- let f = parse_macro_input!(input as ItemFn);
+ let mut f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.sig.constness.is_none()
@@ -109,44 +105,56 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
}
// XXX should we blacklist other attributes?
- let attrs = f.attrs;
- let unsafety = f.sig.unsafety;
- let hash = random_ident();
let (statics, stmts) = match extract_static_muts(f.block.stmts) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};
- let vars = statics
- .into_iter()
- .map(|var| {
- let (ref cfgs, ref attrs) = extract_cfgs(var.attrs);
- let ident = var.ident;
- let ty = var.ty;
- let expr = var.expr;
+ f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
+ f.sig.inputs.extend(statics.iter().map(|statik| {
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let attrs = &statik.attrs;
- quote!(
- #[allow(non_snake_case)]
+ // Note that we use an explicit `'static` lifetime for the entry point arguments. This makes
+ // it more flexible, and is sound here, since the entry will not be called again, ever.
+ syn::parse::<FnArg>(
+ quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(),
+ )
+ .unwrap()
+ }));
+ f.block.stmts = stmts;
+
+ let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
+ let ident = &f.sig.ident;
+
+ let resource_args = statics
+ .iter()
+ .map(|statik| {
+ let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let expr = &statik.expr;
+ quote! {
#(#cfgs)*
- let #ident: &'static mut #ty = unsafe {
+ {
#(#attrs)*
- #(#cfgs)*
static mut #ident: #ty = #expr;
-
&mut #ident
- };
- )
+ }
+ }
})
.collect::<Vec<_>>();
quote!(
#[export_name = "main"]
- #(#attrs)*
- pub #unsafety fn #hash() -> ! {
- #(#vars)*
-
- #(#stmts)*
+ pub unsafe extern "C" fn #tramp_ident() {
+ #ident(
+ #(#resource_args),*
+ )
}
+
+ #f
)
.into()
}
@@ -265,7 +273,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
- let f = parse_macro_input!(input as ItemFn);
+ let mut f = parse_macro_input!(input as ItemFn);
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
@@ -274,7 +282,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
}
let fspan = f.span();
- let ident = f.sig.ident;
+ let ident = f.sig.ident.clone();
enum Exception {
DefaultHandler,
@@ -298,12 +306,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
};
// XXX should we blacklist other attributes?
- let attrs = f.attrs;
- let block = f.block;
- let stmts = block.stmts;
- let unsafety = f.sig.unsafety;
- let hash = random_ident();
match exn {
Exception::DefaultHandler => {
let valid_signature = f.sig.constness.is_none()
@@ -331,23 +334,23 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}
- let arg = match f.sig.inputs[0] {
- FnArg::Typed(ref arg) => arg,
- _ => unreachable!(),
- };
+ f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
+ let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
+ let ident = &f.sig.ident;
quote!(
#[export_name = #ident_s]
- #(#attrs)*
- pub #unsafety extern "C" fn #hash() {
+ pub unsafe extern "C" fn #tramp_ident() {
extern crate core;
const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
- let #arg = unsafe { core::ptr::read(SCB_ICSR) as u8 as i16 - 16 };
+ let irqn = unsafe { core::ptr::read(SCB_ICSR) as u8 as i16 - 16 };
- #(#stmts)*
+ #ident(irqn)
}
+
+ #f
)
.into()
}
@@ -383,24 +386,18 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}
- let arg = match f.sig.inputs[0] {
- FnArg::Typed(ref arg) => arg,
- _ => unreachable!(),
- };
-
- let pat = &arg.pat;
+ f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
+ let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
+ let ident = &f.sig.ident;
quote!(
#[export_name = "HardFault"]
#[link_section = ".HardFault.user"]
- #(#attrs)*
- pub #unsafety extern "C" fn #hash(#arg) -> ! {
- extern crate cortex_m_rt;
-
- // further type check of the input argument
- let #pat: &cortex_m_rt::ExceptionFrame = #pat;
- #(#stmts)*
+ pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
+ #ident(frame)
}
+
+ #f
)
.into()
}
@@ -431,46 +428,63 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}
- let (statics, stmts) = match extract_static_muts(stmts) {
+ let (statics, stmts) = match extract_static_muts(f.block.stmts) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};
- let vars = statics
- .into_iter()
- .map(|var| {
- let (ref cfgs, ref attrs) = extract_cfgs(var.attrs);
- let ident = var.ident;
- let ty = var.ty;
- let expr = var.expr;
+ f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
+ f.sig.inputs.extend(statics.iter().map(|statik| {
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let attrs = &statik.attrs;
+ syn::parse::<FnArg>(
+ quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
+ )
+ .unwrap()
+ }));
+ f.block.stmts = iter::once(
+ syn::parse2(quote! {{
+ extern crate cortex_m_rt;
- quote!(
- #[allow(non_snake_case)]
+ // check that this exception actually exists
+ cortex_m_rt::Exception::#ident;
+ }})
+ .unwrap(),
+ )
+ .chain(stmts)
+ .collect();
+
+ let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
+ let ident = &f.sig.ident;
+
+ let resource_args = statics
+ .iter()
+ .map(|statik| {
+ let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let expr = &statik.expr;
+ quote! {
#(#cfgs)*
- let #ident: &mut #ty = unsafe {
+ {
#(#attrs)*
- #(#cfgs)*
static mut #ident: #ty = #expr;
-
&mut #ident
- };
- )
+ }
+ }
})
.collect::<Vec<_>>();
quote!(
#[export_name = #ident_s]
- #(#attrs)*
- pub #unsafety extern "C" fn #hash() {
- extern crate cortex_m_rt;
-
- // check that this exception actually exists
- cortex_m_rt::Exception::#ident;
-
- #(#vars)*
-
- #(#stmts)*
+ pub unsafe extern "C" fn #tramp_ident() {
+ #ident(
+ #(#resource_args),*
+ )
}
+
+ #f
)
.into()
}
@@ -547,7 +561,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
- let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
+ let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
@@ -556,14 +570,10 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
}
let fspan = f.span();
- let ident = f.sig.ident;
+ let ident = f.sig.ident.clone();
let ident_s = ident.to_string();
// XXX should we blacklist other attributes?
- let attrs = f.attrs;
- let block = f.block;
- let stmts = block.stmts;
- let unsafety = f.sig.unsafety;
let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
@@ -590,44 +600,61 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}
- let (statics, stmts) = match extract_static_muts(stmts) {
+ let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};
- let vars = statics
- .into_iter()
- .map(|var| {
- let (ref cfgs, ref attrs) = extract_cfgs(var.attrs);
- let ident = var.ident;
- let ty = var.ty;
- let expr = var.expr;
-
- quote!(
- #[allow(non_snake_case)]
+ f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
+ f.sig.inputs.extend(statics.iter().map(|statik| {
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let attrs = &statik.attrs;
+ syn::parse::<FnArg>(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into())
+ .unwrap()
+ }));
+ f.block.stmts = iter::once(
+ syn::parse2(quote! {{
+ extern crate cortex_m_rt;
+
+ // Check that this interrupt actually exists
+ interrupt::#ident;
+ }})
+ .unwrap(),
+ )
+ .chain(stmts)
+ .collect();
+
+ let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
+ let ident = &f.sig.ident;
+
+ let resource_args = statics
+ .iter()
+ .map(|statik| {
+ let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let expr = &statik.expr;
+ quote! {
#(#cfgs)*
- let #ident: &mut #ty = unsafe {
+ {
#(#attrs)*
- #(#cfgs)*
static mut #ident: #ty = #expr;
-
&mut #ident
- };
- )
+ }
+ }
})
.collect::<Vec<_>>();
- let hash = random_ident();
quote!(
#[export_name = #ident_s]
- #(#attrs)*
- pub #unsafety extern "C" fn #hash() {
- interrupt::#ident;
-
- #(#vars)*
-
- #(#stmts)*
+ pub unsafe extern "C" fn #tramp_ident() {
+ #ident(
+ #(#resource_args),*
+ )
}
+
+ #f
)
.into()
}
@@ -705,41 +732,10 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
-// Creates a random identifier
-fn random_ident() -> Ident {
- let secs = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .unwrap()
- .as_secs();
-
- let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
- let mut seed: [u8; 16] = [0; 16];
-
- for (i, v) in seed.iter_mut().take(8).enumerate() {
- *v = ((secs >> (i * 8)) & 0xFF) as u8
- }
-
- for (i, v) in seed.iter_mut().skip(8).enumerate() {
- *v = ((count >> (i * 8)) & 0xFF) as u8
- }
-
- let mut rng = rand::rngs::SmallRng::from_seed(seed);
- Ident::new(
- &(0..16)
- .map(|i| {
- if i == 0 || rng.gen() {
- (b'a' + rng.gen::<u8>() % 25) as char
- } else {
- (b'0' + rng.gen::<u8>() % 10) as char
- }
- })
- .collect::<String>(),
- Span::call_site(),
- )
-}
-
/// Extracts `static mut` vars from the beginning of the given statements
-fn extract_static_muts(stmts: Vec<Stmt>) -> Result<(Vec<ItemStatic>, Vec<Stmt>), parse::Error> {
+fn extract_static_muts(
+ stmts: impl IntoIterator<Item = Stmt>,
+) -> Result<(Vec<ItemStatic>, Vec<Stmt>), parse::Error> {
let mut istmts = stmts.into_iter();
let mut seen = HashSet::new();
diff --git a/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs
index 4e568eb..e915518 100644
--- a/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs
+++ b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs
@@ -7,7 +7,7 @@ extern crate panic_halt;
use cortex_m_rt::{entry, interrupt};
#[entry]
-fn foo() -> ! {
+fn entry() -> ! {
loop {}
}
diff --git a/cortex-m-rt/tests/compile-fail/non-static-resource.rs b/cortex-m-rt/tests/compile-fail/non-static-resource.rs
new file mode 100644
index 0000000..ae009a9
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/non-static-resource.rs
@@ -0,0 +1,43 @@
+//! Tests that no `&'static mut` to static mutable resources can be obtained, which would be
+//! unsound.
+//!
+//! Regression test for https://github.com/rust-embedded/cortex-m-rt/issues/212
+
+#![no_std]
+#![no_main]
+
+extern crate cortex_m;
+extern crate cortex_m_rt;
+extern crate panic_halt;
+
+use cortex_m_rt::{entry, exception, interrupt, ExceptionFrame};
+
+#[allow(non_camel_case_types)]
+enum interrupt {
+ UART0,
+}
+
+#[exception]
+fn SVCall() {
+ static mut STAT: u8 = 0;
+
+ let _stat: &'static mut u8 = STAT; //~ ERROR explicit lifetime required in the type of `STAT`
+}
+
+#[interrupt]
+fn UART0() {
+ static mut STAT: u8 = 0;
+
+ let _stat: &'static mut u8 = STAT; //~ ERROR explicit lifetime required in the type of `STAT`
+}
+
+#[entry]
+fn you_died_of_dis_entry() -> ! {
+ static mut STAT: u8 = 0;
+
+ // Allowed. This is sound for the entry point since it is only ever called once, and it makes
+ // resources far more useful.
+ let _stat: &'static mut u8 = STAT;
+
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs
new file mode 100644
index 0000000..c040173
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs
@@ -0,0 +1,45 @@
+//! Makes sure that the expansion of the attributes doesn't put the resource initializer in an
+//! implicit `unsafe` block.
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_halt;
+
+use cortex_m_rt::{entry, exception, interrupt};
+
+#[allow(non_camel_case_types)]
+enum interrupt {
+ UART0,
+}
+
+const unsafe fn init() -> u32 { 0 }
+
+#[entry]
+fn foo() -> ! {
+ static mut X: u32 = init(); //~ ERROR requires unsafe
+
+ loop {}
+}
+
+#[exception]
+fn SVCall() {
+ static mut X: u32 = init(); //~ ERROR requires unsafe
+}
+
+#[exception]
+fn DefaultHandler(_irq: i16) {
+ static mut X: u32 = init(); //~ ERROR requires unsafe
+}
+
+#[exception]
+fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
+ static mut X: u32 = init(); //~ ERROR requires unsafe
+ loop {}
+}
+
+#[interrupt]
+fn UART0() {
+ static mut X: u32 = init(); //~ ERROR requires unsafe
+}