This commit is contained in:
Remco Vermeulen 2022-07-13 12:17:21 +02:00
Родитель 73ac82c1f1
Коммит c9218647fb
135 изменённых файлов: 3187 добавлений и 215 удалений

Просмотреть файл

@ -73,7 +73,7 @@ jobs:
PATH=$PATH:$CODEQL_HOME/codeql
pip install -r scripts/requirements.txt
find rule_packages/cpp -name '*.json' -exec basename {} .json \; | xargs --max-procs "$XARGS_MAX_PROCS" --max-args 1 python3 scripts/generate_rules/generate_package_files.py -a cpp
find rule_packages/c -name '*.json' -exec basename {} .json \; | xargs --max-procs "$XARGS_MAX_PROCS" --max-args 1 python3 scripts/generate_rules/generate_package_files.py -a c
find rule_packages/c -name '*.json' -exec basename {} .json \; | xargs --max-procs "$XARGS_MAX_PROCS" --max-args 1 python3 scripts/generate_rules/generate_package_files.py --skip-shared-test-generation -a c
echo "Generating help markdown file for cert"
$CODEQL_LATEST_HOME/codeql/codeql generate query-help -vvv --format=markdown -o cpp/cert/src/ cpp/cert/src/rules

12
.vscode/tasks.json поставляемый
Просмотреть файл

@ -180,7 +180,9 @@
"Expressions",
"Freed",
"Functions",
"IO",
"IO1",
"IO2",
"IO3",
"Includes",
"Initialization",
"IntegerConversion",
@ -205,9 +207,17 @@
"Strings",
"Strings1",
"Strings2",
"Strings3",
"Syntax",
"Templates",
"TypeRanges",
"Lambdas",
"Pointers",
"Preprocessor1",
"Preprocessor2",
"IntegerConversion",
"Expressions",
"DeadCode"
"VirtualFunctions"
]
},

Просмотреть файл

@ -1,4 +1,4 @@
name: cert-c-coding-standards
version: 2.3.0
version: 2.4.0
suites: codeql-suites
libraryPathDependencies: common-c-coding-standards

Просмотреть файл

