Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lint suggestions based on MISRA 2004 #2227

Open
11 of 48 tasks
xd009642 opened this issue Nov 15, 2017 · 15 comments
Open
11 of 48 tasks

Lint suggestions based on MISRA 2004 #2227

xd009642 opened this issue Nov 15, 2017 · 15 comments
Labels
L-correctness Lint: Belongs in the correctness lint group

Comments

@xd009642
Copy link
Contributor

xd009642 commented Nov 15, 2017

As mentioned in #2215 I'm compiling a list of lints based on MISRA rules. For the C language MISRA 2004 defines C90 should be used so there are numerous rules forbidding certain C99 features, C++ features. And there are also rules written for machines which provide FPUs that don't use IEEE754 for floats. So I'm going to attempt to remove all the irrelevant rules and keep it to rules which are relevant to rust 😄

So here it goes. All the relevant rules, a lot of these are probably already covered. And a number are actually already caught by the rust compiler!

  • No reliance on undefined or unspecified behaviour
  • Only interface with code from another language if the object code follow the same standard (so no rust-java interfaces)
  • Assembly language should be isolated from main code
  • No commented out sections of code
  • Manually specified struct packing should be documented
  • All third party libraries should conform to MISRA as well
  • Identifiers in different namespaces should not share the same name (with exception of struct elements)
  • Identifier names should not be reused
  • Only use char for characters, use u8 or i8 for numeric values
  • static variables should only be referenced in the file they are defined in
  • array size must be defined at compile time
  • (required) if first enum element is not 0, all elements must be explicitly assigned a value
  • a value of a "complex" expression of integer type may only be cast to a narrower type if it is the same signedness
  • a value of a "complex" expression of float type may only be cast to a smaller float type
  • result of shift operators must be explicitly cast to type of result before the shift i.e. let x:u8 = (8 as u8) >> 2;
  • No casting pointers to void* or pointer to other object type
  • casts should not remove volatile or constness from what a pointer points to
  • No dependency on operator precedence rules. Use brackets etc.
  • a value of an expression shall be the same under any order of evaluation the standard allows
  • right hand operator of && and || may not contain side effects
  • && and || can only be primary expressions a && b && c is good, a && (b && c) is bad
  • do not mix logical and non-logical operators
  • bitwise operators can only be applied to unsigned values
  • right hand side of shift must be between 0 and "element width" - 1
  • floating point values cannot be loop counters in loops
  • invariant boolean operations are not permitted (i.e. true == false)
  • no unreachable code
  • no use of goto or continue
  • a loop may not have more than one break statement
  • a function shall have a single point of exit at the end of the function
  • no recursion (directly or indirectly)
  • all results must be tested
  • only use pointer arithmetic on arrays
  • only use two levels of indirection maximum
  • imports should only be preceded by other imports
  • do not redefine things defined in the standard library
  • dynamic memory allocation must not be used
  • errno, stdio, unix signals and functions that interact with the environment (getenv, system etc) must not be used (MISRA assumes embedded applications only).
  • all if statements must have an else
    • else_if_without_else

Already solved by clippy

  • do not test floating point values for equality or inequality
  • Identifiers in an inner scope must not share a name with identifiers in a higher scope (no masking)
  • all statements must have at least one side-effect or cause control flow to change. No null statements
    • no_effect

Already solved by the language

  • pointers may only be cast to integral types
  • conditions should be explicitly checked against true and false
  • sizeof operator should not be used on expressions with side effects
  • No bitwise operations on floating point values
  • unsigned must only be used for numeric types
  • functions must have a fixed number of arguments
@oli-obk oli-obk added the L-correctness Lint: Belongs in the correctness lint group label Nov 15, 2017
@goodmanjonathan
Copy link
Contributor

I'd like to take some of these on! I already have a lint prepared for else if branches without an else.

@shssoichiro
Copy link
Contributor

I think many more of these are solved by the language/compiler than are checked off. Someone with edit privileges may want to update the list.

@goodmanjonathan
Copy link
Contributor

goodmanjonathan commented Dec 24, 2017

Yeah, I realized that too. We can definitely cross off the following:

  • No bitwise operations on floating point values (solved by language)
  • unsigned must only be used for numeric types (solved by language)
  • Identifiers in an inner scope must not share a name with identifiers in a higher scope (no masking)
    (solved by clippy)
  • sizeof operator should not be used on expressions with side effects (solved by language)
  • conditions should be explicitly checked against true and false (solved by language)

@oli-obk
Copy link
Contributor

oli-obk commented Dec 24, 2017

Thanks. I only went through the list quickly. Feel free to post if I need to update any others.

@pJunger
Copy link
Contributor

pJunger commented Sep 2, 2019

Maybe it would be better to analyze rulesets from other (more similar) languages as well (e.g. ADA or C++).
In my opinion many of those rules do not make sense for Rust even if they are implementable (e.g. the single point of return).

C++:

Ada:
?

@xd009642
Copy link
Contributor Author

xd009642 commented Sep 5, 2019

SPARK Ada is a "safer" subset designed for easier verification of safety critical systems

@llogiq
Copy link
Contributor

llogiq commented Sep 5, 2019

While that's true, it still requires manual verification of the code, including setting up invariants, etc.

Are there no rule sets or lints for security relevant Ada code?

@xd009642
Copy link
Contributor Author

xd009642 commented Sep 5, 2019

I didn't see any when I worked in aerospace. But then no new projects were done in Ada only legacy code used it. The lack of support for Ada in certain testing and analysis tools meant those parts of the code were painful as we had to manually verify 😢

@pJunger
Copy link
Contributor

pJunger commented Sep 7, 2019

@xd009642 I'm no expert on Ada, but wouldn't the Ravenscar profile be a better comparison?

I also found this wikipedia article on Coding conventions which lists 4 coding styles for Ada (including ones from NASA and ESA).

Additionally, I don't think that the availability of tooling (or lack thereof) is that relevant here - those rules still might be interesting for Rust (or clippy).

@xd009642
Copy link
Contributor Author

xd009642 commented Sep 7, 2019

Additionally, I don't think that the availability of tooling (or lack thereof) is that relevant here

Well all the C/C++ standards we used had a lot of automated tooling and static analysis which automated the strenuous parts of reviewing. And I'm not familiar with Ravenscar, the legacy projects (systems on jets) I worked on used the SPARK subset or a standard written specifically for the project.

As well as looking at other coding standards could also peruse standards like DO-178B (aerospace), ISO 26262 (automotive) etc. to see what they assurances they need at the various safety levels

@llogiq
Copy link
Contributor

llogiq commented Sep 7, 2019

Btw. re-reading the list above, the "no bitwise ops on signed ints" rule stood out, because it may be problematic in C, unlike in Rust. So we might want to have a fourth category "unneeded because of different semantics"...

@pJunger
Copy link
Contributor

pJunger commented Sep 7, 2019

@xd009642 I know that Misra and other standards are (partly or mostly) checked by tools like QA-C or similar, but what I meant was: Just because these tools might not exist for Ada does not mean that one shouldn't have a look at those rules (because we might implement them for Rust).

I agree that safety standards for those various domains should be considered.

@llogiq That's why I mentioned those other coding standards - many of those rules definitely make sense for C, but not for Rust. It could provide a simple way to filter out C-specific rules.

@synek317
Copy link

synek317 commented Oct 14, 2019

I don't understand most of the rules, could you please give me some more details?

Identifiers in different namespaces should not share the same name (with exception of struct elements)

how is this an issue in rust?

Identifier names should not be reused

so this would become a clippy warning?

fn foo(bar: impl AsRef<Path>) {
  let bar = bar.as_ref();
  // ...
}

static variables should only be referenced in the file they are defined in

Probably mutable static vars? Anyway, is this really an rust issue?

array size must be defined at compile time

Not rust related afaik?

a value of a "complex" expression of integer type may only be cast to a narrower type if it is the
same signedness
a value of a "complex" expression of float type may only be cast to a smaller float type

Can you give an example of 'comples' expression?

No dependency on operator precedence rules. Use brackets etc.

So this would trigger a warning? 2 + 5 * 10

&& and || can only be primary expressions a && b && c is good, a && (b && c) is bad
do not mix logical and non-logical operators

How is a && (b && c) bad? Or how should I write a || (b && c)?

floating point values cannot be loop counters in loops

How this applies to rust?

no use of goto or continue

How is continue harmful? It sometimes improves readability or performance a lot.

a loop may not have more than one break statement
a function shall have a single point of exit at the end of the function

Too general. In some cases, multiple returns or breaks improves readability

no recursion (directly or indirectly)

Why??? I know recursion may cause stack overflow easily, but come on. A lot of functions have nice recursive versions. Not every recursion is easy to change into iteration. Tail recursion may be optimized by the compiler.

only use pointer arithmetic on arrays

Is this applicable to rust?

imports should only be preceded by other imports

How can this affect the rust code negatively?

do not redefine things defined in the standard library

No more custom Result type? :(

dynamic memory allocation must not be used

So no more Vecs?

all if statements must have an else

Why? How?

@pJunger
Copy link
Contributor

pJunger commented Oct 14, 2019

Does it really make sense to collect & discuss those rules here?
In my opinion it would be great to have a separate repository for this (similar to the RFC repo?)

There a discussion could take place in order to find a set of rules that

  • will allow writing code that fulfills ISO26262, DO-178C, etc.
  • makes sense for Rust (ideally not only to fulfill the ISO, but really help us write maintainable & reliable code)
  • can then be implemented by clippy or by the compiler itself(?)

Edit: Prototype can be found in this repo.

@oli-obk
Copy link
Contributor

oli-obk commented Oct 9, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
L-correctness Lint: Belongs in the correctness lint group
Projects
None yet
Development

No branches or pull requests

7 participants