Remove CERT addon from open source Cppcheck. The plan is to provide complete CERT C checking in Cppcheck Premium during this year.
This commit is contained in:
parent
a0bab85bf9
commit
92316b07c8
|
@ -62,13 +62,6 @@ matrix:
|
||||||
- ${CPPCHECK} --dump misc-test.cpp
|
- ${CPPCHECK} --dump misc-test.cpp
|
||||||
- python3 ../misc.py -verify misc-test.cpp.dump
|
- python3 ../misc.py -verify misc-test.cpp.dump
|
||||||
- cd ../../
|
- cd ../../
|
||||||
# check addons/cert.py
|
|
||||||
- cd addons/test
|
|
||||||
- ${CPPCHECK} --dump cert-test.c
|
|
||||||
- python3 ../cert.py -verify cert-test.c.dump
|
|
||||||
- ${CPPCHECK} --dump cert-test.cpp
|
|
||||||
- python3 ../cert.py -verify cert-test.cpp.dump
|
|
||||||
- cd ../../
|
|
||||||
# check addons/misra.py
|
# check addons/misra.py
|
||||||
- cd addons/test
|
- cd addons/test
|
||||||
# We'll force C89 standard to enable an additional verification for
|
# We'll force C89 standard to enable an additional verification for
|
||||||
|
|
|
@ -4,8 +4,6 @@ Addons are scripts that analyses Cppcheck dump files to check compatibility with
|
||||||
|
|
||||||
## Supported addons
|
## Supported addons
|
||||||
|
|
||||||
+ [cert.py](https://github.com/danmar/cppcheck/blob/main/addons/cert.py)
|
|
||||||
Checks for compliance with the safe programming standard [CERT](http://www.cert.org/secure-coding/).
|
|
||||||
+ [misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py)
|
+ [misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py)
|
||||||
Used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/main/addons/test/misra/).
|
Used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/main/addons/test/misra/).
|
||||||
+ [y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py)
|
+ [y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py)
|
||||||
|
@ -18,13 +16,13 @@ Addons are scripts that analyses Cppcheck dump files to check compatibility with
|
||||||
### Command line interface
|
### Command line interface
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cppcheck --addon=cert --addon=y2038 src/test.c
|
cppcheck --addon=misc src/test.c
|
||||||
```
|
```
|
||||||
|
|
||||||
It is also possible to call scripts as follows:
|
It is also possible to call scripts as follows:
|
||||||
```bash
|
```bash
|
||||||
cppcheck --dump --quiet src/test.c
|
cppcheck --dump --quiet src/test.c
|
||||||
python cert.py src/test.c.dump
|
python misc.py src/test.c.dump
|
||||||
python misra.py --rule-texts=~/misra_rules.txt src/test.c.dump
|
python misra.py --rule-texts=~/misra_rules.txt src/test.c.dump
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
475
addons/cert.py
475
addons/cert.py
|
@ -1,475 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Cert: Some extra CERT checkers
|
|
||||||
#
|
|
||||||
# Cppcheck itself handles many CERT rules. Cppcheck warns when there is undefined behaviour.
|
|
||||||
# CERT Homepage: https://www.cert.org/secure-coding/
|
|
||||||
#
|
|
||||||
# Example usage of this addon (scan a sourcefile main.cpp)
|
|
||||||
# cppcheck --dump main.cpp
|
|
||||||
# python cert.py main.cpp.dump
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import cppcheckdata
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
VERIFY = ('-verify' in sys.argv)
|
|
||||||
VERIFY_EXPECTED = []
|
|
||||||
VERIFY_ACTUAL = []
|
|
||||||
|
|
||||||
def reportError(token, severity, msg, id):
|
|
||||||
if VERIFY:
|
|
||||||
VERIFY_ACTUAL.append(str(token.linenr) + ':cert-' + id)
|
|
||||||
else:
|
|
||||||
cppcheckdata.reportError(token, severity, msg, 'cert', id)
|
|
||||||
|
|
||||||
def simpleMatch(token, pattern):
|
|
||||||
return cppcheckdata.simpleMatch(token, pattern)
|
|
||||||
|
|
||||||
|
|
||||||
def isUnpackedStruct(token):
|
|
||||||
if token.valueType is None:
|
|
||||||
return False
|
|
||||||
if token.valueType.typeScope is None:
|
|
||||||
return False
|
|
||||||
if token.valueType.typeScope.type != "Struct":
|
|
||||||
return False
|
|
||||||
startToken = token.valueType.typeScope.bodyStart
|
|
||||||
|
|
||||||
linenr = int(startToken.linenr)
|
|
||||||
for line in open(startToken.file):
|
|
||||||
linenr -= 1
|
|
||||||
if linenr == 0:
|
|
||||||
return True
|
|
||||||
if linenr < 3 and re.match(r'#pragma\s+pack\s*\(', line):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def isLocalUnpackedStruct(arg):
|
|
||||||
if arg and arg.str == '&' and not arg.astOperand2:
|
|
||||||
arg = arg.astOperand1
|
|
||||||
return arg and arg.variable and (arg.variable.isLocal or arg.variable.isArgument) and isUnpackedStruct(arg)
|
|
||||||
|
|
||||||
|
|
||||||
def isBitwiseOp(token):
|
|
||||||
return token and (token.str in {'&', '|', '^'})
|
|
||||||
|
|
||||||
|
|
||||||
def isComparisonOp(token):
|
|
||||||
return token and (token.str in {'==', '!=', '>', '>=', '<', '<='})
|
|
||||||
|
|
||||||
def isCast(expr):
|
|
||||||
if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2:
|
|
||||||
return False
|
|
||||||
if simpleMatch(expr, '( )'):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def isStandardFunction(token):
|
|
||||||
if token.function:
|
|
||||||
return False
|
|
||||||
prev = token.previous
|
|
||||||
if prev:
|
|
||||||
if prev.str == '.':
|
|
||||||
return False
|
|
||||||
if prev.str == '::':
|
|
||||||
prevprev = prev.previous
|
|
||||||
if prevprev and not prevprev.str == 'std':
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Is this a function call
|
|
||||||
def isFunctionCall(token, function_names, number_of_arguments=None):
|
|
||||||
if not token.isName:
|
|
||||||
return False
|
|
||||||
if token.str not in function_names:
|
|
||||||
return False
|
|
||||||
if (token.next is None) or token.next.str != '(' or token.next != token.astParent:
|
|
||||||
return False
|
|
||||||
if number_of_arguments is None:
|
|
||||||
return True
|
|
||||||
return len(cppcheckdata.getArguments(token)) == number_of_arguments
|
|
||||||
|
|
||||||
|
|
||||||
# EXP05-C
|
|
||||||
# do not attempt to cast away const
|
|
||||||
def exp05(data):
|
|
||||||
# TODO Reuse code in misra rule 11.8
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if isCast(token):
|
|
||||||
# C-style cast
|
|
||||||
if not token.valueType:
|
|
||||||
continue
|
|
||||||
if not token.astOperand1.valueType:
|
|
||||||
continue
|
|
||||||
if token.valueType.pointer == 0:
|
|
||||||
continue
|
|
||||||
if token.astOperand1.valueType.pointer == 0:
|
|
||||||
continue
|
|
||||||
const1 = token.valueType.constness
|
|
||||||
const2 = token.astOperand1.valueType.constness
|
|
||||||
if (const1 % 2) < (const2 % 2):
|
|
||||||
reportError(token, 'style', "Attempt to cast away const", 'EXP05-C')
|
|
||||||
|
|
||||||
elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function:
|
|
||||||
function = token.astOperand1.function
|
|
||||||
arguments = cppcheckdata.getArguments(token.previous)
|
|
||||||
if not arguments:
|
|
||||||
continue
|
|
||||||
for argnr, argvar in function.argument.items():
|
|
||||||
if argnr < 1 or argnr > len(arguments):
|
|
||||||
continue
|
|
||||||
if not argvar.isPointer:
|
|
||||||
continue
|
|
||||||
if (argvar.constness % 2) == 1: # data is const
|
|
||||||
continue
|
|
||||||
argtok = arguments[argnr - 1]
|
|
||||||
if not argtok.valueType:
|
|
||||||
continue
|
|
||||||
if argtok.valueType.pointer == 0:
|
|
||||||
continue
|
|
||||||
const2 = arguments[argnr - 1].valueType.constness
|
|
||||||
if (const2 % 2) == 1:
|
|
||||||
reportError(token, 'style', "Attempt to cast away const", 'EXP05-C')
|
|
||||||
|
|
||||||
|
|
||||||
# EXP42-C
|
|
||||||
# do not compare padding data
|
|
||||||
def exp42(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if token.str != '(' or not token.astOperand1 or token.astOperand1.str != 'memcmp':
|
|
||||||
continue
|
|
||||||
|
|
||||||
arg1 = None
|
|
||||||
arg2 = None
|
|
||||||
if token.astOperand2 and token.astOperand2.str == ',':
|
|
||||||
if token.astOperand2.astOperand1 and token.astOperand2.astOperand1.str == ',':
|
|
||||||
arg1 = token.astOperand2.astOperand1.astOperand1
|
|
||||||
arg2 = token.astOperand2.astOperand1.astOperand2
|
|
||||||
|
|
||||||
if isLocalUnpackedStruct(arg1) or isLocalUnpackedStruct(arg2):
|
|
||||||
reportError(
|
|
||||||
token, 'style', "Comparison of struct padding data " +
|
|
||||||
"(fix either by packing the struct using '#pragma pack' or by rewriting the comparison)", 'EXP42-C')
|
|
||||||
|
|
||||||
# EXP15-C
|
|
||||||
# Do not place a semicolon on the same line as an if, for or while statement
|
|
||||||
def exp15(data):
|
|
||||||
for scope in data.scopes:
|
|
||||||
if scope.type in ('If', 'For', 'While'):
|
|
||||||
token = scope.bodyStart.next
|
|
||||||
if token.str==';' and token.linenr==scope.bodyStart.linenr:
|
|
||||||
reportError(token, 'style', 'Do not place a semicolon on the same line as an IF, FOR or WHILE', 'EXP15-C')
|
|
||||||
|
|
||||||
|
|
||||||
# EXP46-C
|
|
||||||
# Do not use a bitwise operator with a Boolean-like operand
|
|
||||||
# int x = (a == b) & c;
|
|
||||||
def exp46(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if isBitwiseOp(token) and (isComparisonOp(token.astOperand1) or isComparisonOp(token.astOperand2)):
|
|
||||||
reportError(
|
|
||||||
token, 'style', 'Bitwise operator is used with a Boolean-like operand', 'EXP46-c')
|
|
||||||
|
|
||||||
# INT31-C
|
|
||||||
# Ensure that integer conversions do not result in lost or misinterpreted data
|
|
||||||
def int31(data, platform):
|
|
||||||
if not platform:
|
|
||||||
return
|
|
||||||
for token in data.tokenlist:
|
|
||||||
to_value_type = None
|
|
||||||
from_values = None
|
|
||||||
action = ''
|
|
||||||
if isCast(token):
|
|
||||||
to_value_type = token.valueType
|
|
||||||
from_values = token.astOperand1.values
|
|
||||||
action = 'casting'
|
|
||||||
elif token.str == '=' and token.astOperand1 and token.astOperand2:
|
|
||||||
to_value_type = token.astOperand1.valueType
|
|
||||||
from_values = token.astOperand2.values
|
|
||||||
action = 'assign'
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if to_value_type is None or not from_values:
|
|
||||||
continue
|
|
||||||
bits = None
|
|
||||||
if token.valueType.pointer > 0:
|
|
||||||
bits = platform.pointer_bit
|
|
||||||
elif to_value_type.type == 'char':
|
|
||||||
bits = platform.char_bit
|
|
||||||
elif to_value_type.type == 'short':
|
|
||||||
bits = platform.short_bit
|
|
||||||
elif to_value_type.type == 'int':
|
|
||||||
bits = platform.int_bit
|
|
||||||
elif to_value_type.type == 'long':
|
|
||||||
bits = platform.long_bit
|
|
||||||
elif to_value_type.type == 'long long':
|
|
||||||
bits = platform.long_long_bit
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if to_value_type.sign == 'unsigned':
|
|
||||||
found = False
|
|
||||||
for value in from_values:
|
|
||||||
if value.intvalue and value.intvalue < 0:
|
|
||||||
found = True
|
|
||||||
reportError(
|
|
||||||
token,
|
|
||||||
'style',
|
|
||||||
'Ensure that integer conversions do not result in lost or misinterpreted data (' + action + ' ' + str(value.intvalue) + ' to unsigned ' + token.valueType.type + ')',
|
|
||||||
'INT31-c')
|
|
||||||
break
|
|
||||||
if found:
|
|
||||||
continue
|
|
||||||
if bits >= 64:
|
|
||||||
continue
|
|
||||||
minval = 0
|
|
||||||
maxval = 1
|
|
||||||
if token.valueType.sign == 'signed':
|
|
||||||
minval = -(1 << (bits - 1))
|
|
||||||
maxval = ((1 << (bits - 1)) - 1)
|
|
||||||
else:
|
|
||||||
minval = 0
|
|
||||||
maxval = ((1 << bits) - 1)
|
|
||||||
for value in from_values:
|
|
||||||
if value.intvalue and (value.intvalue < minval or value.intvalue > maxval):
|
|
||||||
destType = ''
|
|
||||||
if token.valueType.sign:
|
|
||||||
destType = token.valueType.sign + ' ' + token.valueType.type
|
|
||||||
else:
|
|
||||||
destType = token.valueType.type
|
|
||||||
reportError(
|
|
||||||
token,
|
|
||||||
'style',
|
|
||||||
'Ensure that integer conversions do not result in lost or misinterpreted data (' + action + ' ' + str(value.intvalue) + ' to ' + destType + ')',
|
|
||||||
'INT31-c')
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
# ENV33-C
|
|
||||||
# Do not call system()
|
|
||||||
def env33(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if isFunctionCall(token, ('system',), 1):
|
|
||||||
|
|
||||||
# Invalid syntax
|
|
||||||
if not token.next.astOperand2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# ENV33-C-EX1: It is permissible to call system() with a null
|
|
||||||
# pointer argument to determine the presence of a command processor
|
|
||||||
# for the system.
|
|
||||||
argValue = token.next.astOperand2.getValue(0)
|
|
||||||
if argValue and argValue.intvalue == 0 and argValue.isKnown():
|
|
||||||
continue
|
|
||||||
|
|
||||||
reportError(token, 'style', 'Do not call system()', 'ENV33-C')
|
|
||||||
|
|
||||||
|
|
||||||
# MSC24-C
|
|
||||||
# Do not use deprecated or obsolescent functions
|
|
||||||
def msc24(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if isFunctionCall(token, ('asctime',), 1):
|
|
||||||
reportError(token,'style','Do not use asctime() better use asctime_s()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('atof',), 1):
|
|
||||||
reportError(token,'style','Do not use atof() better use strtod()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('atoi',), 1):
|
|
||||||
reportError(token,'style','Do not use atoi() better use strtol()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('atol',), 1):
|
|
||||||
reportError(token,'style','Do not use atol() better use strtol()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('atoll',), 1):
|
|
||||||
reportError(token,'style','Do not use atoll() better use strtoll()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('ctime',), 1):
|
|
||||||
reportError(token,'style','Do not use ctime() better use ctime_s()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('fopen',), 2):
|
|
||||||
reportError(token,'style','Do not use fopen() better use fopen_s()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('freopen',), 3):
|
|
||||||
reportError(token,'style','Do not use freopen() better use freopen_s()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('rewind',), 1):
|
|
||||||
reportError(token,'style','Do not use rewind() better use fseek()', 'MSC24-C')
|
|
||||||
elif isFunctionCall(token, ('setbuf',), 2):
|
|
||||||
reportError(token,'style','Do not use setbuf() better use setvbuf()', 'MSC24-C')
|
|
||||||
|
|
||||||
# MSC30-C
|
|
||||||
# Do not use the rand() function for generating pseudorandom numbers
|
|
||||||
def msc30(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if simpleMatch(token, "rand ( )") and isStandardFunction(token):
|
|
||||||
reportError(token, 'style', 'Do not use the rand() function for generating pseudorandom numbers', 'MSC30-c')
|
|
||||||
|
|
||||||
# STR03-C
|
|
||||||
# Do not inadvertently truncate a string
|
|
||||||
def str03(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if not isFunctionCall(token, 'strncpy'):
|
|
||||||
continue
|
|
||||||
arguments = cppcheckdata.getArguments(token)
|
|
||||||
if len(arguments)!=3:
|
|
||||||
continue
|
|
||||||
if arguments[2].str=='(' and arguments[2].astOperand1.str=='sizeof':
|
|
||||||
reportError(token, 'style', 'Do not inadvertently truncate a string', 'STR03-C')
|
|
||||||
|
|
||||||
# STR05-C
|
|
||||||
# Use pointers to const when referring to string literals
|
|
||||||
def str05(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if token.isString:
|
|
||||||
parent = token.astParent
|
|
||||||
if parent is None:
|
|
||||||
continue
|
|
||||||
parentOp1 = parent.astOperand1
|
|
||||||
if parent.isAssignmentOp and parentOp1.valueType:
|
|
||||||
if (parentOp1.valueType.type in ('char', 'wchar_t')) and parentOp1.valueType.pointer and not parentOp1.valueType.constness:
|
|
||||||
reportError(parentOp1, 'style', 'Use pointers to const when referring to string literals', 'STR05-C')
|
|
||||||
|
|
||||||
# STR07-C
|
|
||||||
# Use the bounds-checking interfaces for string manipulation
|
|
||||||
def str07(data):
|
|
||||||
if data.standards.c=='c89' or data.standards.c=='c99':
|
|
||||||
return
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if not isFunctionCall(token, ('strcpy', 'strcat')):
|
|
||||||
continue
|
|
||||||
args = cppcheckdata.getArguments(token)
|
|
||||||
if len(args)!=2:
|
|
||||||
continue
|
|
||||||
if args[1].isString:
|
|
||||||
continue
|
|
||||||
reportError(token, 'style', 'Use the bounds-checking interfaces %s_s()' % token.str, 'STR07-C')
|
|
||||||
|
|
||||||
# STR11-C
|
|
||||||
# Do not specify the bound of a character array initialized with a string literal
|
|
||||||
def str11(data):
|
|
||||||
for token in data.tokenlist:
|
|
||||||
if not token.isString:
|
|
||||||
continue
|
|
||||||
|
|
||||||
strlen = token.strlen
|
|
||||||
parent = token.astParent
|
|
||||||
|
|
||||||
if parent is None:
|
|
||||||
continue
|
|
||||||
parentOp1 = parent.astOperand1
|
|
||||||
if parentOp1 is None or parentOp1.str!='[':
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parent.isAssignmentOp:
|
|
||||||
continue
|
|
||||||
|
|
||||||
varToken = parentOp1.astOperand1
|
|
||||||
if varToken is None or not varToken.isName:
|
|
||||||
continue
|
|
||||||
if varToken.variable is None:
|
|
||||||
continue
|
|
||||||
if varToken != varToken.variable.nameToken:
|
|
||||||
continue
|
|
||||||
valueToken = parentOp1.astOperand2
|
|
||||||
if valueToken is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if valueToken.isNumber and int(valueToken.str)==strlen:
|
|
||||||
reportError(valueToken, 'style', 'Do not specify the bound of a character array initialized with a string literal', 'STR11-C')
|
|
||||||
|
|
||||||
# API01-C
|
|
||||||
# Avoid laying out strings in memory directly before sensitive data
|
|
||||||
def api01(data):
|
|
||||||
for scope in data.scopes:
|
|
||||||
if scope.type!='Struct':
|
|
||||||
continue
|
|
||||||
token = scope.bodyStart
|
|
||||||
string_found = False
|
|
||||||
# loop through the complete struct
|
|
||||||
while token != scope.bodyEnd:
|
|
||||||
if token.isName and token.variable:
|
|
||||||
is_string = False
|
|
||||||
if token.variable.isArray:
|
|
||||||
type_token = token.variable.typeStartToken
|
|
||||||
while type_token and type_token.isName:
|
|
||||||
if type_token.str in ('char', 'wchar_t') and not type_token.isExpandedMacro:
|
|
||||||
is_string = True
|
|
||||||
type_token = type_token.next
|
|
||||||
if is_string:
|
|
||||||
string_found = True
|
|
||||||
elif string_found and not token.variable.isConst:
|
|
||||||
reportError(token, 'style', 'Avoid laying out strings in memory directly before sensitive data', 'API01-C')
|
|
||||||
# reset flags to report other positions in the same struct
|
|
||||||
string_found = False
|
|
||||||
token = token.next
|
|
||||||
|
|
||||||
|
|
||||||
def get_args_parser():
|
|
||||||
parser = cppcheckdata.ArgumentParser()
|
|
||||||
parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true")
|
|
||||||
return parser
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = get_args_parser()
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
path_premium_addon = cppcheckdata.get_path_premium_addon()
|
|
||||||
|
|
||||||
if args.verify:
|
|
||||||
VERIFY = True
|
|
||||||
|
|
||||||
if not args.dumpfile:
|
|
||||||
if not args.quiet:
|
|
||||||
print("no input files.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
for dumpfile in args.dumpfile:
|
|
||||||
if not args.quiet:
|
|
||||||
print('Checking %s...' % dumpfile)
|
|
||||||
|
|
||||||
if path_premium_addon:
|
|
||||||
premium_command = [path_premium_addon, '--cert', dumpfile]
|
|
||||||
if args.cli:
|
|
||||||
premium_command.append('--cli')
|
|
||||||
for line in subprocess.check_output(premium_command).decode('ascii').split('\n'):
|
|
||||||
if line.find('cert-') > 0:
|
|
||||||
print(line.strip())
|
|
||||||
|
|
||||||
data = cppcheckdata.CppcheckData(dumpfile)
|
|
||||||
|
|
||||||
if VERIFY:
|
|
||||||
VERIFY_ACTUAL = []
|
|
||||||
VERIFY_EXPECTED = []
|
|
||||||
for tok in data.rawTokens:
|
|
||||||
if tok.str.startswith('//') and 'TODO' not in tok.str:
|
|
||||||
for word in tok.str[2:].split(' '):
|
|
||||||
if re.match(r'cert-[A-Z][A-Z][A-Z][0-9][0-9].*',word):
|
|
||||||
VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word)
|
|
||||||
|
|
||||||
for cfg in data.iterconfigurations():
|
|
||||||
if not args.quiet:
|
|
||||||
print('Checking %s, config %s...' % (dumpfile, cfg.name))
|
|
||||||
exp05(cfg)
|
|
||||||
exp42(cfg)
|
|
||||||
exp46(cfg)
|
|
||||||
exp15(cfg)
|
|
||||||
int31(cfg, data.platform)
|
|
||||||
str03(cfg)
|
|
||||||
str05(cfg)
|
|
||||||
str07(cfg)
|
|
||||||
str11(cfg)
|
|
||||||
env33(cfg)
|
|
||||||
msc24(cfg)
|
|
||||||
msc30(cfg)
|
|
||||||
api01(cfg)
|
|
||||||
|
|
||||||
if VERIFY:
|
|
||||||
fail = False
|
|
||||||
for expected in VERIFY_EXPECTED:
|
|
||||||
if expected not in VERIFY_ACTUAL:
|
|
||||||
print('Expected but not seen: ' + expected)
|
|
||||||
fail = True
|
|
||||||
for actual in VERIFY_ACTUAL:
|
|
||||||
if actual not in VERIFY_EXPECTED:
|
|
||||||
print('Not expected: ' + actual)
|
|
||||||
fail = True
|
|
||||||
if fail:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
sys.exit(cppcheckdata.EXIT_CODE)
|
|
|
@ -1,207 +0,0 @@
|
||||||
// To test:
|
|
||||||
// ~/cppcheck/cppcheck --dump cert-test.c && python ../cert.py -verify cert-test.c.dump
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
struct S {
|
|
||||||
short a;
|
|
||||||
short b;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack()
|
|
||||||
struct PackedStruct {
|
|
||||||
short a;
|
|
||||||
short b;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct api01_bad_node_s
|
|
||||||
{
|
|
||||||
char name[10];
|
|
||||||
struct api01_bad_node_s* next; // cert-API01-C
|
|
||||||
};
|
|
||||||
struct api01_good_node_s
|
|
||||||
{
|
|
||||||
struct api01_good_node_s* next;
|
|
||||||
char name[String_Size];
|
|
||||||
};
|
|
||||||
struct api01_also_good_node_s
|
|
||||||
{
|
|
||||||
struct api01_also_good_node_s* next;
|
|
||||||
char *name;
|
|
||||||
};
|
|
||||||
struct api01_no_string_1 {
|
|
||||||
int data[10];
|
|
||||||
int x;
|
|
||||||
};
|
|
||||||
struct api01_no_string_2 {
|
|
||||||
int8_t data[10];
|
|
||||||
int x;
|
|
||||||
};
|
|
||||||
|
|
||||||
void dostuff(int *data);
|
|
||||||
|
|
||||||
void exp05()
|
|
||||||
{
|
|
||||||
const int x = 42;
|
|
||||||
int y = (int)x;
|
|
||||||
|
|
||||||
int *p;
|
|
||||||
p = (int *)&x; // cert-EXP05-C
|
|
||||||
|
|
||||||
const int data[] = {1,2,3,4};
|
|
||||||
dostuff(data); // cert-EXP05-C
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(const char *p);
|
|
||||||
void exp05_fp() {
|
|
||||||
print("hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
void exp42()
|
|
||||||
{
|
|
||||||
struct S s1 = {1,2};
|
|
||||||
struct S s2 = {1,2};
|
|
||||||
memcmp(&s1, &s2, sizeof(struct S)); // cert-EXP42-C
|
|
||||||
|
|
||||||
struct PackedStruct s3 = {1,2};
|
|
||||||
struct PackedStruct s4 = {1,2};
|
|
||||||
memcmp(&s3, &s4, sizeof(struct S));
|
|
||||||
}
|
|
||||||
|
|
||||||
void exp46(int x, int y, int z)
|
|
||||||
{
|
|
||||||
if ((x == y) & z) {} // cert-EXP46-c
|
|
||||||
}
|
|
||||||
|
|
||||||
void int31(int x)
|
|
||||||
{
|
|
||||||
x = (unsigned char)1000; // cert-INT31-c
|
|
||||||
x = (signed char)0xff; // cert-INT31-c
|
|
||||||
x = (unsigned char)-1; // cert-INT31-c
|
|
||||||
x = (unsigned long long)-1; // cert-INT31-c
|
|
||||||
unsigned char c;
|
|
||||||
c = 256; // cert-INT31-c
|
|
||||||
c = -1; // cert-INT31-c
|
|
||||||
|
|
||||||
// issue #10782
|
|
||||||
uint16_t * ptr;
|
|
||||||
uint32_t var = 65536;
|
|
||||||
ptr = (uint16_t *)var;
|
|
||||||
}
|
|
||||||
|
|
||||||
void env33()
|
|
||||||
{
|
|
||||||
system("chmod -x $(which chmod)"); // cert-ENV33-C
|
|
||||||
system(""); // cert-ENV33-C
|
|
||||||
system(NULL); // no-warning
|
|
||||||
system(0); // no-warning
|
|
||||||
const int *np = NULL;
|
|
||||||
system(np); // no-warning
|
|
||||||
int system;
|
|
||||||
}
|
|
||||||
|
|
||||||
void msc24()
|
|
||||||
{
|
|
||||||
struct S {
|
|
||||||
int x; int fopen;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct S s;
|
|
||||||
time_t rawtime;
|
|
||||||
struct tm *timeinfo;
|
|
||||||
char buffer[256];
|
|
||||||
int i;
|
|
||||||
long int li;
|
|
||||||
long long int lli;
|
|
||||||
FILE *f;
|
|
||||||
|
|
||||||
s.fopen = 123;
|
|
||||||
|
|
||||||
f = fopen ("myfile.txt","w+"); //cert-MSC24-C
|
|
||||||
setbuf ( f , buffer ); //cert-MSC24-C
|
|
||||||
for ( i='A' ; i<='Z' ; i++)
|
|
||||||
fputc ( i, f);
|
|
||||||
rewind (f); //cert-MSC24-C
|
|
||||||
fclose (f);
|
|
||||||
|
|
||||||
time ( &rawtime );
|
|
||||||
timeinfo = localtime ( &rawtime );
|
|
||||||
printf ( "The current date/time is: %s", asctime (timeinfo) ); //cert-MSC24-C
|
|
||||||
|
|
||||||
float n = atof (buffer); //cert-MSC24-C
|
|
||||||
float m = sin (n*M_PI/180);
|
|
||||||
|
|
||||||
i = atoi (buffer); //cert-MSC24-C
|
|
||||||
|
|
||||||
li = atol(buffer); //cert-MSC24-C
|
|
||||||
|
|
||||||
lli = atoll(buffer); //cert-MSC24-C
|
|
||||||
|
|
||||||
time (&rawtime);
|
|
||||||
printf ("The current local time is: %s", ctime (&rawtime)); //cert-MSC24-C
|
|
||||||
|
|
||||||
freopen ("myfile.txt","w",stdout); //cert-MSC24-C
|
|
||||||
printf ("This sentence is redirected to a file.");
|
|
||||||
fclose (stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void msc30()
|
|
||||||
{
|
|
||||||
unsigned int num = rand(); // cert-MSC30-c
|
|
||||||
int rand = 5;
|
|
||||||
int a = rand;
|
|
||||||
}
|
|
||||||
|
|
||||||
void exp15()
|
|
||||||
{
|
|
||||||
int x=5, y=7;
|
|
||||||
|
|
||||||
if(x==y); //cert-EXP15-C
|
|
||||||
{
|
|
||||||
printf("not working\n");
|
|
||||||
}
|
|
||||||
if(x)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void str03()
|
|
||||||
{
|
|
||||||
char *string_data=(char*)malloc(16);
|
|
||||||
char a[16];
|
|
||||||
int d;
|
|
||||||
strncpy(a, string_data, sizeof(a)); //cert-STR03-C
|
|
||||||
strncpy(a, string_data, 5); d=sizeof(int);
|
|
||||||
}
|
|
||||||
|
|
||||||
void str05()
|
|
||||||
{
|
|
||||||
int x=5, y=7;
|
|
||||||
|
|
||||||
if(x==y); //cert-EXP15-C
|
|
||||||
{
|
|
||||||
printf("not working\n");
|
|
||||||
}
|
|
||||||
if(x)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void str07(char *buf, const char *newBuf)
|
|
||||||
{
|
|
||||||
const char *str = "test";
|
|
||||||
strcat(buf,"bla");
|
|
||||||
strcat(buf, str); //cert-STR07-C
|
|
||||||
strcat(buf, newBuf); //cert-STR07-C
|
|
||||||
strcpy(buf, newBuf); //cert-STR07-C
|
|
||||||
}
|
|
||||||
|
|
||||||
void str11()
|
|
||||||
{
|
|
||||||
const char str[3]="abc"; //cert-STR11-C
|
|
||||||
const char *x[10]; x[3]="def";
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
// To test:
|
|
||||||
// ~/cppcheck/cppcheck --dump cert-test.cpp && python ../cert.py -verify cert-test.cpp.dump
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
class msc30TestClass {
|
|
||||||
public:
|
|
||||||
static int rand();
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace exp05c {
|
|
||||||
using uint32 = std::uint32_t;
|
|
||||||
static const uint32 a = static_cast<uint32>(0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void msc30(msc30TestClass & testClass)
|
|
||||||
{
|
|
||||||
unsigned int num = rand(); // cert-MSC30-c
|
|
||||||
num = std::rand(); // cert-MSC30-c
|
|
||||||
num = msc30TestClass::rand();
|
|
||||||
num = unknownClass::rand();
|
|
||||||
num = testClass.rand();
|
|
||||||
num = unknownClass.rand();
|
|
||||||
int rand = 5;
|
|
||||||
int a = rand;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
# Running the test with Python 2:
|
|
||||||
# Be sure to install pytest version 4.6.4 (newer should also work)
|
|
||||||
# Command in cppcheck directory:
|
|
||||||
# python -m pytest addons/test/test-cert.py
|
|
||||||
#
|
|
||||||
# Running the test with Python 3:
|
|
||||||
# Command in cppcheck directory:
|
|
||||||
# PYTHONPATH=./addons python3 -m pytest addons/test/test-cert.py
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_arguments_regression():
|
|
||||||
args_ok = ["-q", "--quiet",
|
|
||||||
"-verify",
|
|
||||||
"--cli"]
|
|
||||||
# Arguments with expected SystemExit
|
|
||||||
args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"]
|
|
||||||
|
|
||||||
from addons.cert import get_args_parser
|
|
||||||
|
|
||||||
for arg in args_exit:
|
|
||||||
sys.argv.append(arg)
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
parser = get_args_parser()
|
|
||||||
parser.parse_args()
|
|
||||||
sys.argv.remove(arg)
|
|
||||||
|
|
||||||
for arg in args_ok:
|
|
||||||
sys.argv.append(arg)
|
|
||||||
try:
|
|
||||||
parser = get_args_parser()
|
|
||||||
parser.parse_args()
|
|
||||||
except SystemExit:
|
|
||||||
pytest.fail("Unexpected SystemExit with '%s'" % arg)
|
|
||||||
sys.argv.remove(arg)
|
|
|
@ -359,7 +359,7 @@ void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QS
|
||||||
errorItem.severity = Severity::SeverityType::performance;
|
errorItem.severity = Severity::SeverityType::performance;
|
||||||
else if (id1.startsWith("portability"))
|
else if (id1.startsWith("portability"))
|
||||||
errorItem.severity = Severity::SeverityType::portability;
|
errorItem.severity = Severity::SeverityType::portability;
|
||||||
else if (id1.startsWith("cert") || (id1.startsWith("misc") && !id1.contains("unused")))
|
else if (id1.startsWith("misc") && !id1.contains("unused"))
|
||||||
errorItem.severity = Severity::SeverityType::warning;
|
errorItem.severity = Severity::SeverityType::warning;
|
||||||
else
|
else
|
||||||
errorItem.severity = Severity::SeverityType::style;
|
errorItem.severity = Severity::SeverityType::style;
|
||||||
|
|
|
@ -797,13 +797,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="mAddonCert">
|
|
||||||
<property name="text">
|
|
||||||
<string>CERT</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="mAddonMisra">
|
<widget class="QCheckBox" name="mAddonMisra">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
|
@ -335,7 +335,6 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
||||||
const QString dataDir = getDataDir();
|
const QString dataDir = getDataDir();
|
||||||
updateAddonCheckBox(mUI->mAddonThreadSafety, projectFile, dataDir, "threadsafety");
|
updateAddonCheckBox(mUI->mAddonThreadSafety, projectFile, dataDir, "threadsafety");
|
||||||
updateAddonCheckBox(mUI->mAddonY2038, projectFile, dataDir, "y2038");
|
updateAddonCheckBox(mUI->mAddonY2038, projectFile, dataDir, "y2038");
|
||||||
updateAddonCheckBox(mUI->mAddonCert, projectFile, dataDir, "cert");
|
|
||||||
updateAddonCheckBox(mUI->mAddonMisra, projectFile, dataDir, "misra");
|
updateAddonCheckBox(mUI->mAddonMisra, projectFile, dataDir, "misra");
|
||||||
|
|
||||||
const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString();
|
const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString();
|
||||||
|
@ -410,8 +409,6 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
||||||
list << "threadsafety";
|
list << "threadsafety";
|
||||||
if (mUI->mAddonY2038->isChecked())
|
if (mUI->mAddonY2038->isChecked())
|
||||||
list << "y2038";
|
list << "y2038";
|
||||||
if (mUI->mAddonCert->isChecked())
|
|
||||||
list << "cert";
|
|
||||||
if (mUI->mAddonMisra->isChecked())
|
if (mUI->mAddonMisra->isChecked())
|
||||||
list << "misra";
|
list << "misra";
|
||||||
projectFile->setAddons(list);
|
projectFile->setAddons(list);
|
||||||
|
|
Loading…
Reference in New Issue