@ -0,0 +1,594 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<section title="Description">
<p>File names on many operating systems, including Windows and UNIX, may be used to access <em>special files</em>, which are actually devices. Reserved Microsoft Windows device names include <code>AUX</code>, <code>CON</code>, <code>PRN</code>, <code>COM1</code>, and <code>LPT1</code> or paths using the <code>\\.\</code> device namespace. Device files on UNIX systems are used to apply access rights and to direct operations on the files to the appropriate device drivers.</p>
<p>Performing operations on device files that are intended for ordinary character or binary files can result in crashes and <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service">denial-of-service attacks</a>. For example, when Windows attempts to interpret the device name as a file resource, it performs an invalid resource access that usually results in a crash [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Howard02">Howard 2002</a>].</p>
<p>Device files in UNIX can be a security risk when an attacker can access them in an unauthorized way. For example, if attackers can read or write to the <code>/dev/kmem</code> device, they may be able to alter the priority, UID, or other attributes of their process or simply crash the system. Similarly, access to disk devices, tape devices, network devices, and terminals being used by other processes can lead to problems [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Garfinkel96">Garfinkel 1996</a>].</p>
<p>On Linux, it is possible to lock certain applications by attempting to open devices rather than files. Consider the following example:</p>
<sample language="cpp">/dev/mouse
/dev/console
/dev/tty0
/dev/zero
</sample>
<p>A Web browser that failed to check for these devices would allow an attacker to create a website with image tags such as <code>&lt;IMG src="file:///dev/mouse"&gt;</code> that would lock the user's mouse [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Howard02">Howard 2002</a>].</p>
</section>
<section title="Noncompliant Code Example">
<p>In this noncompliant code example, the user can specify a locked device or a FIFO (first-in, first-out) file name, which can cause the program to hang on the call to <code>fopen()</code>:</p>
<sample language="cpp">#include &lt;stdio.h&gt;
 
void func(const char *file_name) {
FILE *file;
if ((file = fopen(file_name, "wb")) == NULL) {
/* Handle error */
}
/* Operate on the file */
if (fclose(file) == EOF) {
/* Handle error */
}
}</sample>
</section>
<section title="Compliant Solution (POSIX)">
<p>POSIX defines the <code>O_NONBLOCK</code> flag to <code>open()</code>, which ensures that delayed operations on a file do not hang the program [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013">IEEE Std 1003.1:2013</a>].</p>
<blockquote>
<p>When opening a FIFO with <code>O_RDONLY</code> or <code>O_WRONLY</code> set:</p>
<ul>
<li>If <code>O_NONBLOCK</code> is set, an <code>open()</code> for reading-only returns without delay. An <code>open()</code> for writing-only returns an error if no process currently has the file open for reading.</li>
<li>If <code>O_NONBLOCK</code> is clear, an <code>open()</code> for reading-only blocks the calling thread until a thread opens the file for writing. An <code>open()</code> for writing-only blocks the calling thread until a thread opens the file for reading.</li>
</ul>
<p>When opening a block special or character special file that supports nonblocking opens:</p>
<ul>
<li>If <code>O_NONBLOCK</code> is set, the <code>open()</code> function returns without blocking for the device to be ready or available; subsequent behavior is device-specific.</li>
<li>If <code>O_NONBLOCK</code> is clear, the <code>open()</code> function blocks the calling thread until the device is ready or available before returning.</li>
</ul>
<p>Otherwise, the behavior of <code>O_NONBLOCK</code> is unspecified.</p>
</blockquote>
<p>Once the file is open, programmers can use the POSIX <code>lstat()</code> and <code>fstat()</code> functions to obtain information about a file and the <code>S_ISREG()</code> macro to determine if the file is a regular file. </p>
<p>Because the behavior of <code>O_NONBLOCK</code> on subsequent calls to <code>read()</code> or <code>write()</code> is <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unspecifiedbehavior">unspecified</a>, it is advisable to disable the flag after it has been determined that the file in question is not a special device.</p>
<p>When available (Linux 2.1.126+, FreeBSD, Solaris 10, POSIX.1-2008), the <code>O_NOFOLLOW</code> flag should also be used. (See <a href="https://wiki.sei.cmu.edu/confluence/display/c/POS01-C.+Check+for+the+existence+of+links+when+dealing+with+files">POS01-C. Check for the existence of links when dealing with files</a>.) When <code>O_NOFOLLOW</code> is not available, symbolic link checks should use the method from <a href="https://wiki.sei.cmu.edu/confluence/display/c/POS35-C.+Avoid+race+conditions+while+checking+for+the+existence+of+a+symbolic+link">POS35-C. Avoid race conditions while checking for the existence of a symbolic link</a>.</p>
<sample language="cpp">#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#ifdef O_NOFOLLOW
#define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK
#else
#define OPEN_FLAGS O_NONBLOCK
#endif
void func(const char *file_name) {
struct stat orig_st;
struct stat open_st;
int fd;
int flags;
if ((lstat(file_name, &amp;orig_st) != 0) ||
(!S_ISREG(orig_st.st_mode))) {
/* Handle error */
}
/* Race window */
fd = open(file_name, OPEN_FLAGS | O_WRONLY);
if (fd == -1) {
/* Handle error */
}
if (fstat(fd, &amp;open_st) != 0) {
/* Handle error */
}
if ((orig_st.st_mode != open_st.st_mode) ||
(orig_st.st_ino != open_st.st_ino) ||
(orig_st.st_dev != open_st.st_dev)) {
/* The file was tampered with */
}
/*
* Optional: drop the O_NONBLOCK now that we are sure
* this is a good file.
 */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
/* Handle error */
}
if (fcntl(fd, F_SETFL, flags &amp; ~O_NONBLOCK) == -1) {
/* Handle error */
}
/* Operate on the file */
if (close(fd) == -1) {
/* Handle error */
}
}</sample>
<p>This code contains an intractable <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-TOCTOU">TOCTOU (time-of-check, time-of-use)</a> race condition under which an attacker can alter the file referenced by <code>file_name</code> following the call to <code>lstat()</code> but before the call to <code>open()</code>. The switch will be discovered after the file is opened, but opening the file cannot be prevented in the case where this action itself causes undesired behavior. (See <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO45-C.+Avoid+TOCTOU+race+conditions+while+accessing+files">FIO45-C. Avoid TOCTOU race conditions while accessing files</a> for more information about TOCTOU race conditions.)</p>
<p>Essentially, an attacker can switch out a file for one of the file types shown in the following table with the specified effect.</p>
<p>File Types and Effects</p>
<table>
<tbody>
<tr>
<th>
Type
</th>
<th>
Note on Effect
</th>
</tr>
<tr>
<td>
Another regular file
</td>
<td>
The
<code>fstat()</code>
verification fails.
</td>
</tr>
<tr>
<td>
FIFO
</td>
<td>
Either
<code>open()</code>
returns
<code>-1</code>
and sets
<code>errno</code>
to
<code>ENXIO</code>
, or
<code>open()</code>
succeeds and the
<code>fstat()</code>
verification fails.
</td>
</tr>
<tr>
<td>
Symbolic link
</td>
<td>
<code>open()</code>
returns
<code>-1</code>
if
<code>O_NOFOLLOW</code>
is available; otherwise, the
<code>fstat()</code>
verification fails.
</td>
</tr>
<tr>
<td>
Special device
</td>
<td>
Usually the
<code>fstat()</code>
verification fails on
<code>st_mode</code>
. This can still be a problem if the device is one for which just opening (or closing) it causes a side effect. If
<code>st_mode</code>
compares equal, then the device is one that, after opening, appears to be a regular file. It would then fail the
<code>fstat()</code>
verification on
<code>st_dev</code>
and
<code>st_ino</code>
(unless it happens to be the
<em>
same
</em>
file, as can happen with
<code>/dev/fd/*</code>
on Solaris, but this would not be a problem).
</td>
</tr>
</tbody>
</table>
<p>To be compliant with this rule and to prevent this TOCTOU race condition, <code>file_name</code> must refer to a file in a secure directory. (See <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO15-C.+Ensure+that+file+operations+are+performed+in+a+secure+directory">FIO15-C. Ensure that file operations are performed in a secure directory</a>.)</p>
</section>
<section title="Noncompliant Code Example (Windows)">
<p>This noncompliant code example uses the <code>GetFileType()</code> function to attempt to prevent opening a special file: </p>
<sample language="cpp">#include &lt;Windows.h&gt;
void func(const TCHAR *file_name) {
HANDLE hFile = CreateFile(
file_name,
GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
/* Handle error */
} else if (GetFileType(hFile) != FILE_TYPE_DISK) {
/* Handle error */
CloseHandle(hFile);
} else {
/* Operate on the file */
CloseHandle(hFile);
}
}</sample>
<p>Although tempting, the Win32 <code>GetFileType()</code> function is dangerous in this case. If the file name given identifies a named pipe that is currently blocking on a read request, the call to <code>GetFileType()</code> will block until the read request completes. This provides an effective attack vector for a <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service">denial-of-service attack</a> on the application. Furthermore, the act of opening a file handle may cause side effects, such as line states being set to their default voltage when opening a serial device.</p>
</section>
<section title="Compliant Solution (Windows)">
<p>Microsoft documents a list of reserved identifiers that represent devices and have a device namespace to be used specifically by devices [<a href="http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx">MSDN</a>]. In this compliant solution, the <code>isReservedName()</code> function can be used to determine if a specified path refers to a device. Care must be taken to avoid a TOCTOU race condition when first testing a path name using the <code>isReservedName()</code> function and then later operating on that path name. </p>
<sample language="cpp">#include &lt;ctype.h&gt;
#include &lt;stdbool.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
static bool isReservedName(const char *path) {
/* This list of reserved names comes from MSDN */
static const char *reserved[] = {
"nul", "con", "prn", "aux", "com1", "com2", "com3",
"com4", "com5", "com6", "com7", "com8", "com9",
"lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6",
"lpt7", "lpt8", "lpt9"
};
bool ret = false;
/*
* First, check to see if this is a device namespace, which
* always starts with \\.\, because device namespaces are not
* valid file paths.
*/
  if (!path || 0 == strncmp(path, "\\\\.\\", 4)) {
return true;
}
/* Compare against the list of ancient reserved names */
for (size_t i = 0; !ret &amp;&amp;
i &lt; sizeof(reserved) / sizeof(*reserved); ++i) {
/*
* Because Windows uses a case-insensitive file system, operate on
* a lowercase version of the given filename. Note: This ignores
* globalization issues and assumes ASCII characters.
*/
if (0 == _stricmp(path, reserved[i])) {
ret = true;
}
}
return ret;
}</sample>
</section>
<section title="Risk Assessment">
<p>Allowing operations that are appropriate only for regular files to be performed on devices can result in <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service">denial-of-service attacks</a> or more serious <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit">exploits</a> depending on the platform.</p>
<table>
<tbody>
<tr>
<th>
Rule
</th>
<th>
Severity
</th>
<th>
Likelihood
</th>
<th>
Remediation Cost
</th>
<th>
Priority
</th>
<th>
Level
</th>
</tr>
<tr>
<td>
FIO32-C
</td>
<td>
Medium
</td>
<td>
Unlikely
</td>
<td>
Medium
</td>
<td>
<strong>P4</strong>
</td>
<td>
<strong>L3</strong>
</td>
</tr>
</tbody>
</table>
</section>
<section title="Automated Detection">
<table>
<tbody>
<tr>
<th>
Tool
</th>
<th>
Version
</th>
<th>
Checker
</th>
<th>
Description
</th>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Rose">
Compass/ROSE
</a>
</td>
<td>
</td>
<td>
</td>
<td>
Could detect some violations of this rule. This rule applies only to untrusted file name strings, and ROSE cannot tell which strings are trusted and which are not. The best heuristic is to note if there is any verification of the file name before or after the
<code>fopen()</code>
call. If there is any verification, then the file opening should be preceded by an
<code>lstat()</code>
call and succeeded by an
<code>fstat()</code>
call. Although that does not enforce the rule completely, it does indicate that the coder is aware of the
<code>lstat-fopen</code>
-
<code>fstat</code>
idiom
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Helix+QAC">
Helix QAC
</a>
</td>
<td>
2022.1
</td>
<td>
<strong>C4921, C4922, C4923</strong>
<strong>C++4921, C++4922, C++4923</strong>
</td>
<td>
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Parasoft">
Parasoft C/C++test
</a>
</td>
<td>
2021.2
</td>
<td>
<strong>CERT_C-FIO32-a</strong>
</td>
<td>
Protect against file name injection
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Polyspace+Bug+Finder">
Polyspace Bug Finder
</a>
</td>
<td>
R2022a
</td>
<td>
<a href="https://www.mathworks.com/help/bugfinder/ref/certcrulefio32c.html">
CERT C: Rule FIO32-C
</a>
</td>
<td>
Checks for inappropriate I/O operation on device files (rule fully covered)
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/PRQA+QA-C">
PRQA QA-C
</a>
</td>
<td>
9.7
</td>
<td>
<strong>4921, 4922, 4923</strong>
</td>
<td>
Enforced by QAC
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046345">
PRQA QA-C++
</a>
</td>
<td>
4.4
</td>
<td>
<strong>4921, 4922, 4923</strong>
</td>
<td>
</td>
</tr>
</tbody>
</table>
</section>
<section title="Related Vulnerabilities">
<p>Search for <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability">vulnerabilities</a> resulting from the violation of this rule on the <a href="https://www.kb.cert.org/vulnotes/bymetric?searchview&amp;query=FIELD+KEYWORDS+contains+FIO32-C">CERT website</a>.</p>
</section>
<section title="Related Guidelines">
<p><a href="https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines">Key here</a> (explains table format and definitions)</p>
<table>
<tbody>
<tr>
<th>
Taxonomy
</th>
<th>
Taxonomy item
</th>
<th>
Relationship
</th>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO05-C.+Identify+files+using+multiple+file+attributes">
FIO05-C. Identify files using multiple file attributes
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO15-C.+Ensure+that+file+operations+are+performed+in+a+secure+directory">
FIO15-C. Ensure that file operations are performed in a secure directory
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS01-C.+Check+for+the+existence+of+links+when+dealing+with+files">
POS01-C. Check for the existence of links when dealing with files
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS35-C.+Avoid+race+conditions+while+checking+for+the+existence+of+a+symbolic+link">
POS35-C. Avoid race conditions while checking for the existence of a symbolic link
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java">
CERT Oracle Secure Coding Standard for Java
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/java/FIO00-J.+Do+not+operate+on+files+in+shared+directories">
FIO00-J. Do not operate on files in shared directories
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
</tbody>
</table>
</section>
<section title="CERT-CWE Mapping Notes">
<p><a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes">Key here</a> for mapping notes</p>
<p><strong>CWE-67 and FIO32-C</strong></p>
<p>FIO32-C = Union( CWE-67, list) where list =</p>
<ul>
<li>Treating trusted device names like regular files in Windows.</li>
</ul>
<ul>
<li>Treating device names (both trusted and untrusted) like regular files in POSIX</li>
</ul>
</section>
<section title="Bibliography">
<table>
<tbody>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Garfinkel96">
Garfinkel 1996
</a>
]
</td>
<td>
Section 5.6, "Device Files"
</td>
</tr>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Howard02">
Howard 2002
</a>
]
</td>
<td>
Chapter 11, "Canonical Representation Issues"
</td>
</tr>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013">
IEEE Std 1003.1:2013
</a>
]
</td>
<td>
XSH, System Interfaces,
<code>open</code>
</td>
</tr>
<tr>
<td>
[
<a href="http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx">
MSDN
</a>
]
</td>
<td>
</td>
</tr>
</tbody>
</table>
</section>
</qhelp>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<!-- THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. -->
<overview>
<p>This query implements the CERT-C rule FIO32-C:</p>
<blockquote>
<p>Do not perform operations on devices that are only appropriate for files</p>
</blockquote>
</overview>
<include src="DoNotPerformFileOperationsOnDevices-standard.qhelp" />
<references>
<li>
CERT-C:
<a href="https://wiki.sei.cmu.edu/confluence/display/c">FIO32-C: Do not perform operations on devices that are only appropriate for files</a>
.
</li>
</references>
</qhelp>

Просмотреть файл

@ -0,0 +1,64 @@
/**
* @id c/cert/do-not-perform-file-operations-on-devices
* @name FIO32-C: Do not perform operations on devices that are only appropriate for files
* @description Performing file operations on devices can result in crashes.
* @kind path-problem
* @precision medium
* @problem.severity error
* @tags external/cert/id/fio32-c
* correctness
* security
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
// Query TaintedPath.ql from the CodeQL standard library
/**
* A function for opening a file.
*/
class FileFunction extends FunctionWithWrappers {
FileFunction() {
exists(string nme | this.hasGlobalName(nme) |
nme = ["fopen", "_fopen", "_wfopen", "open", "_open", "_wopen"]
or
// create file function on windows
nme.matches("CreateFile%")
)
or
this.hasQualifiedName("std", "fopen")
or
// on any of the fstream classes, or filebuf
exists(string nme | this.getDeclaringType().hasQualifiedName("std", nme) |
nme = ["basic_fstream", "basic_ifstream", "basic_ofstream", "basic_filebuf"]
) and
// we look for either the open method or the constructor
(this.getName() = "open" or this instanceof Constructor)
}
// conveniently, all of these functions take the path as the first parameter!
override predicate interestingArg(int arg) { arg = 0 }
}
class TaintedPathConfiguration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) {
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
}
}
from
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
PathNode sinkNode, string taintCause, string callChain
where
not isExcluded(taintedArg, IO3Package::doNotPerformFileOperationsOnDevicesQuery()) and
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause)
select taintedArg, sourceNode, sinkNode,
"This argument to a file access function is derived from $@ and then passed to " + callChain,
taintSource, "user input (" + taintCause + ")"

Просмотреть файл

