Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
Find a file
bors[bot] ed6460f6dc Merge #140
140: fix soundness issue: forbid early returns in init r=japaric a=japaric

TL;DR

1. v0.4.1 will be published once this PR lands

2. v0.4.0 will be yanked once v0.4.1 is out

3. v0.4.1 will reject code that contains early returns in `init` *and* contains
   late resources. Yes, this is a breaking change but such code is unsound /
   has undefined behavior.

4. as of v0.4.1 users are encouraged to use `fn init() -> init::LateResources`
   instead of `fn init()` when they make use of late resources.

---

This PR fixes a soundness issue reported by @RalfJung. Basically, early
returning from `init` leaves *late resources* (runtime initialized statics)
uninitialized, and this produces undefined behavior as tasks rely on those
statics being initialized. The example below showcases a program that runs into
this soundness issue.

``` rust
 #[rtfm::app(device = lm3s6965)]
const APP: () = {
    // this is actually `static mut UNINITIALIZED: MaybeUninit<bool> = ..`
    static mut UNINITIALIZED: bool = ();

    #[init]
    fn init() {
        // early return
        return;

        // this is translated into `UNINITIALIZED.set(true)`
        UNINITIALIZED = true; // the DSL forces you to write this at the end
    }

    #[interrupt(resources = [UNINITIALIZED])]
    fn UART0() {
        // `resources.UNINITIALIZED` is basically `UNINITIALIZED.get_mut()`

        if resources.UNINITIALIZED {
            // undefined behavior
        }
    }
};
```

The fix consists of two parts. The first part is producing a compiler error
whenever the `app` procedural macro finds a `return` expression in `init`. This
covers most cases, except for macros (e.g. `ret!()` expands into `return`) which
cannot be instrospected by procedural macros. This fix is technically a
breaking change (though unlikely to affect real code out there) but as per our
SemVer policy (which follows rust-lang/rust's) we are allowed to make breaking
changes to fix soundness bugs.

The second part of the fix consists of extending the `init` syntax to let the
user return the initial values of late resources in a struct. Namely, `fn() ->
init::LateResources` will become a valid signature for `init` (we allowed this
signature back in v0.3.x). Thus the problematic code shown above can be
rewritten as:

``` rust
 #[rtfm::app(device = lm3s6965)]
const APP: () = {
    static mut UNINITIALIZED: bool = ();

    #[init]
    fn init() -> init::LateResources {
        // rejected by the compiler
        // return; //~ ERROR expected `init::LateResources`, found `()`

        // initialize late resources
        init::LateResources {
            UNINITIALIZED: true,
        }
    }

    #[interrupt(resources = [UNINITIALIZED])]
    fn UART0() {
        if resources.UNINITIALIZED {
            // OK
        }
    }
};
```

Attempting to early return without giving the initial values for late resources
will produce a compiler error.

~~Additionally, we'll emit warnings if the `init: fn()` signature is used to
encourage users to switch to the alternative `init: fn() -> init::LateResources`
signature.~~ Turns out we can't do this on stable. Bummer.

The book and examples have been updated to make use of `init::LateResources`.

In the next minor version release we'll reject `fn init()` if late resources
are declared. `fn init() -> init::LateResources` will become the only way to
initialize late resources.

This PR also prepares release v0.4.1. Once that version is published the unsound
version v0.4.0 will be yanked.


Co-authored-by: Jorge Aparicio <jorge@japaric.io>
2019-02-12 14:28:42 +00:00
.cargo v0.4.0 2018-11-03 17:16:55 +01:00
.github v0.4.0 2018-11-03 17:16:55 +01:00
book (en) update the text related to late resources 2019-02-12 15:13:41 +01:00
ci fix ci/after-success.sh 2019-02-11 22:07:15 +01:00
examples update examples and tests 2019-02-12 15:08:46 +01:00
macros accept init: fn() -> init::LateResources 2019-02-12 14:53:49 +01:00
src document MSRV and SemVer policy 2019-02-12 11:08:39 +01:00
tests update examples and tests 2019-02-12 15:08:46 +01:00
.gitignore change layout of books 2019-02-11 21:40:53 +01:00
.travis.yml don't pin to an older nightly 2018-12-16 22:05:32 +01:00
build.rs note that the timer queue is not supported on ARMv6-M 2018-12-16 19:38:22 +01:00
Cargo.toml v0.4.1 2019-02-12 11:28:34 +01:00
CHANGELOG.md update CHANGELOG with alt init syntax 2019-02-12 15:27:08 +01:00
LICENSE-APACHE initial commit 2017-03-05 00:29:08 -05:00
LICENSE-CC-BY-SA v0.4.0 2018-11-03 17:16:55 +01:00
LICENSE-MIT v0.4.0 2018-11-03 17:16:55 +01:00
README.md v0.4.0 2018-12-16 21:19:19 +01:00

Real Time For the Masses

A concurrency framework for building real time systems.

Features

  • Tasks as the unit of concurrency 1. Tasks can be event triggered (fired in response to asynchronous stimuli) or spawned by the application on demand.

  • Message passing between tasks. Specifically, messages can be passed to software tasks at spawn time.

  • A timer queue 2. Software tasks can be scheduled to run at some time in the future. This feature can be used to implement periodic tasks.

  • Support for prioritization of tasks and, thus, preemptive multitasking.

  • Efficient and data race free memory sharing through fine grained priority based critical sections 1.

  • Deadlock free execution guaranteed at compile time. This is an stronger guarantee than what's provided by the standard Mutex abstraction.

  • Minimal scheduling overhead. The task scheduler has minimal software footprint; the hardware does the bulk of the scheduling.

  • Highly efficient memory usage: All the tasks share a single call stack and there's no hard dependency on a dynamic memory allocator.

  • All Cortex-M devices are supported. The core features of RTFM are supported on all Cortex-M devices. The timer queue is currently only supported on ARMv7-M devices.

  • This task model is amenable to known WCET (Worst Case Execution Time) analysis and scheduling analysis techniques. (Though we haven't yet developed Rust friendly tooling for that.)

Requirements

  • Rust 1.31.0+

  • Applications must be written using the 2018 edition.

User documentation

API reference

Acknowledgments

This crate is based on the RTFM language created by the Embedded Systems group at Luleå University of Technology, led by Prof. Per Lindgren.

References

License

All source code (including code snippets) is licensed under either of

at your option.

The written prose contained within the book is licensed under the terms of the Creative Commons CC-BY-SA v4.0 license (LICENSE-CC-BY-SA or https://creativecommons.org/licenses/by-sa/4.0/legalcode).

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.


  1. Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P. (2013, June). Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013 8th IEEE International Symposium on (pp. 110-113). IEEE. ↩︎

  2. Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho, L. M. (2016). Abstract timers and their implementation onto the arm cortex-m family of mcus. ACM SIGBED Review, 13(1), 48-53. ↩︎