2015-08-18 16:14:53 +02:00
|
|
|
#/usr/bin/python
|
|
|
|
#
|
|
|
|
# Cert: Some extra CERT checkers
|
|
|
|
#
|
|
|
|
# Cppcheck itself handles many CERT rules. Cppcheck warns when there is undefined behaviour.
|
|
|
|
#
|
|
|
|
# Example usage of this addon (scan a sourcefile main.cpp)
|
|
|
|
# cppcheck --dump main.cpp
|
|
|
|
# python cert.py main.cpp.dump
|
|
|
|
|
|
|
|
import cppcheckdata
|
|
|
|
import sys
|
2015-08-28 18:07:12 +02:00
|
|
|
import re
|
2015-08-21 10:55:19 +02:00
|
|
|
|
2015-12-14 09:37:26 +01:00
|
|
|
|
2015-08-18 16:14:53 +02:00
|
|
|
def reportError(token, severity, msg):
|
2015-12-14 09:37:26 +01:00
|
|
|
sys.stderr.write(
|
|
|
|
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') cert.py: ' + msg + '\n')
|
|
|
|
|
2015-08-21 10:55:19 +02:00
|
|
|
|
2015-08-28 18:07:12 +02:00
|
|
|
def isUnpackedStruct(var):
|
|
|
|
decl = var.typeStartToken
|
|
|
|
while decl and decl.isName:
|
|
|
|
if decl.str == 'struct':
|
|
|
|
structScope = decl.next.typeScope
|
|
|
|
if structScope:
|
|
|
|
linenr = int(structScope.classStart.linenr)
|
|
|
|
for line in open(structScope.classStart.file):
|
|
|
|
linenr = linenr - 1
|
|
|
|
if linenr == 0:
|
|
|
|
return True
|
2015-12-14 09:37:26 +01:00
|
|
|
if re.match(r'#pragma\s+pack\s*\(', line):
|
2015-08-28 18:07:12 +02:00
|
|
|
return False
|
|
|
|
break
|
|
|
|
decl = decl.next
|
|
|
|
return False
|
|
|
|
|
2015-12-14 09:37:26 +01:00
|
|
|
|
2015-08-28 18:07:12 +02:00
|
|
|
def isLocalUnpackedStruct(arg):
|
2015-08-19 10:21:25 +02:00
|
|
|
if arg and arg.str == '&' and not arg.astOperand2:
|
|
|
|
arg = arg.astOperand1
|
2015-08-28 18:07:12 +02:00
|
|
|
return arg and arg.variable and (arg.variable.isLocal or arg.variable.isArgument) and isUnpackedStruct(arg.variable)
|
2015-08-21 10:55:19 +02:00
|
|
|
|
2015-12-14 09:37:26 +01:00
|
|
|
|
2015-08-18 16:14:53 +02:00
|
|
|
def isBitwiseOp(token):
|
|
|
|
return token and (token.str in ['&', '|', '^'])
|
|
|
|
|
2015-08-21 10:55:19 +02:00
|
|
|
|
2015-08-18 16:14:53 +02:00
|
|
|
def isComparisonOp(token):
|
|
|
|
return token and (token.str in ['==', '!=', '>', '>=', '<', '<='])
|
|
|
|
|
2015-08-19 10:21:25 +02:00
|
|
|
|
|
|
|
# EXP42-C
|
|
|
|
# do not compare padding data
|
|
|
|
def exp42(data):
|
|
|
|
for token in data.tokenlist:
|
|
|
|
if token.str != '(' or not token.astOperand1:
|
|
|
|
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
|
|
|
|
|
2015-08-28 18:07:12 +02:00
|
|
|
if token.astOperand1.str == 'memcmp' and (isLocalUnpackedStruct(arg1) or isLocalUnpackedStruct(arg2)):
|
2015-12-14 09:37:26 +01:00
|
|
|
reportError(
|
|
|
|
token, 'style', 'EXP42-C Comparison of struct padding data (fix either by packing the struct using \'#pragma pack\' or by rewriting the comparison)')
|
2015-08-19 10:21:25 +02:00
|
|
|
|
2015-08-18 16:14:53 +02:00
|
|
|
# EXP46-C
|
|
|
|
# Do not use a bitwise operator with a Boolean-like operand
|
|
|
|
# int x = (a == b) & c;
|
2015-08-21 10:55:19 +02:00
|
|
|
|
|
|
|
|
2015-08-18 16:14:53 +02:00
|
|
|
def exp46(data):
|
|
|
|
for token in data.tokenlist:
|
|
|
|
if isBitwiseOp(token) and (isComparisonOp(token.astOperand1) or isComparisonOp(token.astOperand2)):
|
2015-12-14 09:37:26 +01:00
|
|
|
reportError(
|
|
|
|
token, 'style', 'EXP46-C Bitwise operator is used with a Boolean-like operand')
|
2015-08-18 16:14:53 +02:00
|
|
|
|
|
|
|
for arg in sys.argv[1:]:
|
2015-08-21 10:55:19 +02:00
|
|
|
print('Checking ' + arg + '...')
|
|
|
|
data = cppcheckdata.parsedump(arg)
|
2015-12-14 09:37:26 +01:00
|
|
|
for cfg in data.configurations:
|
|
|
|
if len(data.configurations) > 1:
|
|
|
|
print('Checking ' + arg + ', config "' + cfg.name + '"...')
|
|
|
|
exp42(cfg)
|
|
|
|
exp46(cfg)
|