@ -0,0 +1,392 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<section title="Description">
<p>Errors can occur when incorrect assumptions are made about the type of data being read. These assumptions may be violated, for example, when binary data has been read from a file instead of text from a user's terminal or the output of a process is piped to <code>stdin.</code> (See <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO14-C.+Understand+the+difference+between+text+mode+and+binary+mode+with+file+streams">FIO14-C. Understand the difference between text mode and binary mode with file streams</a>.) On some systems, it may also be possible to input a null byte (as well as other binary codes) from the keyboard.</p>
<p>Subclause 7.21.7.2 of the C Standard [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011">ISO/IEC 9899:2011</a>] says,</p>
<blockquote>
<p>The <code>fgets</code> function returns <code>s</code> if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned.</p>
</blockquote>
<p>The wide-character function <code>fgetws()</code> has the same behavior. Therefore, if <code>fgets()</code> or <code>fgetws()</code> returns a non-null pointer, it is safe to assume that the array contains data. However, it is erroneous to assume that the array contains a nonempty string because the data may contain null characters.</p>
</section>
<section title="Noncompliant Code Example">
<p>This noncompliant code example attempts to remove the trailing newline (<code>\n</code>) from an input line. The <code>fgets()</code> function is typically used to read a newline-terminated line of input from a stream. It takes a size parameter for the destination buffer and copies, at most, <code>size - 1</code> characters from a stream to a character array.</p>
<sample language="cpp">#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
enum { BUFFER_SIZE = 1024 };
void func(void) {
char buf[BUFFER_SIZE];
if (fgets(buf, sizeof(buf), stdin) == NULL) {
/* Handle error */
}
buf[strlen(buf) - 1] = '\0';
}</sample>
<p>The <code>strlen()</code> function computes the length of a string by determining the number of characters that precede the terminating null character. A problem occurs if the first character read from the input by <code>fgets()</code> happens to be a null character. This may occur, for example, if a binary data file is read by the <code>fgets()</code> call [<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Lai06">Lai 2006</a>]. If the first character in <code>buf</code> is a null character, <code>strlen(buf)</code> returns 0, the expression <code>strlen(buf) - 1</code> wraps around to a large positive value, and a write-outside-array-bounds error occurs.</p>
</section>
<section title="Compliant Solution">
<p>This compliant solution uses <code>strchr()</code> to replace the newline character in the string if it exists:</p>
<sample language="cpp">#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
enum { BUFFER_SIZE = 1024 };
void func(void) {
char buf[BUFFER_SIZE];
char *p;
if (fgets(buf, sizeof(buf), stdin)) {
p = strchr(buf, '\n');
if (p) {
*p = '\0';
}
} else {
/* Handle error */
}
}</sample>
</section>
<section title="Risk Assessment">
<p>Incorrectly assuming that character data has been read can result in an out-of-bounds memory write or other flawed logic.</p>
<table>
<tbody>
<tr>
<th>
Rule
</th>
<th>
Severity
</th>
<th>
Likelihood
</th>
<th>
Remediation Cost
</th>
<th>
Priority
</th>
<th>
Level
</th>
</tr>
<tr>
<td>
FIO37-C
</td>
<td>
High
</td>
<td>
Probable
</td>
<td>
Medium
</td>
<td>
<strong>P12</strong>
</td>
<td>
<strong>L1</strong>
</td>
</tr>
</tbody>
</table>
</section>
<section title="Automated Detection">
<table>
<tbody>
<tr>
<th>
Tool
</th>
<th>
Version
</th>
<th>
Checker
</th>
<th>
Description
</th>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152428">
Astrée
</a>
</td>
<td>
20.10
</td>
<td>
</td>
<td>
Supported: Astrée reports defects due to returned (empty) strings.
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Axivion+Bauhaus+Suite">
Axivion Bauhaus Suite
</a>
</td>
<td>
7.2.0
</td>
<td>
<strong>CertC-FIO37</strong>
</td>
<td>
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/CodeSonar">
CodeSonar
</a>
</td>
<td>
6.2p0
</td>
<td>
<strong>(general)</strong>
</td>
<td>
Considers the possibility that
<code>fgets()</code>
and
<code>fgetws()</code>
may return empty strings (Warnings of various classes may be triggered depending on subsequent operations on those strings. For example, the noncompliant code example cited above would trigger a buffer underrun warning.)
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Rose">
Compass/ROSE
</a>
</td>
<td>
</td>
<td>
</td>
<td>
Could detect some violations of this rule (In particular, it could detect the noncompliant code example by searching for
<code>fgets()</code>
, followed by
<code>strlen() - 1</code>
, which could be 1. The crux of this rule is that a string returned by
<code>fgets()</code>
could still be empty, because the first
<code>char</code>
is '
<code>\0</code>
'. There are probably other code examples that violate this guideline; they would need to be enumerated before ROSE could detect them.)
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Helix+QAC">
Helix QAC
</a>
</td>
<td>
2022.1
</td>
<td>
<strong>C4911, C4912, C4913</strong>
<strong>C++4911, C++4912, C++4913</strong>
</td>
<td>
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/LDRA">
LDRA tool suite
</a>
</td>
<td>
9.7.1
</td>
<td>
<strong>44 S</strong>
</td>
<td>
Enhanced enforcement
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Parasoft">
Parasoft C/C++test
</a>
</td>
<td>
2021.2
</td>
<td>
<strong>CERT_C-FIO37-a</strong>
</td>
<td>
Avoid accessing arrays out of bounds
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/Polyspace+Bug+Finder">
Polyspace Bug Finder
</a>
</td>
<td>
R2022a
</td>
<td>
<a href="https://www.mathworks.com/help/bugfinder/ref/certcrulefio37c.html">
CERT C: Rule FIO37-C
</a>
</td>
<td>
Checks for use of indeterminate string (rule fully covered)
</td>
</tr>
<tr>
<td>
<a href="https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=142409849">
PRQA QA-C++
</a>
</td>
<td>
4.4
</td>
<td>
<strong>2844</strong>
</td>
<td>
</td>
</tr>
</tbody>
</table>
</section>
<section title="Related Vulnerabilities">
<p>Search for <a href="https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability">vulnerabilities</a> resulting from the violation of this rule on the <a href="https://www.kb.cert.org/vulnotes/bymetric?searchview&amp;query=FIELD+KEYWORDS+contains+FIO37-C">CERT website</a>.</p>
</section>
<section title="Related Guidelines">
<p><a href="https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines">Key here</a> (explains table format and definitions)</p>
<table>
<tbody>
<tr>
<th>
Taxonomy
</th>
<th>
Taxonomy item
</th>
<th>
Relationship
</th>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO14-C.+Understand+the+difference+between+text+mode+and+binary+mode+with+file+streams">
FIO14-C. Understand the difference between text mode and binary mode with file streams
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard">
CERT C Secure Coding Standard
</a>
</td>
<td>
<a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152445">
FIO20-C. Avoid unintentional truncation when using fgets() or fgetws()
</a>
</td>
<td>
Prior to 2018-01-12: CERT: Unspecified Relationship
</td>
</tr>
<tr>
<td>
<a href="https://cwe.mitre.org/data/index.html">
CWE 2.11
</a>
</td>
<td>
<a href="http://cwe.mitre.org/data/definitions/241.html">
CWE-241
</a>
, Improper Handling of Unexpected Data Type
</td>
<td>
2017-07-05: CERT: Rule subset of CWE
</td>
</tr>
</tbody>
</table>
</section>
<section title="CERT-CWE Mapping Notes">
<p><a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes">Key here</a> for mapping notes</p>
<p><strong>CWE-241 and FIO37-C</strong></p>
<p>CWE-241 = Union( FIO37-C, list) where list =</p>
<ul>
<li>Improper handling of unexpected data type that does not come from the fgets() function.</li>
</ul>
</section>
<section title="Bibliography">
<table>
<tbody>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011">
ISO/IEC 9899:2011
</a>
]
</td>
<td>
Subclause 7.21.7.2, "The
<code>fgets</code>
Function"
Subclause 7.29.3.2, "The
<code>fgetws</code>
Function"
</td>
</tr>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Lai06">
Lai 2006
</a>
]
</td>
<td>
</td>
</tr>
<tr>
<td>
[
<a href="https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Seacord2013">
Seacord 2013
</a>
]
</td>
<td>
Chapter 2, "Strings"
</td>
</tr>
</tbody>
</table>
</section>
</qhelp>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<!-- THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. -->
<overview>
<p>This query implements the CERT-C rule FIO37-C:</p>
<blockquote>
<p>Do not assume that fgets() or fgetws() returns a nonempty string when successful</p>
</blockquote>
</overview>
<include src="SuccessfulFgetsOrFgetwsMayReturnAnEmptyString-standard.qhelp" />
<references>
<li>
CERT-C:
<a href="https://wiki.sei.cmu.edu/confluence/display/c">FIO37-C: Do not assume that fgets() or fgetws() returns a nonempty string when successful</a>
.
</li>
</references>
</qhelp>

Просмотреть файл

@ -0,0 +1,48 @@
/**
* @id c/cert/successful-fgets-or-fgetws-may-return-an-empty-string
* @name FIO37-C: Do not assume that fgets() or fgetws() returns a nonempty string when successful
* @description A string returned by fgets() or fegtws() might be empty.
* @kind problem
* @precision high
* @problem.severity error
* @tags external/cert/id/fio37-c
* correctness
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.FgetsErrorManagement
import codingstandards.cpp.Dereferenced
import semmle.code.cpp.dataflow.TaintTracking
/*
* CFG nodes that follows a successful call to `fgets`
*/
ControlFlowNode followsNonnullFgets(FgetsLikeCall fgets) {
exists(FgetsGuard guard |
//fgets.getLocation().getStartLine() = [60] and
fgets = guard.getFgetCall() and
(
result = guard.getNonNullSuccessor()
or
result = followsNonnullFgets(fgets).getASuccessor()
)
)
}
from Expr e, FgetsLikeCall fgets
where
not isExcluded(e, IO3Package::successfulFgetsOrFgetwsMayReturnAnEmptyStringQuery()) and
e = followsNonnullFgets(fgets) and
(
e instanceof ArrayExpr
or
e instanceof PointerDereferenceExpr
) and
not exists(GuardCondition guard |
guard.controls(e.getBasicBlock(), _) and guard = followsNonnullFgets(fgets)
)
select e, "The string $@ could be empty when accessed at this location.", fgets.getBuffer(),
fgets.getBuffer().toString()

Просмотреть файл

@ -58,9 +58,6 @@ class BuffAccessExpr extends Expr {
// dereferenced expressions
this instanceof DereferencedExpr
or
// any array access `array[0]`
this = any(ArrayExpr ae).getArrayBase()
or
// any parameter to a function
this = any(FunctionCall fc).getAnArgument()
}

Просмотреть файл

@ -0,0 +1,33 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<section title="Classification">
<ul>
<li>required</li>
<li>implementation</li>
<li>automated</li>
</ul>
</section>
<section title="Rationale">
<p>
...
</p>
</section>
<section title="Exception">
<p>
...
</p>
</section>
<example>
<sample src="standard-example.c"></sample>
</example>
<section title="See more">
<ul>
<li>...</li>
</ul>
</section>
</qhelp>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<!-- THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. -->
<overview>
<p>This query implements the CERT-C rule STR37-C:</p>
<blockquote>
<p>Arguments to character-handling functions must be representable as an unsigned char</p>
</blockquote>
</overview>
<include src="ToCharacterHandlingFunctionsRepresentableAsUChar-standard.qhelp" />
<references>
<li>
CERT-C:
<a href="https://wiki.sei.cmu.edu/confluence/display/c">STR37-C: Arguments to character-handling functions must be representable as an unsigned char</a>
.
</li>
</references>
</qhelp>

Просмотреть файл

@ -0,0 +1,29 @@
/**
* @id c/cert/to-character-handling-functions-representable-as-u-char
* @name STR37-C: Arguments to character-handling functions must be representable as an unsigned char
* @description Arguments that are not representable as an unsigned char may produce unpredictable
* program behavior.
* @kind problem
* @precision high
* @problem.severity error
* @tags external/cert/id/str37-c
* correctness
* security
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.CharFunctions
from FunctionCall fc, Expr arg
where
not isExcluded(fc, Strings2Package::toCharacterHandlingFunctionsRepresentableAsUCharQuery()) and
// examine all impacted functions
fc.getTarget() instanceof CToOrIsCharFunction and
arg = fc.getArgument(0).getFullyConverted() and
// report on cases where either the explicit or implicit cast
// on the parameter type is not unsigned
not arg.(CStyleCast).getExpr().getType() instanceof UnsignedCharType
select fc, "$@ to character-handling function may not be representable as an unsigned char.", arg,
"Argument"

Просмотреть файл

@ -1,4 +1,4 @@
name: cert-c-coding-standards-tests
version: 2.3.0
version: 2.4.0
libraryPathDependencies: cert-c-coding-standards
extractor: cpp

Просмотреть файл

@ -0,0 +1,40 @@
edges
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... |
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name |
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection |
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... |
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name |
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection |
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... |
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name |
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection |
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... |
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name |
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection |
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... |
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name |
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection |
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... |
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name |
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection |
subpaths
nodes
| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.c:20:15:20:23 | file_name | semmle.label | file_name |
| test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument |
| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... |
| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... |
| test.c:21:8:21:16 | file_name | semmle.label | file_name |
| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection |
| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection |
| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.c:45:15:45:23 | file_name | semmle.label | file_name |
| test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument |
| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... |
| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... |
| test.c:46:29:46:37 | file_name | semmle.label | file_name |
| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection |
| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection |
#select
| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen((unnamed parameter 0)) | test.c:20:15:20:23 | file_name | user input (scanf) |
| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) |

Просмотреть файл

@ -0,0 +1 @@
rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql

Просмотреть файл

@ -0,0 +1,50 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
void func(const char *file_name) {
FILE *file;
if ((file = fopen(file_name, "wb")) == NULL) {
/* Handle error */
}
/* Operate on the file */
if (fclose(file) == EOF) {
/* Handle error */
}
}
int main(int argc, char **argv) {
char file_name[20];
scanf("%s", file_name);
func(file_name); // NON_COMPLIANT
func("file_name"); // COMPLIANT
}
// --- Windows ---
typedef void *HANDLE;
#define GENERIC_READ 0x80000000
#define GENERIC_WRITE 0x40000000
#define OPEN_EXISTING 3
#define FILE_ATTRIBUTE_NORMAL 0x00000080
typedef const char *LPCTSTR;
typedef unsigned long DWORD;
typedef struct _SECURITY_ATTRIBUTES {
} * LPSECURITY_ATTRIBUTES;
typedef bool BOOL;
HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
BOOL CloseHandle(HANDLE hObject);
void funcWin() {
char file_name[20];
scanf("%s", file_name);
HANDLE hFile = CreateFile(file_name, // NON_COMPLIANT
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);
}

