Rules for Writing Safety Critical Code
spinroot.com
spinroot.com
Just to be clear... Following these rules does not get you any kind of safety related certification. There is a lot more work, both up-front in requirements & analysis, and after implementation to gain any kind of certification.
Time to market and how easy it is for a new team member to pick up the code is more important than catching every bug or sub-par performance because patching and deployment is relatively cheap.
Higher level s/w has piled on the abstraction layers and that hides what is really happening on the h/w level.
Web s/w has gone in direction of simplicity-by-increasing-abstraction. Embedded s/w gains simplicity by removing it.
Of course, some knowledge of the h/w is necessary for this, but thats a prerequisite to performant systems anyway.
Abstractions need to be appropriate (and shallow in order to be be easily analysable). A degree of 'mechanical-sympathy' is needed.
A lot of web programming takes place in languages in which constructor expressions implicitly allocate memory.
No Python, Ruby, Lisp, Lua, PHP, Perl or anything for you. Not to mention JS on the client side or any other side.
Giving loops a fixed upper bound? "Sorry, you have 13729 widgets in your basket, but we can only display 1000 due to a limit on the number of iterations in the loop!"
The linux kernel.
The kernel coding conventions actually state that this is because over-zealous nesting is more harmful than using goto statements.
>The rationale for using gotos is: - unconditional statements are easier to understand and follow - nesting is reduced - errors by not updating individual exit points when making modifications are prevented - saves the compiler work to optimize redundant code away ;)
Linux would never get chosen for any higher level safety-related s/w.
No goto un-tested. No branches not taken.
The other situation is that the actual control is deferred to a proper RTOS/micro combination which can do the correct thing when Linux goes off the rails.
The Linux kernel is not a hard-real-time system, it doesn't have complete test coverage and its inclusion in highly-safety-critical systems is as a lower-safety-level subsystem. You will not find a Linux task controlling (for example) a GPIO enabling an X-Ray beam in human rated medical devices.
BTW: I can promise you that there are many untested goto's in the Linux Kernel...
What part of Linux precludes it from being a proper RT OS in the hands of industrial developers whose purpose it is to certify it as such?
>BTW: I can promise you that there are many untested goto's in the Linux Kernel...
Not in a development environment looking for SIL-4 certification, there isn't. Perhaps in the public sphere you may feel that is an adequate position, but in industrial computing a significant part of the value is in the testing and real certification of the Linux kernel for certain ratings.
>The Linux kernel is not a hard-real-time system, it doesn't have complete test coverage and its inclusion in highly-safety-critical systems is as a lower-safety-level subsystem
I assure you, I have found none of these statements to be truly true.
My background is industrial/defense/medical s/w on hard real time systems. e.g. I've worked on train braking systems, high throughput radar systems, medical devices, lots hard real-time control stuff....
I personally have never seen any evidence of anything greater than SIL-2 certified Linux (https://www.osadl.org/SIL2LinuxMP.sil2-linux-project.0.html) and that is only in very narrowly defined context.
Like I said earlier, any higher certification, Linux would be run as a comparmentalised, lower-level subsystem. The real control would not be via a Linux controlled task.
Until you can point me to the test-suite for a Linux kernel& driver set that gives me 100% code-coverage, then Linux will never get past this point.
Dont get me wrong, I like Linux and it has a valuable place in embedded systems.... but hard-real-time control and safety-related systems is not it.
As a final word on the subject, dont take my word for it: http://www.hse.gov.uk/research/rrhtm/rr011.htm "it is not likely to be either suitable or certifiable for SIL 4 applications"
"It is unlikely that Linux would be useful for SIL 4 applications and it would not be reasonably practicable to provide evidence that it meets a SIL 4 integrity requirement. "
...And in (https://www.osadl.org/fileadmin/dam/presentations/61508/6150...) "One of the main conclusion: Linux is not suitable for SIL4."
Of course, this is proprietary to the whims of the companys' deploying SIL-4 Linux: THALES, et al.
http://ti.tuwien.ac.at/ecs/teaching/courses/hwswcode_vu_WS_2...
http://ercim-news.ercim.eu/en75/special/tas-control-platform...
http://www.ibm.com/developerworks/library/l-real-time-linux/
(BTW, HSE isn't exactly an industrial authority on much ..)
>http://ti.tuwien.ac.at/ecs/teaching/courses/hwswcode_vu_WS_2.... No mention of Linux.
> http://ercim-news.ercim.eu/en75/special/tas-control-platform.... 1 mention of Linux... no mention of the role it plays.
>http://www.ibm.com/developerworks/library/l-real-time-linux/ Verifies my point. Only the kernel-in-kernel/hypervisor systems get hard-real-time performance. This is necessary for safety-critical work.
> (BTW, HSE isn't exactly an industrial authority on much ..) HSE == Health & Safety Executive... they are very much involved in this.... they're the government authority related to this type of stuff. They license software for use in Nuclear Reactors and the like (along with other agencies).
My points absolutely still stand.
If what you're talking about involves RedHat MRG, then that product has a completely different kernel. Its not standard linux.
I just hope that such kind of rules allow well documented/argumented exceptions. But I guess even in safety-critical environments you will have to handle exceptions (small, like in "we did something different than what was agreed on" not as in "throw BufferOverflowException") anyway. No system is perfect.
Though some of the rules are portable to other languages.
I'd add: have someone else familiar with the code base look over your code, add tests for your functions, test exhaustively where feasible, document your code and document your reasoning behind the code as well, make it look good, try to avoid being clever.
My feeling says that limiting loops and not using some language constructs can enhance quality that much. Checking and rechecking requirements, having different levels of testing, and most importantly have static and unit test checks on the source code abstraction level has a huge impact, though. Example: Even if you use only simple constructs like if and for you still run into problems with complexity because some problems are complex. Now the complexity simply is spread, which in some regards might make it easier to handle but in other cases makes it much harder to see the dependencies.
There is a lot of upfront analysis of the system as a whole and the algorithms before a line of code is written. Complexity and dynamism is minimised and all code paths are checked in the final code. This is one of the few places where 100% code-coverage is needed. you need to account for every path in the code, all the way down to bare-metal.
Also, a common thing to so is to have h/w fail-safe systems (iff possible), due to the difficulty in getting s/w approved.
There are another set of arguments /against/ using stopping the loop at the sentinel values because it may make the software less deterministic. But, that's another topic.
EDIT: On re-reading your message and the message it is responding to, I believe you are meaning opposite things by "sentinel".
memcpy() always copies n bytes, regardless of actual string size so is very deterministic and (commonly) inlined so is static. strcpy() on the other hand would loop infinitely until it found a null-terminator, it also is not deterministic.
memcpy() is easily analysable. strcpy() isn't.
Yes. It can be a few orders of magnitude higher than your expected array size: the goal is to prevent infinite loops. If you're looping over short user provided strings, if you hit an arbitrary counter of 1M, something probably went wrong, and the code should error out appropriately.
Though I suppose in real safety critical systems, you often review these limits and make sure they still apply.
Rules, Mostly Pertaining to C, For Writing Safety-Critical Code (with Rule 0 Removed)
(Rule 0 is: Don't use C for safety-critical code!)
(And you know there was a Rule [0], obviously!)
> 1 Restrict to simple control flow constructs. Details give the precision : no recursion allowed. Functional languages are out, there.
> 2 Give all loops a fixed upper-bound. Func languages give no warranty on that either.
> 3 Do not use dynamic memory allocation after initialization.
You have no access to memory layout, but func languages heavily rely on the creation on-the-fly of data and rely on garbage collection (which is forbidden, too).
> 4 Limit functions to no more than 60 lines of text. > 5 Use minimally two assertions per function on average. > 6 Declare data objects at the smallest possible level of scope. > 7 Check the return value of non-void functions, and check the validity of function parameters. OK, that might be easier with func languages.
> 8 Limit the use of the preprocessor to file inclusion and simple macros. Not relevant
> 9 Limit the use of pointers. Use no more than two levels of dereferencing per expression. OK, no pointer manipulation with these languages.
> 10 Compile with all warnings enabled, and use one or more source code analyzers.
Name one professional code analyzer for any func language of your choice.
Actually, what you can do in this case is when you call recursively, pass back an incremented counter variable as part of the state, and if it passes some upper limit, handle it appropriately. You don't need to "avoid recursion" just to get control over the number of times something loops.
> and rely on garbage collection (which is forbidden, too)
Fair enough, but if I work in a language that doesn't even give me much control (if any) over the GC, then how is this relevant to non-C coders, again? Which was my original point.
> Name one professional code analyzer for any func language of your choice.
Erlang (and by association Elixir) has Dialyzer http://www.erlang.org/doc/man/dialyzer.html which is a static code analyzer. Static code analysis has been endorsed by John Carmack (and of course, all the 3 year old links to the blog post are now dead, sigh)
For anyone (like me) who is hooked on Elixir, https://github.com/jeremyjh/dialyxir provides a bit of easier Dialyzer management
Elixir (and I suppose Erlang) has this concept of "specs" which you don't have to use, but which help code analysis if you do... http://elixir-lang.org/docs/v1.0/elixir/Kernel.Typespec.html
This is relevant to users of assembly, forth, C++, Fortran, Ada, Rust and probably a few others. Beside the very last one (which is way too young), these are the languages embedded developers used most of the time, precisely because of their virtues regarding the 10 points in the original post.
I have to agree with you, though, regarding this list functional languages are probably as good as, or even better than most other high-level languages, but they clearly fail on most requirements.
Thanks for the reference about Dialyzer, I'll check it out.
Personally I can't remember coming across any Java/Lua/Python/Lisp/Haskell/etc VM's that have been, they're too complex and dynamic. you don't want dynamically-generated code controlling your x-ray machine...
I don't see how it follows that you can't have a functional language controlling an x-ray machine. You can make an appeal to complexity, I suppose, but this can be managed. If Erlang is used to create a 99.999999999% reliable cellphone infrastructure (http://stackoverflow.com/questions/8426897/erlangs-99-999999...), I believe it can handle medical device functions :P I mean seriously, at that level of reliability, even if you have a health-critical device with literally just 10 assembly instructions, a cosmic ray could still flip a bit (http://status.aws.amazon.com/s3-20080720.html) and cause a catastrophe!
And as far as Java (which I'm not necessarily a fan of, I'm just making a counterargument) and medical devices, look here: http://www.oracle.com/us/technologies/embedded/embedded-java...
Imagine you gave someone cancer every time a call was dropped. Even 0.00001% is a big risk, depending on what you're doing. You also want something that reliable out of the gate, not after years of beta testing.
> even if you have a health-critical device with literally just 10 assembly instructions, a cosmic ray could still flip a bit (http://status.aws.amazon.com/s3-20080720.html) and cause a catastrophe!
Sure. That's why there are dead-man switches, watchdogs, heartbeats, acks, checksums, etc. in the majority of ('all' is a strong word) safety critical software. If random corrupt data gets written to the wrong location, the consuming subsystem (hardware or software) should do the safest thing possible.
I was sort of referring to the fact that most functional languages are also dynamic/meta-programming type of environments, not compiled to straight non-modifiable all-ROMable code that absoutely will not change at run-time. Those are the features that are frowned upon, not the functional programming features. So for examnple, a LISP or Haskell that compiles down to a straight binary that is all static is absolutely possible, but you do lose out on the nice features.
There has been research into a SIL4 jvm, but I cant find any product that came out of it.
Bare in mind that you need 100% traceability, 100% code coverage, etc all the way from your byte-code down to bare-metal, thru the JVM/runtime/etc.
>http://www.oracle.com/us/technologies/embedded/embedded-java....
Thats just plain old embedded Java... nothing to see here, we're talking SIL rated java... its a different animal
In embedded systems, very, very little is dynamically allocated. Everything is as static as possible. no new/delete malloc/free, etc. Its all static data.
In general, embedded systems have taken the opposite tack of solving the memory allocation issues of malloc/free... we just don't do it. Desktop & server type applications use garbage collection/ARC type tracking. you wont find anything like that on small systems and certainly not in safety-critical code.