4

I have implemented a complex state machine with numerous state transitions for a safety SIL 4 system. The back bone for this implementation was done using function pointers. When all was sailing smoothly, the V&V opposed the use of function pointers in a SIL 4 system. Reference- Rule 9 NASA.Misra C 2004 however doesnt say that function pointers cant be used.

Is there any other way to implement complex state machines without any function pointers?

AlphaGoku
  • 968
  • 1
  • 9
  • 24
  • 1
    what do function pointers have to do with state machines? – old_timer Jan 22 '16 at 05:12
  • The func pointer keeps pointing to new functions based on events. Program flow is easily traceable and simple. I know SM can be implemented using switch statements as well,but, there are numerous conditions that must be checked for transitions in switch.Hence i went for func pointers – AlphaGoku Jan 22 '16 at 05:18
  • Don't extend your question with comments. Edit your question if you want to add information. If you want a reader to answer you should give him the best information possible. Searching all the stuff from the comment list is not the expected thing. – harper Jan 22 '16 at 07:08
  • Against which norm is this code to be certified? Have you agreed on a coding guideline / allowed language subset beforehand (which may exclude / restrict the use of pointers)? AFAIK the IEC 61508 descendants (i.e. those that use the *SIL* terminology) only contain general clauses regarding "coding standards" and "limited use of pointers" - both of which are just *recommendatory* even for SIL 3/4. – morido Jan 22 '16 at 10:15
  • 1
    Assign a simple integer instead of the function pointer, then have a (big) switch on that id that calls the function matching that id. – nos Jan 22 '16 at 11:04
  • @morido The SIL standards label everything "recommended" or "highly recommended", which in turn implies that if you don't follow recommended practice, you better have some strong rationale for it. Most notably, getting the product through the fuzzy 3rd party notified body bureaucrats, who usually lack every hint of technical knowledge, will be hard. The questionable "limited use of pointers" rule is HR for SIL 3 and 4. – Lundin Jan 22 '16 at 12:48
  • 1
    Regarding that NASA paper, you should remark that it is a generic document about overall safety-critical program design, but lacks all the necessary professionalism which is required by a coding standard for safety-critical software. No matter how sound the rules, you can't just barf them into a paper and then call it canon for safety-critical systems. You need to consider the overall software design procedure and also how the rules should be enforced. Just go with MISRA, preferably 2012. It plays in an entirely different division than that NASA hobbyist... – Lundin Jan 22 '16 at 12:55
  • I have yet to find any serious authority who can, preferably with scientific proof, point out that function pointers lead to decreased safety. I found one related remark though by Hatton, _Safer C_ p184: "From a safety-related viewpoint, the simple answer is that they should never be allowed outside the typedef mechanism." This is of course very sound advise (and this book is quite canonical, it is one of the main sources for MISRA-C). I would agree that function pointers which are not `typedef`:ed are a safety hazard. – Lundin Jan 22 '16 at 13:03
  • @Lundin Regarding your [first comment](http://stackoverflow.com/questions/34939379/state-machine-with-no-function-pointer?noredirect=1#comment57625421_34939379): At least in my copy of EN50128:2011 (which is one of those IEC 61508 descendants) "Limited Use of Pointers" is just *R* throughout all SI-levels > 0. This means you do not *necessarily* have to justify why you do not adhere to that "measure". Presumably due to the aim of being language-agnostic there is also no definition of what *limited* actually means. – morido Jan 22 '16 at 13:12
  • @morido I only have access to IEC 61508:1998, where 61508-3 table B.1, row 5 says `- R HR HR` from SIL 1 to 4. So either this was relaxed in later versions or 50128 is different from 61508. (Btw if you have MISRA-C compliance it should be able to cover this whole table B.1) – Lundin Jan 22 '16 at 13:57
  • @Lundin Which is why I asked the OP about the norm he is targetting... All the SIL-based norms are somehow related but not identical. – morido Jan 22 '16 at 14:01
  • back to my question, state variables are much easier and make more sense than using function pointers. that was the point of the quesiton. using function pointers for state variables is on the fringe and makes sense that it would also be on the fringe of safety. go with a normal state machine that fits well within the safety standards. – old_timer Jan 24 '16 at 14:08
  • thus the question, what does function pointers have to do with state machines. answer nothing. you question is not about state machines at all it is about function pointers. Or if your question is about state machines then it is about not using function pointers. – old_timer Jan 24 '16 at 14:09
  • @dwelch He is referring to an industry standard state machine like [this](http://electronics.stackexchange.com/questions/95569/best-practice-to-keep-main-in-embedded-systems/96415#96415) one. There's nothing strange or unsafe with using well-proven design patterns, rather the contrary. IEC 61508 et al encourages the practice "proven in use". – Lundin Jan 25 '16 at 07:39
  • @Lundin- Thats exactly how i implemented my code. – AlphaGoku Jan 25 '16 at 07:48
  • @AkshayImmanuelD And there's no big difference between that code and more naive code using a `switch`. The only difference is that you have cleaner code and you've done the optimization to a function-pointer table manually: the compiler's optimizer would do the same anyhow. You get identical machine code with either method. But by making the function-pointer table yourself, you get a much cleaner call of the state machine and eliminate all maintenance mishaps that a huge switch-case might have. That is, as long as you sanity-check the state variable before calling (equal to switch `default`). – Lundin Jan 25 '16 at 09:59
  • I.e the only acceptable use of a `switch` instead of the function pointer table would be `switch(state) { case STATE1: function1(); break; case STATE2: function2(); break; default: // handle errors }`. Writing the actual state code inside the switch would be very messy and lead to far too complex code. – Lundin Jan 25 '16 at 10:02

1 Answers1

13

First of all, that NASA document is not canon. Start by asking which law/directive/standard/requirement/document that enforces you to follow the NASA document. If it isn't enforced anywhere (which seems very likely, even at NASA itself), you are not obliged to follow it and you can dismiss the whole thing.

Failing to dismiss the nonsense as nonsense, you can use the usual procedure when hitting a wall with safety standards: the solution is always to document in detail how the stated rule doesn't make sense, and slap them in the face with their own methodology.

So rather than abandoning function pointers, ensure that they are used in a safe manner, through the methods described below.


Since all safety-related design boils down to risk assessment, you'll always have:

Error -> Cause -> Hazard -> Safety measure

With the given (poor) rationale from the NASA document you would justify the safety measure "avoid function pointers" with something like:

  1. Wrong code executed -> Corrupt function pointer -> Runaway code/illegal op code

  2. Stack overflow -> Function pointer recursion -> Memory corruption

  3. Confused programmer -> Function pointer syntax -> Unintended program functionality

That is all rather vague and a questionable risk assessment, but this is what the NASA document boils down to.

Instead of "avoid function pointers" for the above 3 listed hazards, I would suggest using the following safety measures instead:

  1. Defensive programming and assertions.
  2. Defensive programming and assertions. Educate programmers.
  3. Use typedef. Educate programmers.

Defensive programming and assertions

  • For the state machine, ensure that the number of states (items in the enum) corresponds to the number of handlers (items in the array of function pointers). Simply static assert the last item in the enum (called STATES_N or some such) against sizeof(func_pointer_array)/sizeof(*func_pointer_array).
  • The function pointer array must be allocated in ROM with error detection/CRC. Other safety requirements will point to this need, easiest way to solve is use a microcontroller with "ECC flash". Back in the days before ECC, you would have to calculate a CRC on the whole ROM instead, which is complex and tedious, but can be done too. You can also create an identical ROM mirror image of the table to protect against flash corruption.
  • The only function pointer which your state machine code is allowed to call, is one obtained from this safe function pointer array. Given that your state machine call looks like STATE_MACHINE[i](); where STATE_MACHINE is the array of function pointers, then simply add a run-time check of i to ensure that it is always valid.
  • For other function pointers in your program, such as callback functions, ensure that they are initialized to point at valid functions in ROM. Use const pointers if possible (the pointer itself is read-only). If you need to re-assign them in runtime, ensure that they are pointing at a valid function before invoking them.

The above kind of state machine is idiomatic and extremely safe, likely much safer than ordinary function calls elsewhere in your code. You'll of course have to ensure that the state transits are done in a safe and reasonable manner, but that's not something that concerns the function pointers.

Avoiding recursion

This is mainly about educating programmers not to use it, function pointers or no function pointers (seems this would have prevented the Toyota bug).

It is neither hard to spot nor avoid recursion, so half-decent code review formalities should be enough to prevent it. No veteran embedded systems programmer, regardless of safety-critical systems experience, will approve of code containing recursion.

You could/should set an in-house design rule stating that all safety-related code must be reviewed and approved by a veteran C programmer with n years of experience of safety-critical program design.

In addition, you should also check for recursion with static analyser tools (even if they aren't able to detect recursion through function pointers). If you have a static analyser that conforms to any version of MISRA-C, this is included.

Regarding unintended recursion, it is avoided with the above mentioned defensive programming methods.

Confusig function pointer syntax

The function pointer syntax in C can admittedly be very confusing, just look at

int (*(*func)[5])(void);

or some other ridiculous example. It can be solved by always enforcing a typedef for function pointer types.

(Reference: Les Hatton, Safer C, p184 "From a safety-related viewpoint, the simple answer is that they should never be allowed outside the typedef mechanism.")

There are two different ways you can typedef them, I prefer this:

typedef int func_t (void);
func_t* fptr;

Because this doesn't hide the pointer behind a typedef, which is generally bad practice. But if you feel more comfortable with the alternative

typedef int (*func_t) (void);
func_t fptr;

then that's ok practice too.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    So to demonstrate compliance, you'd write a document with the risks listed, then point out which measures you use to eliminate the risks, point at your implementation of MISRA rules (the MISRA compliance matrix) or at your quality system routines for code review etc. That way you don't explicitly say that you break the rule "avoid function pointers" but rather say "yes we follow that rule, this is how". And thereby you avoid having peculiar remarks in the report by the 3rd party notified body. – Lundin Jan 25 '16 at 10:07
  • This is pretty much word for word my preferred way of implementing FSM's in our SIL3 product. As an additional selling poit, unit testing is an important requirement for safety critical software, and a function pointer based FSM can be immensely easier to test than a huge `switch-case`. And since OP stated we are dealing with SIL4, you could mention that the FSM index `i` should be a safe variable with protection agains soft errors (recommended for SIL3, required for SIL4) in addition to traditional runtime limit checking that should be a given outside safety critical context as well. – sendaran Feb 01 '16 at 12:03
  • I had a chance to speak to [Dr.Michael Hennell](https://en.wikipedia.org/wiki/Michael_Hennell) at a LDRA conference. He said its completely OK to use function pointers even in safety critical code. I have the audio recording of the chat with him. If anyone needs it,plz do ask. He went up even to the point that even recursion can be used(not infinite recursion,controlled recursion) – AlphaGoku Feb 08 '16 at 04:17
  • `void f1(int *ptr); void f2(int *ptr); void (*const fpTest[2])(int *ptr1)={f1,f2}; int b[2][2]={{0,1},{2,3}}; void f1(int *ptr) { int a=0; int b=0; a=ptr[0]; b=ptr[1]; printf("%d\n",a); printf("%d\n",b); } void f2(int *ptr) { int a=0; int b=0; a=ptr[0]; b=ptr[1]; printf("%d\n",a); printf("%d\n",b); } int main() { (*fpTest[0])(b[0]); (*fpTest[1])(b[1]); return 0; }` Based on inputs from @Lundin and Dr.Michael Hennell, im planning on implementing my code this way – AlphaGoku Feb 10 '16 at 04:40
  • I am getting MISRA warning: "conversion between a pointer to function and another type [MISRA 2012 Rule 11.1, required]" Do I need to use typedef ? – kapilddit Jan 06 '21 at 12:36
  • @kapilddit That doesn't sound like anything related to this post here. You should post that as a new question and include the code. – Lundin Jan 07 '21 at 07:39
  • @Lundin Question raised here at: https://stackoverflow.com/questions/65596207/conversion-between-a-pointer-to-function-and-another-type-misra-2012-rule-11-1 – kapilddit Jan 07 '21 at 10:06