Просмотреть файл

@ -1,6 +1,5 @@
| test.c:11:12:11:19 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |
| test.c:19:12:19:19 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |
| test.c:77:13:77:22 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |
| test.c:96:12:96:19 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |
| test.c:101:10:102:15 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |
| test.c:116:12:116:19 | ... != ... | This check is only portable to platforms where type `char` is less wide than type `int`. |

Просмотреть файл

@ -74,7 +74,7 @@ void f4(void) {
size_t i = 0;
while ((wc = getwc(stdin)) != L'\n' // COMPLIANT
&& wc != WEOF) { // NON_PORTABLE
&& wc != WEOF) { // PORTABLE
if (i < BUFFER_SIZE - 1) {
buf[i++] = wc;
}

Просмотреть файл

@ -0,0 +1 @@
| test.c:13:3:13:22 | access to array | The string $@ could be empty when accessed at this location. | test.c:8:13:8:15 | buf | buf |

Просмотреть файл

@ -0,0 +1 @@
rules/FIO37-C/SuccessfulFgetsOrFgetwsMayReturnAnEmptyString.ql

Просмотреть файл

@ -0,0 +1,56 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
char buf[1024];
void f1() {
if (fgets(buf, sizeof(buf), stdin) == NULL) {
/*null*/
return;
}
/*notnull*/
buf[strlen(buf) - 1] = '\0'; // NON_COMPLIANT
return;
}
void f2() {
char *p;
if (fgets(buf, sizeof(buf), stdin)) {
/*notnull*/
p = strchr(buf, '\n');
if (p) {
*p = '\0'; // COMPLIANT
}
}
return;
}
static inline bool strends(const char *str, const char *postfix) {
if (strlen(str) < strlen(postfix))
return false;
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
void f3() {
if (fgets(buf, sizeof(buf), stdin)) {
/*notnull*/
if (strends(buf, "\n")) {
buf[strlen(buf) - 1] = '\0'; // COMPLIANT
}
}
return;
}
void f4() {
char *p;
if (fgets(buf, sizeof(buf), stdin) == NULL) {
return;
}
p = strchr(buf, '\n');
if (p) {
*p = '\0'; // COMPLIANT
}
return;
}

Просмотреть файл

@ -0,0 +1,28 @@
| test.c:7:3:7:9 | call to isalnum | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:11:7:12 | (int)... | Argument |
| test.c:8:3:8:13 | call to isalpha | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:11:8:12 | (int)... | Argument |
| test.c:10:3:10:9 | call to isblank | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:11:10:12 | (int)... | Argument |
| test.c:11:3:11:9 | call to iscntrl | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:11:11:12 | (int)... | Argument |
| test.c:12:3:12:13 | call to isdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:11:12:12 | (int)... | Argument |
| test.c:13:3:13:13 | call to isgraph | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:11:13:12 | (int)... | Argument |
| test.c:14:3:14:13 | call to islower | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:11:14:12 | (int)... | Argument |
| test.c:15:3:15:13 | call to isprint | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:11:15:12 | (int)... | Argument |
| test.c:16:3:16:9 | call to ispunct | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:11:16:12 | (int)... | Argument |
| test.c:17:3:17:13 | call to __isspace | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:11:17:12 | (int)... | Argument |
| test.c:18:3:18:13 | call to isupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:11:18:12 | (int)... | Argument |
| test.c:19:3:19:10 | call to isxdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:12:19:13 | (int)... | Argument |
| test.c:21:3:21:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:11:21:12 | (int)... | Argument |
| test.c:22:3:22:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:11:22:12 | (int)... | Argument |
| test.c:70:3:70:9 | call to isalnum | $@ to character-handling function may not be representable as an unsigned char. | test.c:70:11:70:11 | t | Argument |
| test.c:71:3:71:12 | call to isalpha | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:11:71:11 | t | Argument |
| test.c:73:3:73:9 | call to isblank | $@ to character-handling function may not be representable as an unsigned char. | test.c:73:11:73:11 | t | Argument |
| test.c:74:3:74:9 | call to iscntrl | $@ to character-handling function may not be representable as an unsigned char. | test.c:74:11:74:11 | t | Argument |
| test.c:75:3:75:12 | call to isdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:11:75:11 | t | Argument |
| test.c:76:3:76:12 | call to isgraph | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:11:76:11 | t | Argument |
| test.c:77:3:77:12 | call to islower | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:11:77:11 | t | Argument |
| test.c:78:3:78:12 | call to isprint | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:11:78:11 | t | Argument |
| test.c:79:3:79:9 | call to ispunct | $@ to character-handling function may not be representable as an unsigned char. | test.c:79:11:79:11 | t | Argument |
| test.c:80:3:80:12 | call to __isspace | $@ to character-handling function may not be representable as an unsigned char. | test.c:80:11:80:11 | t | Argument |
| test.c:81:3:81:12 | call to isupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:11:81:11 | t | Argument |
| test.c:82:3:82:10 | call to isxdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:82:12:82:12 | t | Argument |
| test.c:84:3:84:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:84:11:84:11 | t | Argument |
| test.c:85:3:85:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:85:11:85:11 | t | Argument |

Просмотреть файл

@ -0,0 +1 @@
rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.ql

Просмотреть файл

@ -0,0 +1,86 @@
#include <ctype.h>
#include <stdio.h>
void f1() {
char *t;
isalnum(*t); // NON_COMPLIANT
isalpha(*t); // NON_COMPLIANT
// isascii(*t); // Not part of the C Standard
isblank(*t); // NON_COMPLIANT
iscntrl(*t); // NON_COMPLIANT
isdigit(*t); // NON_COMPLIANT
isgraph(*t); // NON_COMPLIANT
islower(*t); // NON_COMPLIANT
isprint(*t); // NON_COMPLIANT
ispunct(*t); // NON_COMPLIANT
isspace(*t); // NON_COMPLIANT
isupper(*t); // NON_COMPLIANT
isxdigit(*t); // NON_COMPLIANT
// toascii(i); // Not part of the C Standard
toupper(*t); // NON_COMPLIANT
tolower(*t); // NON_COMPLIANT
}
void f2() {
unsigned char *t;
isalnum(*t); // COMPLIANT
isalpha(*t); // COMPLIANT
// isascii(*t); // Not part of the C Standard
isblank(*t); // COMPLIANT
iscntrl(*t); // COMPLIANT
isdigit(*t); // COMPLIANT
isgraph(*t); // COMPLIANT
islower(*t); // COMPLIANT
isprint(*t); // COMPLIANT
ispunct(*t); // COMPLIANT
isspace(*t); // COMPLIANT
isupper(*t); // COMPLIANT
isxdigit(*t); // COMPLIANT
// toascii(i); // Not part of the C Standard
toupper(*t); // COMPLIANT
tolower(*t); // COMPLIANT
}
void f3() {
char *t;
isalnum((unsigned char)*t); // COMPLIANT
isalpha((unsigned char)*t); // COMPLIANT
// isascii((unsigned char*)*t); // Not part of the C Standard
isblank((unsigned char)*t); // COMPLIANT
iscntrl((unsigned char)*t); // COMPLIANT
isdigit((unsigned char)*t); // COMPLIANT
isgraph((unsigned char)*t); // COMPLIANT
islower((unsigned char)*t); // COMPLIANT
isprint((unsigned char)*t); // COMPLIANT
ispunct((unsigned char)*t); // COMPLIANT
isspace((unsigned char)*t); // COMPLIANT
isupper((unsigned char)*t); // COMPLIANT
isxdigit((unsigned char)*t); // COMPLIANT
// toascii((unsigned int) i); // Not part of the C Standard
toupper((unsigned char)*t); // COMPLIANT
tolower((unsigned char)*t); // COMPLIANT
}
void f4() {
int t;
isalnum(t); // NON_COMPLIANT
isalpha(t); // NON_COMPLIANT
// isascii(t); // Not part of the C Standard
isblank(t); // NON_COMPLIANT
iscntrl(t); // NON_COMPLIANT
isdigit(t); // NON_COMPLIANT
isgraph(t); // NON_COMPLIANT
islower(t); // NON_COMPLIANT
isprint(t); // NON_COMPLIANT
ispunct(t); // NON_COMPLIANT
isspace(t); // NON_COMPLIANT
isupper(t); // NON_COMPLIANT
isxdigit(t); // NON_COMPLIANT
// toascii(i); // Not part of the C Standard
toupper(t); // NON_COMPLIANT
tolower(t); // NON_COMPLIANT
}

Просмотреть файл

@ -1,3 +1,3 @@
name: common-c-coding-standards
version: 2.3.0
version: 2.4.0
libraryPathDependencies: common-cpp-coding-standards

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,20 @@
| test.c:9:10:11:3 | { ... } | test.c:7:38:9:3 | { ... } |
| test.c:16:39:18:3 | { ... } | test.c:18:10:20:3 | { ... } |
| test.c:25:43:27:3 | { ... } | test.c:27:10:29:3 | { ... } |
| test.c:36:10:38:3 | { ... } | test.c:34:43:36:3 | { ... } |
| test.c:45:10:47:3 | { ... } | test.c:43:46:45:3 | { ... } |
| test.c:53:10:55:3 | { ... } | test.c:51:49:53:3 | { ... } |
| test.c:66:3:66:13 | return ... | test.c:60:49:62:3 | { ... } |
| test.c:72:10:74:3 | { ... } | test.c:70:48:72:3 | { ... } |
| test.c:83:3:83:13 | return ... | test.c:79:48:81:3 | { ... } |
| test.c:87:46:89:3 | { ... } | test.c:89:10:91:3 | { ... } |
| test.c:98:13:100:3 | { ... } | test.c:100:10:102:3 | { ... } |
| test.c:111:10:113:3 | { ... } | test.c:109:14:111:3 | { ... } |
| test.c:118:66:121:3 | { ... } | test.c:118:66:121:3 | { ... } |
| test.c:128:63:131:3 | { ... } | test.c:128:63:131:3 | { ... } |
| test.c:140:10:142:3 | { ... } | test.c:138:54:140:3 | { ... } |
| test.c:147:54:149:3 | { ... } | test.c:149:10:151:3 | { ... } |
| test.c:158:10:161:3 | { ... } | test.c:158:10:161:3 | { ... } |
| test.c:168:10:171:3 | { ... } | test.c:168:10:171:3 | { ... } |
| test.c:176:54:179:3 | { ... } | test.c:179:10:182:3 | { ... } |
| test.c:190:10:193:3 | { ... } | test.c:187:54:190:3 | { ... } |

Просмотреть файл

@ -0,0 +1,5 @@
import cpp
import codingstandards.cpp.FgetsErrorManagement
from FgetsGuard e
select e.getNullSuccessor(), e.getNonNullSuccessor()

Просмотреть файл

@ -0,0 +1,195 @@
#include <stdbool.h>
#include <stdio.h>
char buf[1024];
void f0(FILE *file) {
if (fgets(buf, sizeof(buf), file)) {
/*notNull*/
} else {
/*null*/
}
return;
}
void f1(FILE *file) {
if (!fgets(buf, sizeof(buf), file)) {
/*null*/
} else {
/*notNull*/
}
return;
}
void f2(FILE *file) {
if (fgets(buf, sizeof(buf), file) == 0) {
/*null*/
} else {
/*notNull*/
}
return;
}
void f3(FILE *file) {
if (fgets(buf, sizeof(buf), file) != 0) {
/*notNull*/
} else {
/*null*/
}
return;
}
void f4(FILE *file) {
if (!(fgets(buf, sizeof(buf), file) == 0)) {
/*notNull*/
} else {
/*null*/
}
return;
}
char *f4a(FILE *file) {
if (!(fgets(buf, sizeof(buf), file) == NULL)) {
/*notNull*/
} else {
/*null*/
}
return buf; // NON_COMPLIANT
}
char *f4b(FILE *file) {
if (!(fgets(buf, sizeof(buf), file) == NULL)) {
/*notNull*/
}
/*notNull*/
/*null*/
return buf; // NON_COMPLIANT
}
char *f4c(FILE *file) {
if ((fgets(buf, sizeof(buf), file) != NULL)) {
/*notNull*/
} else {
/*null*/
}
return buf; // NON_COMPLIANT
}
char *f4d(FILE *file) {
if ((fgets(buf, sizeof(buf), file) != NULL)) {
/*notNull*/
}
/*null*/
return buf; // NON_COMPLIANT
}
void f5(FILE *file) {
if (!(fgets(buf, sizeof(buf), file) != 0)) {
/*null*/
} else {
/*notNull*/
}
return;
}
void f6(FILE *file) {
char *ret = fgets(buf, sizeof(buf), file);
bool flag = (ret == NULL);
if (flag) {
/*null*/
} else {
/*notNull*/
}
return;
}
void f7(FILE *file) {
char *ret = fgets(buf, sizeof(buf), file);
bool flag = (ret == NULL);
if (!flag) {
/*notNull*/
} else {
/*null*/
}
return;
}
void f8(FILE *file, bool cond) {
if (fgets(buf, sizeof(buf), file) == NULL || /*notNull*/ cond) {
/*null*/
/*notNull*/
} else {
/*notNull*/
}
return;
}
void f9a(FILE *file, bool cond) {
if (fgets(buf, sizeof(buf), file) != NULL || /*null*/ cond) {
/*null*/
/*notNull*/
} else {
/*null*/
}
return;
}
void f9b(FILE *file, bool cond) {
if (cond || fgets(buf, sizeof(buf), file) != NULL) {
/*notNull*/
} else {
/*null*/
}
return;
}
void f9c(FILE *file, bool cond) {
if (cond || fgets(buf, sizeof(buf), file) == NULL) {
/*null*/
} else {
/*notNnull*/
}
return;
}
void f10(FILE *file, bool cond) {
if (fgets(buf, sizeof(buf), file) == NULL && /*null*/ cond) {
/*null*/
} else {
/*null*/
/*notNull*/
}
return;
}
void f11a(FILE *file, bool cond) {
if (fgets(buf, sizeof(buf), file) != NULL && /*notNull*/ cond) {
/*notNull*/
} else {
/*null*/
/*notNull*/
}
return;
}
void f11b(FILE *file, bool cond) {
if (cond && fgets(buf, sizeof(buf), file) == NULL) {
/*null*/
fprintf(stderr, "Read error or end of file.\n");
} else {
/*notNull*/
fprintf(stderr, "Read OK.\n");
}
return;
}
void f11c(FILE *file, bool cond) {
if (cond && fgets(buf, sizeof(buf), file) != NULL) {
/*notNull*/
fprintf(stderr, "Read error or end of file.\n");
} else {
/*null*/
fprintf(stderr, "Read OK.\n");
}
return;
}

Просмотреть файл

@ -1,4 +1,4 @@
name: common-c-coding-standards-tests
version: 2.3.0
version: 2.4.0
libraryPathDependencies: common-c-coding-standards
extractor: cpp

Просмотреть файл

@ -1,4 +0,0 @@
| test.c:9:1:9:22 | #define MACROFIVE(X) #X | Macro definition uses the # or ## operator. |
| test.c:11:1:11:26 | #define MACROSIX(X,Y) X ## Y | Macro definition uses the # or ## operator. |
| test.c:13:1:13:29 | #define MACROSEVEN "##'" #"#" | Macro definition uses the # or ## operator. |
| test.c:15:1:15:28 | #define MACROEIGHT '##' #"#" | Macro definition uses the # or ## operator. |

Просмотреть файл

@ -1,2 +0,0 @@
// GENERATED FILE - DO NOT MODIFY
import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed

Просмотреть файл

@ -1,19 +0,0 @@
#define MACROONE 1 // COMPLIANT
#define MACROTWO '#' // COMPLIANT
#define MACROTHREE "##" // COMPLIANT
#define MACROFOUR "##" + "#" // COMPLIANT
#define MACROFIVE(X) #X // NON_COMPLIANT
#define MACROSIX(X, Y) X##Y // NON_COMPLIANT
#define MACROSEVEN "##'" #"#" // NON_COMPLIANT
#define MACROEIGHT '##' #"#" // NON_COMPLIANT
#define MACRONINE "##\"\"" + "#" // COMPLIANT
#define MACROTEN "##\"\"'" + "#" // COMPLIANT

Просмотреть файл

@ -0,0 +1,4 @@
| headers/test3.h:0:0:0:0 | headers/test3.h | Header file test3.h is missing expected include guard. | headers/test3.h:0:0:0:0 | headers/test3.h | |
| headers/test4.h:0:0:0:0 | headers/test4.h | Header file test4.h is missing expected include guard. | headers/test4.h:0:0:0:0 | headers/test4.h | |
| headers/test5.h:0:0:0:0 | headers/test5.h | Header file test5.h is missing expected include guard. | headers/test5.h:0:0:0:0 | headers/test5.h | |
| headers/test7.h:0:0:0:0 | headers/test7.h | Header file test7.h is never included by reusing the include guard used by $@. | headers/test6.h:0:0:0:0 | headers/test6.h | include guard |

Просмотреть файл

@ -0,0 +1,2 @@
// GENERATED FILE - DO NOT MODIFY
import codingstandards.cpp.rules.includeguardsnotused.IncludeGuardsNotUsed

Просмотреть файл

@ -0,0 +1,5 @@
// NON_COMPLIANT
#ifndef HEADER_SIX
#define HEADER_SIX
int g5;
#endif

Просмотреть файл

@ -0,0 +1,5 @@
// NON_COMPLIANT
#ifndef HEADER_SIX
#define HEADER_SIX
int g5;
#endif

Просмотреть файл

@ -0,0 +1,14 @@
#include "headers/test1.h"
#include "headers/test2.h"
#include "headers/test3.h" //NON_COMPLIANT
#include "headers/test4.h" //NON_COMPLIANT
#define HEADER_FOUR
#include "headers/test5.h" //NON_COMPLIANT
#include "headers/test6.h" //NON_COMPLIANT - non unique and reported in alert for the next
#include "headers/test7.h" //NON_COMPLIANT - non unique

Просмотреть файл

@ -1,4 +1,4 @@
name: misra-c-coding-standards
version: 2.3.0
version: 2.4.0
suites: codeql-suites
libraryPathDependencies: common-c-coding-standards

Просмотреть файл

@ -0,0 +1,26 @@
/**
* @id c/misra/more-than-one-hash-operator-in-macro-definition
* @name RULE-20-11: A macro parameter immediately following a # operator shall not immediately be followed by a ##
* @description The order of evaluation for the '#' and '##' operators may differ between compilers,
* which can cause unexpected behaviour if more than one '#' or '##' operator is used.
* @kind problem
* @precision very-high
* @problem.severity warning
* @tags external/misra/id/rule-20-11
* correctness
* external/misra/obligation/required
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.Macro
from Macro m
where
not isExcluded(m, Preprocessor2Package::moreThanOneHashOperatorInMacroDefinitionQuery()) and
exists(StringizingOperator one, TokenPastingOperator two |
one.getMacro() = m and
two.getMacro() = m and
one.getOffset() < two.getOffset()
)
select m, "Macro definition uses an # operator followed by a ## operator."

Просмотреть файл

@ -0,0 +1,3 @@
#define A(x) #x /* Compliant */
#define B(x, y) x##y /* Compliant */
#define C(x, y) #x##y /* Non-compliant */

Просмотреть файл

@ -0,0 +1,39 @@
/**
* @id c/misra/macro-parameter-used-as-hash-operand
* @name RULE-20-12: A macro parameter used as an operand to the # or ## operators shall only be used as an operand to these operators
* @description A macro parameter used in different contexts such as: 1) an operand to the # or ##
* operators where it is not expanded, versus 2) elsewhere where it is expanded, makes
* code more difficult to understand.
* @kind problem
* @precision high
* @problem.severity warning
* @tags external/misra/id/rule-20-12
* maintainability
* readability
* external/misra/obligation/required
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.Macro
from FunctionLikeMacro m, MacroInvocation mi, int i, string expanded, string param
where
not isExcluded(m, Preprocessor2Package::macroParameterUsedAsHashOperandQuery()) and
mi = m.getAnInvocation() and
param = m.getParameter(i) and
(
exists(TokenPastingOperator op | op.getMacro() = m and op.getOperand() = param)
or
exists(StringizingOperator op | op.getMacro() = m and op.getOperand() = param)
) and
// An expansion that is equal to "" means the expansion is not used and is optimized away by EDG. This happens when the expanded argument is an operand to `#` or `##`.
// This check ensure there is an expansion that is used.
expanded = mi.getExpandedArgument(i) and
not expanded = "" and
exists(Macro furtherExpandedMacro |
mi.getUnexpandedArgument(i).matches(furtherExpandedMacro.getName() + "%")
)
select m,
"Macro " + m.getName() + " contains use of parameter " + m.getParameter(i) +
" used in multiple contexts."

Просмотреть файл

@ -0,0 +1,7 @@
#define AA 0xffff
#define BB(x) (x) + wow##x /* Non-compliant */
void f(void) {
int32_t wowAA = 0;
/* Expands as wowAA = ( 0xffff ) + wowAA; */
wowAA = BB(AA);
}

Просмотреть файл

@ -0,0 +1,19 @@
/**
* @id c/misra/undef-should-not-be-used
* @name RULE-20-5: #undef should not be used
* @description Using the #undef preprocessor directive makes code more difficult to understand.
* @kind problem
* @precision very-high
* @problem.severity warning
* @tags external/misra/id/rule-20-5
* maintainability
* readability
* external/misra/obligation/advisory
*/
import cpp
import codingstandards.c.misra
from PreprocessorUndef d
where not isExcluded(d, Preprocessor2Package::undefShouldNotBeUsedQuery())
select d, "Use of undef found."

Просмотреть файл

@ -0,0 +1,7 @@
#define QUALIFIER volatile
#undef QUALIFIER /* Non-compliant */
void f(QUALIFIER int32_t p) {
while (p != 0) {
; /* Wait... */
}
}

Просмотреть файл

@ -0,0 +1,68 @@
/**
* @id c/misra/file-open-for-read-and-write-on-different-streams
* @name RULE-22-3: The same file shall not be open for read and write access at the same time on different streams
* @description Accessing the same file for read and write from different streams is undefined
* behavior.
* @kind problem
* @precision high
* @problem.severity error
* @tags external/misra/id/rule-22-3
* correctness
* external/misra/obligation/required
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.standardlibrary.FileAccess
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.controlflow.SubBasicBlocks
/**
* Models calls to `fopen` with different read/write modes
*/
class FOpenSBB extends SubBasicBlockCutNode {
FOpenSBB() {
this instanceof FOpenCall or
this instanceof FileCloseFunctionCall
}
}
SubBasicBlock followsOpen(FOpenCall fopen) {
result = fopen
or
exists(SubBasicBlock mid |
result = mid.getASuccessor() and
mid = followsOpen(fopen) and
// stop recursion when the first stream is closed
not DataFlow::localExprFlow(fopen, result.(FileCloseFunctionCall).getFileExpr())
)
}
class MatchedFOpenCall extends FOpenCall {
FOpenCall pair;
MatchedFOpenCall() {
not pair = this and
pair.getEnclosingFunction() = this.getEnclosingFunction() and
this = followsOpen(pair)
}
FOpenCall getMatch() { result = pair }
}
from MatchedFOpenCall fst, FOpenCall snd
where
not isExcluded(fst, IO3Package::fileOpenForReadAndWriteOnDifferentStreamsQuery()) and
// must be opening the same filename
snd = fst.getMatch() and
globalValueNumber(fst.getFilenameExpr()) = globalValueNumber(snd.getFilenameExpr()) and
(
// different open mode
fst.isReadMode() and snd.isWriteMode()
or
fst.isWriteMode() and snd.isReadMode()
)
select fst,
"The same file was already opened $@. Files should not be read and written at the same time using different streams.",
snd, "here"

Просмотреть файл

@ -0,0 +1,5 @@
#include <stdio.h>
void fn(void) {
FILE *fw = fopen("tmp", "r+"); /* "r+" opens for read/write */
FILE *fr = fopen("tmp", "r"); /* Non-compliant */
}

Просмотреть файл

@ -0,0 +1,36 @@
/**
* @id c/misra/attempt-to-write-to-a-read-only-stream
* @name RULE-22-4: There shall be no attempt to write to a stream which has been opened as read-only
* @description Attempting to write on a read-only stream is undefined behavior.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-22-4
* correctness
* external/misra/obligation/mandatory
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.standardlibrary.FileAccess
import semmle.code.cpp.dataflow.DataFlow
class FileDFConf extends DataFlow::Configuration {
FileDFConf() { this = "FileDFConf" }
override predicate isSource(DataFlow::Node source) {
// source is the return value of a call to fopen
source.asExpr().(FOpenCall).isReadOnlyMode()
}
override predicate isSink(DataFlow::Node sink) {
// sink must be the second parameter of a FsetposCall call
sink.asExpr() = any(FileWriteFunctionCall write).getFileExpr()
}
}
from FileDFConf dfConf, DataFlow::Node source, FileWriteFunctionCall sink
where
not isExcluded(sink, IO3Package::attemptToWriteToAReadOnlyStreamQuery()) and
dfConf.hasFlow(source, DataFlow::exprNode(sink.getFileExpr()))
select sink, "Attempt to write to a $@ opened as read-only.", source, "stream"

Просмотреть файл

@ -0,0 +1,6 @@
#include <stdio.h>
void fn(void) {
FILE *fp = fopen("tmp", "r");
(void)fprintf(fp, "What happens now?"); /* Non-compliant */
(void)fclose(fp);
}

Просмотреть файл

@ -0,0 +1,38 @@
/**
* @id c/misra/pointer-to-a-file-object-dereferenced
* @name RULE-22-5: A pointer to a FILE object shall not be dereferenced
* @description A FILE object should not be directly manipulated.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-22-5
* correctness
* external/misra/obligation/mandatory
*/
import cpp
import codingstandards.c.misra
class IndirectlyDereferencedExpr extends Expr {
IndirectlyDereferencedExpr() {
exists(Call call, string names |
names = ["memcmp", "memcpy"] and
(
call.getTarget().hasGlobalOrStdName(names)
or
exists(MacroInvocation mi | mi.getMacroName() = names and call = mi.getExpr())
) and
this = [call.getArgument(0), call.getArgument(1)]
)
}
}
from Expr e
where
not isExcluded(e, IO3Package::pointerToAFileObjectDereferencedQuery()) and
(
e.(PointerDereferenceExpr).getType().hasName("FILE") or
e.(PointerFieldAccess).getQualifier().getType().(DerivedType).getBaseType().hasName("FILE") or
e.(IndirectlyDereferencedExpr).getType().(DerivedType).getBaseType().hasName("FILE")
)
select e, "Dereferencing an object of type `FILE`."

Просмотреть файл

@ -0,0 +1,8 @@
#include <stdio.h>
FILE *pf1;
FILE *pf2;
FILE f3;
pf2 = pf1; /* Compliant */
f3 = *pf2; /* Non-compliant */

Просмотреть файл

@ -0,0 +1,60 @@
/**
* @id c/misra/eof-shall-be-compared-with-unmodified-return-values
* @name RULE-22-7: The macro EOF shall only be compared with the unmodified return value from any Standard Library
* @description The macro EOF shall only be compared with the unmodified return value from any
* Standard Library function capable of returning EOF
* @kind problem
* @precision high
* @problem.severity error
* @tags external/misra/id/rule-22-7
* correctness
* external/misra/obligation/required
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.ReadErrorsAndEOF
/**
* The getchar() return value propagates directly to a check against EOF macro
* type conversions are not allowed
*/
class DFConf extends DataFlow::Configuration {
DFConf() { this = "DFConf" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof InBandErrorReadFunctionCall
}
override predicate isSink(DataFlow::Node sink) {
exists(EOFWEOFInvocation mi, EqualityOperation eq |
// one operand is the sink
sink.asExpr() = eq.getAnOperand() and
// one operand is an invocation of the EOF macro
mi.getAGeneratedElement() = eq.getAnOperand()
)
}
override predicate isBarrier(DataFlow::Node barrier) {
barrier.asExpr() = any(IntegralConversion c).getExpr()
}
}
// The equality operation `eq` checks a char fetched from `read` against a macro
predicate isWeakMacroCheck(EqualityOperation eq, InBandErrorReadFunctionCall read) {
exists(Expr c, EOFWEOFInvocation mi |
// one operand is the char c fetched from `read`
c = eq.getAnOperand() and
// an operand is an invocation of the EOF macro
mi.getAGeneratedElement() = eq.getAnOperand() and
DataFlow::localExprFlow(read, c)
)
}
from EqualityOperation eq, InBandErrorReadFunctionCall read, DFConf dfConf
where
not isExcluded(eq, IO3Package::eofShallBeComparedWithUnmodifiedReturnValuesQuery()) and
isWeakMacroCheck(eq, read) and
not dfConf.hasFlow(DataFlow::exprNode(read), DataFlow::exprNode(eq.getAnOperand()))
select eq, "The check is not reliable as the type of the return value of $@ is converted.", read,
read.toString()

Просмотреть файл

@ -0,0 +1,10 @@
void f1(void) {
char ch;
ch = (char)getchar();
/*
* The following test is non-compliant. It will not be reliable as the
* return value is cast to a narrower type before checking for EOF.
*/
if (EOF != (int32_t)ch) {
}
}

Просмотреть файл

@ -0,0 +1,19 @@
void f2(void) {
char ch;
ch = (char)getchar();
if (!feof(stdin)) {
}
}
void f3(void) {
int32_t i_ch;
i_ch = getchar();
/*
* The following test is compliant. It will be reliable as the
* unconverted return value is used when checking for EOF.
*/
if (EOF != i_ch) {
char ch;
ch = (char)i_ch;
}
}

Просмотреть файл

@ -0,0 +1,24 @@
/**
* @id c/misra/precaution-include-guards-not-provided
* @name RULE-4-10: Precautions shall be taken in order to prevent the contents of a header file being included more than once
* @description Using anything other than a standard include guard form can make code confusing and
* can lead to multiple or conflicting definitions.
* @kind problem
* @precision very-high
* @problem.severity warning
* @tags external/misra/id/rule-4-10
* correctness
* maintainability
* readability
* external/misra/obligation/required
*/
import cpp
import codingstandards.c.misra
import codingstandards.cpp.rules.includeguardsnotused.IncludeGuardsNotUsed
class PrecautionIncludeGuardsNotProvidedQuery extends IncludeGuardsNotUsedSharedQuery {
PrecautionIncludeGuardsNotProvidedQuery() {
this = Preprocessor2Package::precautionIncludeGuardsNotProvidedQuery()
}
}

Просмотреть файл

@ -0,0 +1,12 @@
<start-of-file>
#if !defined ( identifier )
#define identifier
/* Contents of file */
#endif
<end-of-file>
<start-of-file>
#ifndef identifier
#define identifier
/* Contents of file */
#endif
<end-of-file>

Просмотреть файл

@ -1,4 +1,4 @@
name: misra-c-coding-standards-tests
version: 2.3.0
version: 2.4.0
libraryPathDependencies: misra-c-coding-standards
extractor: cpp

Просмотреть файл

@ -0,0 +1 @@
| test.c:25:1:25:29 | #define MACROTHIRTEEN(X) #X ## X | Macro definition uses an # operator followed by a ## operator. |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql

Просмотреть файл

@ -0,0 +1,27 @@
#define MACROONE 1 // COMPLIANT
#define MACROTWO '#\'-#' + '#' // COMPLIANT
#define MACROTHREE "##" // COMPLIANT
#define MACROFOUR "##" + "#" // COMPLIANT
#define MACROFIVE(X) #X // COMPLIANT
#define MACROSIX(X, Y) X##Y // COMPLIANT
#define MACROSEVEN "##'" #"#" // COMPLIANT
#define MACROEIGHT '##' #"#" // COMPLIANT
#define MACRONINE "##\"\"" + "#" // COMPLIANT
#define MACROTEN "##\"\"'" + "#" // COMPLIANT
#define MACROELEVEN(X) X #X #X // COMPLIANT
#define MACROTWELVE(X) X##X##X // COMPLIANT
#define MACROTHIRTEEN(X) #X##X // NON_COMPLIANT
#define MACROFOURTEEN '#\'-#' + 1 #1 #1 + '#' // COMPLIANT

Просмотреть файл

@ -0,0 +1,2 @@
| test.c:4:1:4:41 | #define BAD_MACRO_WITH_ARG(x) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG contains use of parameter x used in multiple contexts. |
| test.c:5:1:5:48 | #define BAD_MACRO_WITH_ARG_TWO(x,y) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG_TWO contains use of parameter x used in multiple contexts. |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql

Просмотреть файл

@ -0,0 +1,25 @@
#define GOOD_MACRO_WITH_ARG(X) ((X)*X##_scale) // COMPLIANT
#define MACRO 1
#define BAD_MACRO_WITH_ARG(x) (x) + wow##x // NON_COMPLIANT
#define BAD_MACRO_WITH_ARG_TWO(x, y) (x) + wow##x // NON_COMPLIANT
#define MACROONE(x) #x // COMPLIANT
#define MACROTWO(x) x *x // COMPLIANT
#define MACROTHREE(x) "##\"\"'" + (x) // COMPLIANT
#define FOO(x) #x MACROONE(x) // COMPLIANT - no further arg expansion
void f() {
int x;
int x_scale;
int y;
int wowMACRO = 0;
y = GOOD_MACRO_WITH_ARG(x);
wowMACRO = BAD_MACRO_WITH_ARG(MACRO);
wowMACRO = BAD_MACRO_WITH_ARG_TWO(MACRO, 1);
char s[] = MACROONE(MACRO);
y = MACROTWO(MACRO);
MACROTHREE(MACRO);
FOO(x);
}

Просмотреть файл

@ -0,0 +1 @@
| test.c:2:1:2:11 | #undef TEST | Use of undef found. |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-20-5/UndefShouldNotBeUsed.ql

Просмотреть файл

@ -0,0 +1,2 @@
#define TEST 1 // COMPLIANT
#undef TEST // NON_COMPLIANT

Просмотреть файл

@ -0,0 +1,5 @@
| test.c:6:14:6:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:5:14:5:18 | call to fopen | here |
| test.c:17:14:17:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:16:14:16:18 | call to fopen | here |
| test.c:33:14:33:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:32:14:32:18 | call to fopen | here |
| test.c:43:14:43:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:42:14:42:18 | call to fopen | here |
| test.c:59:14:59:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:58:14:58:18 | call to fopen | here |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.ql

Просмотреть файл

@ -0,0 +1,60 @@
#include <stdio.h>
#include <string.h>
void f1(void) {
FILE *fw = fopen("tmp1", "r+");
FILE *fr = fopen("tmp1", "r"); // NON_COMPLIANT
}
void f2(void) {
FILE *fw = fopen("tmp2", "r+");
fclose(fw);
FILE *fr = fopen("tmp2", "r"); // COMPLIANT
}
void f3(void) {
FILE *fr = fopen("tmp3", "r");
FILE *fw = fopen("tmp3", "r+"); // NON_COMPLIANT
fclose(fw);
}
void f4(void) {
FILE *fw = fopen("tmp4", "r");
FILE *fr = fopen("tmp4", "r"); // COMPLIANT
}
void f5(void) {
FILE *fr = fopen("tmp5a", "r");
FILE *fw = fopen("tmp5b", "r+"); // COMPLIANT
}
void f6(void) {
FILE *fw = fopen("tmp6", "w");
FILE *fr = fopen("tmp6", "r"); // NON_COMPLIANT
}
void f7(void) {
FILE *fw = fopen("tmp1", "r"); // COMPLIANT
}
void f8(void) {
char file[] = "tmp8";
FILE *fw = fopen(file, "r+");
FILE *fr = fopen(file, "r"); // NON_COMPLIANT
}
void f9(void) {
char name[50] = "tmp9";
char ext[] = "txt";
char file[] = strcat(name, ext);
FILE *fw = fopen(file, "r+");
FILE *fr = fopen(strcat(name, ext), "r"); // NON_COMPLIANT[FALSE_NEGATIVE]
}
void f10(void) {
char name[50] = "tmp10";
char ext[] = "txt";
strcat(name, ext);
FILE *fw = fopen(name, "r+");
FILE *fr = fopen(name, "r"); // NON_COMPLIANT
}

Просмотреть файл

@ -0,0 +1,2 @@
| test.c:10:3:10:9 | call to fprintf | Attempt to write to a $@ opened as read-only. | test.c:9:14:9:18 | call to fopen | stream |
| test.c:15:3:15:9 | call to fprintf | Attempt to write to a $@ opened as read-only. | test.c:18:14:18:18 | call to fopen | stream |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.ql

Просмотреть файл

@ -0,0 +1,21 @@
#include <stdio.h>
void f0() {
FILE *fp = fopen("file", "r+");
fprintf(fp, "text"); // COMPLIANT
fclose(fp);
}
void f1() {
FILE *fp = fopen("file", "r");
fprintf(fp, "text"); // NON_COMPLIANT
fclose(fp);
}
void f2help(FILE *fp) {
fprintf(fp, "text"); // NON_COMPLIANT
}
void f2() {
FILE *fp = fopen("file", "r");
f2help(fp);
fclose(fp);
}

Просмотреть файл

@ -0,0 +1,7 @@
| test.c:13:8:13:11 | * ... | Dereferencing an object of type `FILE`. |
| test.c:14:8:14:10 | pos | Dereferencing an object of type `FILE`. |
| test.c:16:10:16:12 | pf1 | Dereferencing an object of type `FILE`. |
| test.c:16:15:16:17 | pf2 | Dereferencing an object of type `FILE`. |
| test.c:17:10:17:12 | pf1 | Dereferencing an object of type `FILE`. |
| test.c:17:15:17:17 | pf2 | Dereferencing an object of type `FILE`. |
| test.c:22:8:22:10 | pos | Dereferencing an object of type `FILE`. |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-22-5/PointerToAFileObjectDereferenced.ql

Просмотреть файл

@ -0,0 +1,28 @@
//#include <stdio.h>
#include <string.h>
typedef struct {
int pos;
} FILE;
void f() {
FILE *pf1;
FILE *pf2;
FILE f3;
pf2 = pf1; // COMPLIANT
f3 = *pf2; // NON_COMPLIANT
pf1->pos = 0; // NON_COMPLIANT
memcpy(pf1, pf2, 1); // NON_COMPLIANT
memcmp(pf1, pf2, 1); // NON_COMPLIANT
}
void f1help(FILE *pf1, FILE *pf2) {
pf2 = pf1; // COMPLIANT
pf1->pos = 0; // NON_COMPLIANT
}
void f1() {
FILE *pf1;
FILE *pf2;
f1help(pf1, pf2);
}

Просмотреть файл

@ -0,0 +1,2 @@
| test.c:6:7:6:20 | ... != ... | The check is not reliable as the type of the return value of $@ is converted. | test.c:5:14:5:20 | call to getchar | call to getchar |
| test.c:13:7:13:15 | ... != ... | The check is not reliable as the type of the return value of $@ is converted. | test.c:12:14:12:20 | call to getchar | call to getchar |

Просмотреть файл

@ -0,0 +1 @@
rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql

Просмотреть файл

@ -0,0 +1,31 @@
#include <stdio.h>
void f1a(void) {
char ch;
ch = (char)getchar();
if (EOF != (int)ch) { // NON_COMPLIANT
}
}
void f1b(void) {
int ch;
ch = (char)getchar();
if (EOF != ch) { // NON_COMPLIANT
}
}
void f2(void) {
char ch;
ch = (char)getchar();
if (!feof(stdin)) { // COMPLIANT
}
}
void f3(void) {
int i_ch;
i_ch = getchar();
if (EOF != i_ch) { // COMPLIANT
char ch;
ch = (char)i_ch;
}
}

Просмотреть файл

@ -0,0 +1 @@
c/common/test/rules/nonuniqueincludeguardsused/NonUniqueIncludeGuardsUsed.ql

Просмотреть файл

@ -0,0 +1 @@
c/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql

Просмотреть файл

@ -0,0 +1,2 @@
- `M16-3-1` - `MoreThanOneOccurrenceHashOperatorInMacroDefinition.ql`:
- Removes detection of more than one occurrence in non function like Macros.

Просмотреть файл

@ -1,4 +1,4 @@
name: autosar-cpp-coding-standards
version: 2.3.0
version: 2.4.0
suites: codeql-suites
libraryPathDependencies: common-cpp-coding-standards

Просмотреть файл

@ -17,54 +17,10 @@
import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.rules.includeguardsnotused.IncludeGuardsNotUsed
pragma[noinline]
predicate isPreprocFileAndLine(Element pd, string filepath, int startLine) {
pd.getLocation().hasLocationInfo(filepath, startLine, _, _, _)
}
pragma[noinline]
predicate isPreprocConditionalRange(
PreprocessorBranch pb, string filepath, int startLine, int endLine
) {
exists(PreprocessorEndif end | pb.getEndIf() = end |
isPreprocFileAndLine(pb, filepath, startLine) and
isPreprocFileAndLine(end, filepath, endLine)
)
}
class GuardMacro extends Macro {
GuardMacro() {
//wrapper #ifndef present for use as include guard
//and they only suffice if there are no non comment elements above them
exists(PreprocessorIfndef wrapper |
getAGuard(this) = wrapper and
not exists(Element above, string filepath, int aboveStartLine, int ifdefStartLine |
aboveStartLine < ifdefStartLine and
isPreprocFileAndLine(wrapper, filepath, ifdefStartLine) and
isPreprocFileAndLine(above, filepath, aboveStartLine) and
not (above instanceof Comment or above instanceof File)
)
)
class PrecautionIncludeGuardsNotProvidedQuery extends IncludeGuardsNotUsedSharedQuery {
PrecautionIncludeGuardsNotProvidedQuery() {
this = IncludesPackage::includeGuardsNotProvidedQuery()
}
}
/**
* An optimised version of `PreprocessorBranchDirective.getAGuard()`.
*/
private PreprocessorBranch getAGuard(Element guardedElement) {
exists(string filepath, int ifStartLine, int guardedElementStartLine, int endifStartLine |
isPreprocConditionalRange(result, filepath, ifStartLine, endifStartLine) and
isPreprocFileAndLine(guardedElement, filepath, guardedElementStartLine) and
ifStartLine < guardedElementStartLine and
guardedElementStartLine < endifStartLine
)
}
from File file
where
not exists(GuardMacro guard | guard.getFile() = file) and
//headers are anything included
exists(Include i | i.getIncludedFile() = file) and
not isExcluded(file, IncludesPackage::includeGuardsNotProvidedQuery())
select file, "Header file $@ is missing expected include guard.", file, file.getBaseName()

Просмотреть файл

@ -15,16 +15,17 @@
import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.Macro
from Macro m, string body
from Macro m
where
body =
m.getBody()
.regexpReplaceAll("#+", "#")
.regexpReplaceAll("\\\\\"", "")
.regexpReplaceAll("\\\\'", "")
.regexpReplaceAll("\"[^\"]+\"", "")
.regexpReplaceAll("'[^']+'", "") and
exists(int c | c = count(int x | x = body.indexOf("#")) and c > 1) and
count(StringizingOperator op | op.getMacro() = m | op) > 1
or
count(any(TokenPastingOperator op | op.getMacro() = m | op.getOffset())) > 1
or
exists(StringizingOperator one, TokenPastingOperator two |
one.getMacro() = m and
two.getMacro() = m
) and
not isExcluded(m, MacrosPackage::moreThanOneOccurrenceHashOperatorInMacroDefinitionQuery())
select m, "Macro definition uses the # or ## operator more than once."

Просмотреть файл

@ -1,4 +1,4 @@
name: autosar-cpp-coding-standards-tests
version: 2.3.0
version: 2.4.0
libraryPathDependencies: autosar-cpp-coding-standards
extractor: cpp

Просмотреть файл

@ -1,3 +0,0 @@
| headers/test3.hpp:0:0:0:0 | headers/test3.hpp | Header file $@ is missing expected include guard. | headers/test3.hpp:0:0:0:0 | headers/test3.hpp | test3.hpp |
| headers/test4.hpp:0:0:0:0 | headers/test4.hpp | Header file $@ is missing expected include guard. | headers/test4.hpp:0:0:0:0 | headers/test4.hpp | test4.hpp |
| headers/test5.hpp:0:0:0:0 | headers/test5.hpp | Header file $@ is missing expected include guard. | headers/test5.hpp:0:0:0:0 | headers/test5.hpp | test5.hpp |

Просмотреть файл

@ -1 +0,0 @@
rules/M16-2-3/IncludeGuardsNotProvided.ql

Просмотреть файл

@ -0,0 +1 @@
cpp/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql

Просмотреть файл

@ -0,0 +1 @@
cpp/common/test/rules/nonuniqueincludeguardsused/NonUniqueIncludeGuardsUsed.ql

Просмотреть файл

@ -1,4 +1,3 @@
| test.cpp:21:1:21:29 | #define MACROELEVEN(X) X #X #X | Macro definition uses the # or ## operator more than once. |
| test.cpp:23:1:23:29 | #define MACROTWELVE(X) X ## X ## X | Macro definition uses the # or ## operator more than once. |
| test.cpp:25:1:25:29 | #define MACROTHIRTEEN(X) #X ## X | Macro definition uses the # or ## operator more than once. |
| test.cpp:27:1:27:45 | #define MACROFOURTEEN '#\\'-#' + 1 #1 #1 + '#' | Macro definition uses the # or ## operator more than once. |

Просмотреть файл

@ -24,4 +24,5 @@
#define MACROTHIRTEEN(X) #X##X // NON_COMPLIANT
#define MACROFOURTEEN '#\'-#' + 1 #1 #1 + '#' // NON_COMPLIANT
#define MACROFOURTEEN \
'#\'-#' + 1 #1 #1 + '#' // COMPLIANT, not a function-like macro.

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше