Webinar: C++ semantics - 06.11
A couple of years ago the PVS-Studio analyzer got its first diagnostic rules to check program code compliance with the MISRA C and MISRA C++ standards. We collected feedback and saw that our clients were interested in using the analyzer to check their projects for MISRA compliance. So, we decided to further develop the analyzer in this direction. The article covers the MISRA C/C++ standard and the MISRA Compliance report. It also shows what we already managed to do and what we plan to achieve by the end of the year.
Our company started working on a static code analyzer back in 2006. At that time, the IT world was undergoing a smooth transition: applications started migrating from 32-bit systems to 64-bit. Many developers started to encounter unexpected problems. Our product, whose name then was Viva64, helped developers to look for code errors that occurred after they adapted applications to 64-bit systems. Over time, the analyzer was learning to examine projects for error patterns related to typos, uninitialized variables, unreachable code, undefined behavior, etc. Currently the analyzer provides over 1000 diagnostics.
Before 2018, we positioned PVS-Studio as a tool that detects code errors. In 2018, we realized that a significant portion of errors that we learned to detect were simultaneously potential vulnerabilities. Starting with 2018, PVS-Studio has been a tool for Static Application Security Testing (SAST). At the same time, we started to classify both new and existing diagnostics in accordance with Common Weakness (CWE), SEI CERT Coding (CERT), MISRA C/C++. In 2021, AUTOSAR joined this list.
In 2018 we started supporting embedded systems, and that prompted us to support MISRA and AUTOSAR. The analyzer supported the following systems:
You can find detailed instructions on how to use PVS-Studio for embedded development on our website.
Unlike desktop projects, many embedded projects are developed according to MISRA recommendations. So, we thought that programming our analyzer to support the standard would be a good idea. Since then, we have been slowly developing rules for this standard and collecting feedback.
We were waiting for the demand to appear, and it did not keep us waiting long. People wrote us messages, asked about the analyzer's features, tried to analyze their projects. For us this meant that it was time to develop our analyzer in the MISRA direction further. Most of our clients were interested in MISRA C rather than in MISRA C++, which is why at first, we decided to increase the MISRA C coverage. Users were also interested in the MISRA Compliance report, which we recently supported as well.
And now let's talk about the MISRA C/C++ standard itself.
The MISRA standard is designed for critical embedded systems where the requirements of a quality, reliability and portability must be met. Such systems are used in automotive industry, aircraft construction, medicine, space industry and other industry fields. The price of a programmatic error in such systems could be human health and lives, or large financial or reputation losses.
The MISRA C standard applies to programs in C. The standard is regularly updated and currently contains 143 rules and 16 directives. The rules are split into categories:
Let's look at a few Required rules, since this category is the largest.
Rule MISRA-C-11.8. The cast should not remove const/volatile qualification from the type that is pointed to by a pointer. Diagnostic for this rule - V2567. Here is the example of the deviation that analyzer found in the reliance-edge project:
V2567 [MISRA-C-11.8] The cast should not remove 'const' qualification from the type that is pointed to by a pointer. toolcmn.c 66
uint8_t RedFindVolumeNumber(const char *pszVolume)
{
const char *pszEndPtr;
....
ulNumber = strtoul(pszVolume, (char **)&pszEndPtr, 10);
....
}
The rule warns that this pattern leads to undefined behavior.
Rule MISRA-C-7.1. Octal constants should not be used. Diagnostic for this rule - V2501. The analyzer found such constants in the same reliance-edge project as well:
V2501 [MISRA-C-7.1] Octal constant '0666' should not be used. fsstress.c 1376
static void creat_f(int opno, long r)
{
int e;
pathname_t f;
int fd;
....
fd = creat_path(&f, 0666); // <=
e = fd < 0 ? errno : 0;
....
}
The rule says that the use of octal literals could hinder code readability, especially when a developer is skimming through it. Misinterpreting numeric values may result in various errors.
Rule MISRA-C-11.1. Conversions should not be performed between pointer to function and any other type. Diagnostic for this rule - V2590.
V2590 Conversions should not be performed between pointer to function and any other type. Consider inspecting the '(fp) & foo' expression.
void foo(int32_t x);
typedef void (*fp)(int16_t x);
void bar(void)
{
fp fp1 = (fp)&foo;
}
The pointer to the fp1 function takes the value of the pointer to the foo function, which does not match the arguments and the return value. The language standard allows such conversions. However, the MISRA C standard warns that they cause undefined behavior.
If you just begin to use the MISRA standard and apply all the rules on your code at once, it will look something like this:
Almost every language construct has its own rule or even multiple rules. That's why code writing in compliance with the MISRA standard is no joke. Luckily, we have static code analyzers to help us. Such analyzers come with a set of diagnostics which detect rule violations of the standard.
MISRA created its rules taking risky features of the language and its subtleties into account. Compliance with meticulous rules helps developers to write secure code. They notice mistakes more easily. They don't have to keep all the subtle language features in their head and worry about the program behaviour when it's ported to different software environment or hardware. MISRA developers have thoroughly studied the entire C language standard in search of ways to shoot themselves in the foot. Now we can draw on their experience rather than learn the language standard from cover to cover.
Adhering to the coding standard can be a huge challenge for a developer. It imposes many restrictions on the code writing. However, this approach significantly reduces the number of errors that may appear when you run a program, for instance, in an airplane engine. High application security pays for the time and money you spend to ensure your code comply with the standard. Find more information about the standard and how to use it in your projects in the article: What is MISRA and how to cook it.
And now let's move on to the development of our static analyzer in the MISRA direction.
Now we know that developers want to use our analyzer to check their code for compliance with the MISRA standard. Therefore, we're actively developing the analyzer in this direction.
We saw the demand for code analysis that complies with MISRA C standard. Our team continued to develop the analyzer to competitive levels and set goals for ourselves:
Starting with April, we prioritized writing the MISRA C diagnostics. Our team expanded and that enhanced the development process. Currently, PVS-Studio covers 60% of the MISRA C standard. By November we plan to increase the coverage up to 75%, and by January 2022 – 80% or more.
While I was writing this article, the beta version of PVS-Studio analyzer got the feature to generate MISRA Compliance report. The PlogConverter.exe utility for Windows and plog-converter for Linux can now convert a "raw" analyzer report into MISRA Compliance report. Now let's talk about MISRA Compliance report.
Here is a couple of examples from the recent MISRA C diagnostics.
V2594. MISRA. Controlling expressions should not be invariant.
Controlling expressions in if, ?:, while, for, do, switch should not be invariant, i.e., controlling expressions should not always lead to executing the same code branch. An invariant value in a controlling expression may indicate a program error.
void adjust(unsigned error)
{
if (error < 0)
{
increase_value(-error);
}
else
{
decrease_value(error);
}
}
This example illustrates the error: the condition is always false because the function receives an unsigned integer. As a result, the decrease_value function is always called. The compiler may remove the code branch with the increase_value function.
V2598. MISRA. Variable length array types are not allowed.
Declaring variable-length arrays can lead to a stack overflow and potential vulnerabilities in the program.
void foo(size_t n)
{
int arr[n];
// ....
}
Transmission of large number n can lead to a stack overflow as the array will become too large and take up more memory than there is available.
The MISRA C standard contains 143 rules and 16 directives. It would be nice to have a general report that could show the code compliance with the standard in a convenient form and contain information for all rule deviations. Such report exists. Its name is MISRA Compliance.
According to the MISRA C standard, it may be unjustified for developers to comply with all MISRA rules. Therefore, it requires to issue a MISRA Compliance report on the code that complies with all Mandatory rules. The standard also allows deviations from the Required rules. A developer must confirm deviations from rules and document them.
As mentioned earlier, the beta version of our analyzer now can generate such reports. Currently, a report has a form of HTML-page generated by the PlogConverter.exe utility for Windows and plog-converter for Linux.
The report contains a table of code compliance with each of the MISRA C rules and a general conclusion.
The Guideline column contains rules and directives number from the MISRA C standard.
The Category - shows the category of a rule or directive indicated in the standard.
The MISRA C standard allows you to raise the level of compliance. Therefore, the Recategorization reflects the new rule or directive category set by the user in accordance with the GRP (Guideline Re-categorization Plan). Only three transitions are possible:
In our case, GRP is a txt file. A file example of acceptable shifts:
Rule 15.3 = Mandatory
Rule 16.4 = Mandatory
Rule 17.5 = Required
If this file contains a category down-shift, the plog-converter will issue an error message and will not generate the report.
The Compliance column contains information about the checked code's compliance with a rule or directive:
Under the table, you will see the report that shows whether your project complies or does not comply with the MISRA C standard. Compliant code meets the following conditions:
If the code does not comply with the standard, the utility will highlight in red the violated statuses of the rules.
Till the beginning of October 2021, the generation of MISRA Compliance report will be accessible in beta version (to get the beta, please, fill in the feedback form). Then we plan to release a new version of PVS-Studio analyzer. PVS-Studio 7.15 will be able to generate this report.
To generate the MISRA Compliance report on Windows, first, run the analysis of the project. Then run the Plog-converter.exe utility with the following arguments:
"C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" "path_to_report_file" \
-t misra -o "path_to_MISRA_report" --grp "path_to_grp.txt"
To generate the report on Linux, you also need to run the analysis. Then call the plog-converter.
plog-converter "path_to_report_file" -t misra -o "path_to_MISRA_report" \
--grp "path_to_grp.txt"
The MISRA Compliance report shows that your project's code complies with the MISRA standard. We strive to reduce the number of Not supported statuses in your report. The development of new MISRA diagnostics results not only in the diagnostic code and the documentation text. It also produces a valuable outcome for developers. And that's what the following paragraph is about.
What output does the MISRA diagnostics development provide?
First, it is understanding of secure coding principles. While developing the General Analysis diagnostics we try to minimize the number of issued warnings. Yet another MISRA diagnostics can issue thousands of messages on a medium-sized project. After we run a new diagnostic on our test projects database, a report may look like this:
Secondly - knowledge of specifics and unexpected features of a language. For example, does anybody remember the designated initialization? Does anybody know how to use the static keyword correctly in an array formal parameter declarator?
int array[] = { 1, 2, 4, [8]={256} };
void foo(int [static 20]);
Third, you learn a million of ways to get unspecified, undefined, or implementation-dependent behavior. You start recognizing potentially unsafe code fragments.
And new MISRA diagnostics development can also give rise to the General Analysis diagnostics.
Let's talk about the last one in more detail. Usually, the ideas of new General Analysis diagnostics show up in the following cases:
So, recently, a new General Analysis diagnostic appeared due to the implementation of one of the MISRA C rules. The rule says: 'Octal and hexadecimal escape sequences should be terminated'. Why? Look at this line:
const char *str = "\x0exit";
This string literal is 4 characters long, instead of 5, as it may seem at first. The \x0e sequence is one character that has the 0xE code - not a character, that has a zero code, followed by the letter e.
Therefore, according to the standard one must terminate the escape sequence in one of two ways:
For example:
const char *str1 = "\x0" "exit";
const char *str2 = "\x1f\x2f";
We found this rule useful for projects that are not written according to MISRA standard. This is how two our diagnostics appeared at once: V1074 and V2602. Obviously, they have the same code under the hood.
Here is another case when new diagnostics appeared due to cooperation with MISRA. It all started when we added the covid-sim project to the base of test projects for testing the PVS-Studio analyzer. The project turned out to be small and cross-platform, so it was suitable for the MISRA diagnostics testing. Before supplementing the base, we find it useful to look through warnings to search out patterns of false positives. This could be an ordinary check. However, the analyzer caught the V2507 warning that seemed to be a false positive:
if (radiusSquared > StateT[tn].maxRad2) StateT[tn].maxRad2 = radiusSquared;
{
SusceptibleToLatent(a->pcell);
if (a->listpos < Cells[a->pcell].S)
{
UpdateCell(Cells[a->pcell].susceptible, a->listpos, Cells[a->pcell].S);
a->listpos = Cells[a->pcell].S;
Cells[a->pcell].latent[0] = ai;
}
}
StateT[tn].cumI_keyworker[a->keyworker]++;
The V2507 diagnostic finds conditional statements whose bodies that are not enclosed in curly braces.
As you can see, the code fragment does have braces. Did the analyzer fail? Let's take a closer look. It becomes clear that the body of the if statement is in the same line with the conditional statement. And the braces have nothing to do with the if in any way.
First, this example proves that the MISRA standard approach works. It does reduce the number of errors made in code of critical embedded systems. After all, if the body of the if statement was in braces, then the logical error would be easy to notice.
Secondly, we have come up with an idea of a new General Analysis diagnostic. The diagnostic issues a warning in case the following conditions are met for the if statement:
Read more about the V1073 diagnostic development here.
Code reliability and safety requires compliance with strict and meticulous rules concerning a certain style of code writing. Avoid dangerous language constructs and functions: their misuse leads to failures. Use static analyzers, for example PVS-Studio, for checking code for compliance. The result of the check will be the MISRA Compliance report.
More information on how to improve code security using static analysis in the following articles:
0