cppcheck-py 2.9__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cppcheck/__init__.py +55 -0
- cppcheck/cppcheck-2.9.tar.gz +0 -0
- cppcheck/data/addons/__init__.py +0 -0
- cppcheck/data/addons/cppcheck.py +39 -0
- cppcheck/data/addons/cppcheckdata.py +1529 -0
- cppcheck/data/addons/findcasts.py +33 -0
- cppcheck/data/addons/misc.py +163 -0
- cppcheck/data/addons/misra.py +4728 -0
- cppcheck/data/addons/misra_9.py +513 -0
- cppcheck/data/addons/naming.py +91 -0
- cppcheck/data/addons/namingng.py +253 -0
- cppcheck/data/addons/runaddon.py +12 -0
- cppcheck/data/addons/threadsafety.py +39 -0
- cppcheck/data/addons/y2038.py +252 -0
- cppcheck/data/cfg/avr.cfg +431 -0
- cppcheck/data/cfg/bento4.cfg +142 -0
- cppcheck/data/cfg/boost.cfg +1268 -0
- cppcheck/data/cfg/bsd.cfg +546 -0
- cppcheck/data/cfg/cairo.cfg +88 -0
- cppcheck/data/cfg/cppcheck-lib.cfg +72 -0
- cppcheck/data/cfg/cppunit.cfg +48 -0
- cppcheck/data/cfg/daca.cfg +31 -0
- cppcheck/data/cfg/dpdk.cfg +11 -0
- cppcheck/data/cfg/embedded_sql.cfg +4 -0
- cppcheck/data/cfg/emscripten.cfg +7 -0
- cppcheck/data/cfg/ginac.cfg +12848 -0
- cppcheck/data/cfg/gnu.cfg +1626 -0
- cppcheck/data/cfg/googletest.cfg +60 -0
- cppcheck/data/cfg/gtk.cfg +20584 -0
- cppcheck/data/cfg/icu.cfg +6 -0
- cppcheck/data/cfg/kde.cfg +51 -0
- cppcheck/data/cfg/libcerror.cfg +128 -0
- cppcheck/data/cfg/libcurl.cfg +487 -0
- cppcheck/data/cfg/libsigc++.cfg +36 -0
- cppcheck/data/cfg/lua.cfg +316 -0
- cppcheck/data/cfg/mfc.cfg +256 -0
- cppcheck/data/cfg/microsoft_atl.cfg +40 -0
- cppcheck/data/cfg/microsoft_sal.cfg +491 -0
- cppcheck/data/cfg/microsoft_unittest.cfg +20 -0
- cppcheck/data/cfg/motif.cfg +318 -0
- cppcheck/data/cfg/nspr.cfg +38 -0
- cppcheck/data/cfg/ntl.cfg +6907 -0
- cppcheck/data/cfg/opencv2.cfg +132 -0
- cppcheck/data/cfg/opengl.cfg +580 -0
- cppcheck/data/cfg/openmp.cfg +157 -0
- cppcheck/data/cfg/openssl.cfg +174 -0
- cppcheck/data/cfg/pcre.cfg +165 -0
- cppcheck/data/cfg/posix.cfg +6252 -0
- cppcheck/data/cfg/python.cfg +674 -0
- cppcheck/data/cfg/qt.cfg +5255 -0
- cppcheck/data/cfg/ruby.cfg +142 -0
- cppcheck/data/cfg/sdl.cfg +354 -0
- cppcheck/data/cfg/sfml.cfg +301 -0
- cppcheck/data/cfg/sqlite3.cfg +1527 -0
- cppcheck/data/cfg/std.cfg +8716 -0
- cppcheck/data/cfg/tinyxml2.cfg +56 -0
- cppcheck/data/cfg/vcl.cfg +4 -0
- cppcheck/data/cfg/windows.cfg +17941 -0
- cppcheck/data/cfg/wxsqlite3.cfg +1955 -0
- cppcheck/data/cfg/wxsvg.cfg +16905 -0
- cppcheck/data/cfg/wxwidgets.cfg +16464 -0
- cppcheck/data/cfg/zlib.cfg +1450 -0
- cppcheck/data/cppcheck-htmlreport +986 -0
- cppcheck/data/cppcheck.exe +0 -0
- cppcheck/data/platforms/aix_ppc64.xml +17 -0
- cppcheck/data/platforms/arm32-wchar_t2.xml +17 -0
- cppcheck/data/platforms/arm32-wchar_t4.xml +17 -0
- cppcheck/data/platforms/arm64-wchar_t2.xml +17 -0
- cppcheck/data/platforms/arm64-wchar_t4.xml +17 -0
- cppcheck/data/platforms/avr8.xml +18 -0
- cppcheck/data/platforms/cray_sv1.xml +17 -0
- cppcheck/data/platforms/elbrus-e1cp.xml +18 -0
- cppcheck/data/platforms/mips32.xml +18 -0
- cppcheck/data/platforms/msp430_eabi_large_datamodel.xml +17 -0
- cppcheck/data/platforms/pic16.xml +18 -0
- cppcheck/data/platforms/pic8-enhanced.xml +18 -0
- cppcheck/data/platforms/pic8.xml +18 -0
- cppcheck/data/platforms/unix32-unsigned.xml +17 -0
- cppcheck/data/platforms/unix64-unsigned.xml +17 -0
- cppcheck_py-2.9.dist-info/METADATA +76 -0
- cppcheck_py-2.9.dist-info/RECORD +84 -0
- cppcheck_py-2.9.dist-info/WHEEL +5 -0
- cppcheck_py-2.9.dist-info/entry_points.txt +4 -0
- cppcheck_py-2.9.dist-info/licenses/LICENSE.md +191 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# cppcheck addon for naming conventions
|
|
4
|
+
# An enhanced version. Configuration is taken from a json file
|
|
5
|
+
# It supports to check for type-based prefixes in function or variable names.
|
|
6
|
+
#
|
|
7
|
+
# Example usage (variable name must start with lowercase, function name must start with uppercase):
|
|
8
|
+
# $ cppcheck --dump path-to-src/
|
|
9
|
+
# $ python namingng.py test.c.dump
|
|
10
|
+
#
|
|
11
|
+
# JSON format:
|
|
12
|
+
#
|
|
13
|
+
# {
|
|
14
|
+
# "RE_VARNAME": "[a-z]*[a-zA-Z0-9_]*\\Z",
|
|
15
|
+
# "RE_PRIVATE_MEMBER_VARIABLE": null,
|
|
16
|
+
# "RE_FUNCTIONNAME": "[a-z0-9A-Z]*\\Z",
|
|
17
|
+
# "var_prefixes": {"uint32_t": "ui32"},
|
|
18
|
+
# "function_prefixes": {"uint16_t": "ui16",
|
|
19
|
+
# "uint32_t": "ui32"}
|
|
20
|
+
# }
|
|
21
|
+
#
|
|
22
|
+
# RE_VARNAME, RE_PRIVATE_MEMBER_VARIABLE and RE_FUNCTIONNAME are regular expressions to cover the basic names
|
|
23
|
+
# In var_prefixes and function_prefixes there are the variable-type/prefix pairs
|
|
24
|
+
|
|
25
|
+
import cppcheckdata
|
|
26
|
+
import sys
|
|
27
|
+
import re
|
|
28
|
+
import argparse
|
|
29
|
+
import json
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Auxiliary class
|
|
33
|
+
class DataStruct:
|
|
34
|
+
def __init__(self, file, linenr, string):
|
|
35
|
+
self.file = file
|
|
36
|
+
self.linenr = linenr
|
|
37
|
+
self.str = string
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def reportError(filename, linenr, severity, msg):
|
|
41
|
+
message = "[{filename}:{linenr}] ( {severity} ) naming.py: {msg}\n".format(
|
|
42
|
+
filename=filename,
|
|
43
|
+
linenr=linenr,
|
|
44
|
+
severity=severity,
|
|
45
|
+
msg=msg
|
|
46
|
+
)
|
|
47
|
+
sys.stderr.write(message)
|
|
48
|
+
return message
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def loadConfig(configfile):
|
|
52
|
+
with open(configfile) as fh:
|
|
53
|
+
data = json.load(fh)
|
|
54
|
+
return data
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def checkTrueRegex(data, expr, msg, errors):
|
|
58
|
+
res = re.match(expr, data.str)
|
|
59
|
+
if res:
|
|
60
|
+
errors.append(reportError(data.file, data.linenr, 'style', msg))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def checkFalseRegex(data, expr, msg, errors):
|
|
64
|
+
res = re.match(expr, data.str)
|
|
65
|
+
if not res:
|
|
66
|
+
errors.append(reportError(data.file, data.linenr, 'style', msg))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def evalExpr(conf, exp, mockToken, msgType, errors):
|
|
70
|
+
if isinstance(conf, dict):
|
|
71
|
+
if conf[exp][0]:
|
|
72
|
+
msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1]
|
|
73
|
+
checkTrueRegex(mockToken, exp, msg, errors)
|
|
74
|
+
elif ~conf[exp][0]:
|
|
75
|
+
msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1]
|
|
76
|
+
checkFalseRegex(mockToken, exp, msg, errors)
|
|
77
|
+
else:
|
|
78
|
+
msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][0]
|
|
79
|
+
checkFalseRegex(mockToken, exp, msg, errors)
|
|
80
|
+
else:
|
|
81
|
+
msg = msgType + ' ' + mockToken.str + ' violates naming convention'
|
|
82
|
+
checkFalseRegex(mockToken, exp, msg, errors)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def process(dumpfiles, configfile, debugprint=False):
|
|
86
|
+
|
|
87
|
+
errors = []
|
|
88
|
+
|
|
89
|
+
conf = loadConfig(configfile)
|
|
90
|
+
|
|
91
|
+
for afile in dumpfiles:
|
|
92
|
+
if not afile[-5:] == '.dump':
|
|
93
|
+
continue
|
|
94
|
+
print('Checking ' + afile + '...')
|
|
95
|
+
data = cppcheckdata.CppcheckData(afile)
|
|
96
|
+
|
|
97
|
+
# Check File naming
|
|
98
|
+
if "RE_FILE" in conf and conf["RE_FILE"]:
|
|
99
|
+
mockToken = DataStruct(afile[:-5], "0", afile[afile.rfind('/') + 1:-5])
|
|
100
|
+
msgType = 'File name'
|
|
101
|
+
for exp in conf["RE_FILE"]:
|
|
102
|
+
evalExpr(conf["RE_FILE"], exp, mockToken, msgType, errors)
|
|
103
|
+
|
|
104
|
+
# Check Namespace naming
|
|
105
|
+
if "RE_NAMESPACE" in conf and conf["RE_NAMESPACE"]:
|
|
106
|
+
for tk in data.rawTokens:
|
|
107
|
+
if tk.str == 'namespace':
|
|
108
|
+
mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str)
|
|
109
|
+
msgType = 'Namespace'
|
|
110
|
+
for exp in conf["RE_NAMESPACE"]:
|
|
111
|
+
evalExpr(conf["RE_NAMESPACE"], exp, mockToken, msgType, errors)
|
|
112
|
+
|
|
113
|
+
for cfg in data.configurations:
|
|
114
|
+
print('Checking %s, config %s...' % (afile, cfg.name))
|
|
115
|
+
if "RE_VARNAME" in conf and conf["RE_VARNAME"]:
|
|
116
|
+
for var in cfg.variables:
|
|
117
|
+
if var.nameToken and var.access != 'Global' and var.access != 'Public' and var.access != 'Private':
|
|
118
|
+
prev = var.nameToken.previous
|
|
119
|
+
varType = prev.str
|
|
120
|
+
while "*" in varType and len(varType.replace("*", "")) == 0:
|
|
121
|
+
prev = prev.previous
|
|
122
|
+
varType = prev.str + varType
|
|
123
|
+
|
|
124
|
+
if debugprint:
|
|
125
|
+
print("Variable Name: " + str(var.nameToken.str))
|
|
126
|
+
print("original Type Name: " + str(var.nameToken.valueType.originalTypeName))
|
|
127
|
+
print("Type Name: " + var.nameToken.valueType.type)
|
|
128
|
+
print("Sign: " + str(var.nameToken.valueType.sign))
|
|
129
|
+
print("variable type: " + varType)
|
|
130
|
+
print("\n")
|
|
131
|
+
print("\t-- {} {}".format(varType, str(var.nameToken.str)))
|
|
132
|
+
|
|
133
|
+
if conf["skip_one_char_variables"] and len(var.nameToken.str) == 1:
|
|
134
|
+
continue
|
|
135
|
+
if varType in conf["var_prefixes"]:
|
|
136
|
+
if not var.nameToken.str.startswith(conf["var_prefixes"][varType]):
|
|
137
|
+
errors.append(reportError(
|
|
138
|
+
var.typeStartToken.file,
|
|
139
|
+
var.typeStartToken.linenr,
|
|
140
|
+
'style',
|
|
141
|
+
'Variable ' +
|
|
142
|
+
var.nameToken.str +
|
|
143
|
+
' violates naming convention'))
|
|
144
|
+
|
|
145
|
+
mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str)
|
|
146
|
+
msgType = 'Variable'
|
|
147
|
+
for exp in conf["RE_VARNAME"]:
|
|
148
|
+
evalExpr(conf["RE_VARNAME"], exp, mockToken, msgType, errors)
|
|
149
|
+
|
|
150
|
+
# Check Private Variable naming
|
|
151
|
+
if "RE_PRIVATE_MEMBER_VARIABLE" in conf and conf["RE_PRIVATE_MEMBER_VARIABLE"]:
|
|
152
|
+
# TODO: Not converted yet
|
|
153
|
+
for var in cfg.variables:
|
|
154
|
+
if (var.access is None) or var.access != 'Private':
|
|
155
|
+
continue
|
|
156
|
+
mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str)
|
|
157
|
+
msgType = 'Private member variable'
|
|
158
|
+
for exp in conf["RE_PRIVATE_MEMBER_VARIABLE"]:
|
|
159
|
+
evalExpr(conf["RE_PRIVATE_MEMBER_VARIABLE"], exp, mockToken, msgType, errors)
|
|
160
|
+
|
|
161
|
+
# Check Public Member Variable naming
|
|
162
|
+
if "RE_PUBLIC_MEMBER_VARIABLE" in conf and conf["RE_PUBLIC_MEMBER_VARIABLE"]:
|
|
163
|
+
for var in cfg.variables:
|
|
164
|
+
if (var.access is None) or var.access != 'Public':
|
|
165
|
+
continue
|
|
166
|
+
mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str)
|
|
167
|
+
msgType = 'Public member variable'
|
|
168
|
+
for exp in conf["RE_PUBLIC_MEMBER_VARIABLE"]:
|
|
169
|
+
evalExpr(conf["RE_PUBLIC_MEMBER_VARIABLE"], exp, mockToken, msgType, errors)
|
|
170
|
+
|
|
171
|
+
# Check Global Variable naming
|
|
172
|
+
if "RE_GLOBAL_VARNAME" in conf and conf["RE_GLOBAL_VARNAME"]:
|
|
173
|
+
for var in cfg.variables:
|
|
174
|
+
if (var.access is None) or var.access != 'Global':
|
|
175
|
+
continue
|
|
176
|
+
mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str)
|
|
177
|
+
msgType = 'Public member variable'
|
|
178
|
+
for exp in conf["RE_GLOBAL_VARNAME"]:
|
|
179
|
+
evalExpr(conf["RE_GLOBAL_VARNAME"], exp, mockToken, msgType, errors)
|
|
180
|
+
|
|
181
|
+
# Check Functions naming
|
|
182
|
+
if "RE_FUNCTIONNAME" in conf and conf["RE_FUNCTIONNAME"]:
|
|
183
|
+
for token in cfg.tokenlist:
|
|
184
|
+
if token.function:
|
|
185
|
+
if token.function.type in ('Constructor', 'Destructor', 'CopyConstructor', 'MoveConstructor'):
|
|
186
|
+
continue
|
|
187
|
+
retval = token.previous.str
|
|
188
|
+
prev = token.previous
|
|
189
|
+
while "*" in retval and len(retval.replace("*", "")) == 0:
|
|
190
|
+
prev = prev.previous
|
|
191
|
+
retval = prev.str + retval
|
|
192
|
+
if debugprint:
|
|
193
|
+
print("\t:: {} {}".format(retval, token.function.name))
|
|
194
|
+
|
|
195
|
+
if retval and retval in conf["function_prefixes"]:
|
|
196
|
+
if not token.function.name.startswith(conf["function_prefixes"][retval]):
|
|
197
|
+
errors.append(reportError(
|
|
198
|
+
token.file, token.linenr, 'style', 'Function ' + token.function.name + ' violates naming convention'))
|
|
199
|
+
mockToken = DataStruct(token.file, token.linenr, token.function.name)
|
|
200
|
+
msgType = 'Function'
|
|
201
|
+
for exp in conf["RE_FUNCTIONNAME"]:
|
|
202
|
+
evalExpr(conf["RE_FUNCTIONNAME"], exp, mockToken, msgType, errors)
|
|
203
|
+
|
|
204
|
+
# Check Class naming
|
|
205
|
+
if "RE_CLASS_NAME" in conf and conf["RE_CLASS_NAME"]:
|
|
206
|
+
for fnc in cfg.functions:
|
|
207
|
+
# Check if it is Constructor/Destructor
|
|
208
|
+
if fnc.type == 'Constructor' or fnc.type == 'Destructor':
|
|
209
|
+
mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name)
|
|
210
|
+
msgType = 'Class ' + fnc.type
|
|
211
|
+
for exp in conf["RE_CLASS_NAME"]:
|
|
212
|
+
evalExpr(conf["RE_CLASS_NAME"], exp, mockToken, msgType, errors)
|
|
213
|
+
return errors
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
parser = argparse.ArgumentParser(description='Naming verification')
|
|
218
|
+
parser.add_argument('dumpfiles', type=str, nargs='+',
|
|
219
|
+
help='A set of dumpfiles to process')
|
|
220
|
+
parser.add_argument("--debugprint", action="store_true", default=False,
|
|
221
|
+
help="Add debug prints")
|
|
222
|
+
parser.add_argument("--configfile", type=str, default="naming.json",
|
|
223
|
+
help="Naming check config file")
|
|
224
|
+
parser.add_argument("--verify", action="store_true", default=False,
|
|
225
|
+
help="verify this script. Must be executed in test folder !")
|
|
226
|
+
|
|
227
|
+
args = parser.parse_args()
|
|
228
|
+
errors = process(args.dumpfiles, args.configfile, args.debugprint)
|
|
229
|
+
|
|
230
|
+
if args.verify:
|
|
231
|
+
print(errors)
|
|
232
|
+
if len(errors) < 6:
|
|
233
|
+
print("Not enough errors found")
|
|
234
|
+
sys.exit(1)
|
|
235
|
+
target = [
|
|
236
|
+
'[namingng_test.c:8] ( style ) naming.py: Variable badui32 violates naming convention\n',
|
|
237
|
+
'[namingng_test.c:11] ( style ) naming.py: Variable a violates naming convention\n',
|
|
238
|
+
'[namingng_test.c:29] ( style ) naming.py: Variable badui32 violates naming convention\n',
|
|
239
|
+
'[namingng_test.c:20] ( style ) naming.py: Function ui16bad_underscore violates naming convention\n',
|
|
240
|
+
'[namingng_test.c:25] ( style ) naming.py: Function u32Bad violates naming convention\n',
|
|
241
|
+
'[namingng_test.c:37] ( style ) naming.py: Function Badui16 violates naming convention\n']
|
|
242
|
+
diff = set(errors) - set(target)
|
|
243
|
+
if len(diff):
|
|
244
|
+
print("Not the right errors found {}".format(str(diff)))
|
|
245
|
+
sys.exit(1)
|
|
246
|
+
print("Verification done\n")
|
|
247
|
+
sys.exit(0)
|
|
248
|
+
|
|
249
|
+
if len(errors):
|
|
250
|
+
print('Found errors: {}'.format(len(errors)))
|
|
251
|
+
sys.exit(1)
|
|
252
|
+
|
|
253
|
+
sys.exit(0)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import cppcheckdata, cppcheck, runpy, sys, os
|
|
2
|
+
|
|
3
|
+
if __name__ == '__main__':
|
|
4
|
+
addon = sys.argv[1]
|
|
5
|
+
__addon_name__ = os.path.splitext(os.path.basename(addon))[0]
|
|
6
|
+
sys.argv.pop(0)
|
|
7
|
+
|
|
8
|
+
runpy.run_path(addon, run_name='__main__')
|
|
9
|
+
|
|
10
|
+
# Run registered checkers
|
|
11
|
+
cppcheck.runcheckers()
|
|
12
|
+
sys.exit(cppcheckdata.EXIT_CODE)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# This script analyses Cppcheck dump files to locate threadsafety issues
|
|
4
|
+
# - warn about static local objects
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
import cppcheckdata
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
def reportError(token, severity, msg, id):
|
|
11
|
+
cppcheckdata.reportError(token, severity, msg, 'threadsafety', id)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def checkstatic(data):
|
|
15
|
+
for var in data.variables:
|
|
16
|
+
if var.isStatic and var.isLocal:
|
|
17
|
+
type = None
|
|
18
|
+
if var.isClass:
|
|
19
|
+
type = 'object'
|
|
20
|
+
else:
|
|
21
|
+
type = 'variable'
|
|
22
|
+
if var.isConst:
|
|
23
|
+
if data.standards.cpp == 'c++03':
|
|
24
|
+
reportError(var.typeStartToken, 'warning', 'Local constant static ' + type + ' \'' + var.nameToken.str + '\', dangerous if it is initialized in parallel threads', 'threadsafety-const')
|
|
25
|
+
else:
|
|
26
|
+
reportError(var.typeStartToken, 'warning', 'Local static ' + type + ': ' + var.nameToken.str, 'threadsafety')
|
|
27
|
+
|
|
28
|
+
for arg in sys.argv[1:]:
|
|
29
|
+
if arg.startswith('-'):
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
print('Checking %s...' % arg)
|
|
33
|
+
data = cppcheckdata.CppcheckData(arg)
|
|
34
|
+
|
|
35
|
+
for cfg in data.iterconfigurations():
|
|
36
|
+
print('Checking %s, config %s...' % (arg, cfg.name))
|
|
37
|
+
checkstatic(cfg)
|
|
38
|
+
|
|
39
|
+
sys.exit(cppcheckdata.EXIT_CODE)
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# cppcheck addon for Y2038 safeness detection
|
|
4
|
+
#
|
|
5
|
+
# Detects:
|
|
6
|
+
#
|
|
7
|
+
# 1. _TIME_BITS being defined to something else than 64 bits
|
|
8
|
+
# 2. _USE_TIME_BITS64 being defined when _TIME_BITS is not
|
|
9
|
+
# 3. Any Y2038-unsafe symbol when _USE_TIME_BITS64 is not defined.
|
|
10
|
+
#
|
|
11
|
+
# Example usage:
|
|
12
|
+
# $ cppcheck --dump path-to-src/test.c
|
|
13
|
+
# $ y2038.py path-to-src/test.c.dump
|
|
14
|
+
#
|
|
15
|
+
# y2038.py will walk the source tree for .dump files.
|
|
16
|
+
from __future__ import print_function
|
|
17
|
+
|
|
18
|
+
import cppcheckdata
|
|
19
|
+
import sys
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# --------------------------------------------
|
|
25
|
+
# #define/#undef detection regular expressions
|
|
26
|
+
# --------------------------------------------
|
|
27
|
+
|
|
28
|
+
# test for '#define _TIME_BITS 64'
|
|
29
|
+
re_define_time_bits_64 = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+64\s*$')
|
|
30
|
+
|
|
31
|
+
# test for '#define _TIME_BITS ...' (combine w/ above to test for 'not 64')
|
|
32
|
+
re_define_time_bits = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+.*$')
|
|
33
|
+
|
|
34
|
+
# test for '#undef _TIME_BITS' (if it ever happens)
|
|
35
|
+
re_undef_time_bits = re.compile(r'^\s*#\s*undef\s+_TIME_BITS\s*$')
|
|
36
|
+
|
|
37
|
+
# test for '#define _USE_TIME_BITS64'
|
|
38
|
+
re_define_use_time_bits64 = re.compile(r'^\s*#\s*define\s+_USE_TIME_BITS64\s*$')
|
|
39
|
+
|
|
40
|
+
# test for '#undef _USE_TIME_BITS64' (if it ever happens)
|
|
41
|
+
re_undef_use_time_bits64 = re.compile(r'^\s*#\s*undef\s+_USE_TIME_BITS64\s*$')
|
|
42
|
+
|
|
43
|
+
# --------------------------------
|
|
44
|
+
# List of Y2038-unsafe identifiers
|
|
45
|
+
# --------------------------------
|
|
46
|
+
|
|
47
|
+
# This is WIP. Eventually it should contain all identifiers (types
|
|
48
|
+
# and functions) which would be affected by the Y2038 bug.
|
|
49
|
+
|
|
50
|
+
id_Y2038 = {
|
|
51
|
+
# Y2038-unsafe types by definition
|
|
52
|
+
'time_t'
|
|
53
|
+
# Types using Y2038-unsafe types
|
|
54
|
+
'lastlog',
|
|
55
|
+
'msqid_ds',
|
|
56
|
+
'semid_ds',
|
|
57
|
+
'timeb',
|
|
58
|
+
'timespec',
|
|
59
|
+
'timeval',
|
|
60
|
+
'utimbuf',
|
|
61
|
+
'itimerspec',
|
|
62
|
+
'stat',
|
|
63
|
+
'clnt_ops',
|
|
64
|
+
'elf_prstatus',
|
|
65
|
+
'itimerval',
|
|
66
|
+
'ntptimeval',
|
|
67
|
+
'rusage',
|
|
68
|
+
'timex',
|
|
69
|
+
'utmp',
|
|
70
|
+
'utmpx',
|
|
71
|
+
# APIs using 2038-unsafe types
|
|
72
|
+
'ctime',
|
|
73
|
+
'ctime_r',
|
|
74
|
+
'difftime',
|
|
75
|
+
'gmtime',
|
|
76
|
+
'gmtime_r',
|
|
77
|
+
'localtime',
|
|
78
|
+
'localtime_r',
|
|
79
|
+
'mktime',
|
|
80
|
+
'stime',
|
|
81
|
+
'timegm',
|
|
82
|
+
'timelocal',
|
|
83
|
+
'time',
|
|
84
|
+
'msgctl',
|
|
85
|
+
'ftime',
|
|
86
|
+
'aio_suspend',
|
|
87
|
+
'clock_getres',
|
|
88
|
+
'clock_gettime',
|
|
89
|
+
'clock_nanosleep',
|
|
90
|
+
'clock_settime',
|
|
91
|
+
'futimens',
|
|
92
|
+
'mq_timedreceive',
|
|
93
|
+
'mq_timedsend',
|
|
94
|
+
'nanosleep',
|
|
95
|
+
'pselect',
|
|
96
|
+
'pthread_cond_timedwait',
|
|
97
|
+
'pthread_mutex_timedlock',
|
|
98
|
+
'pthread_rwlock_timedrdlock',
|
|
99
|
+
'pthread_rwlock_timedwrlock',
|
|
100
|
+
'sched_rr_get_interval',
|
|
101
|
+
'sem_timedwait',
|
|
102
|
+
'sigtimedwait',
|
|
103
|
+
'timespec_get',
|
|
104
|
+
'utimensat',
|
|
105
|
+
'adjtime',
|
|
106
|
+
'pmap_rmtcall',
|
|
107
|
+
'clntudp_bufcreate',
|
|
108
|
+
'clntudp_create',
|
|
109
|
+
'futimes',
|
|
110
|
+
'gettimeofday',
|
|
111
|
+
'lutimes',
|
|
112
|
+
'select',
|
|
113
|
+
'settimeofday',
|
|
114
|
+
'utimes',
|
|
115
|
+
'utime',
|
|
116
|
+
'timerfd_gettime',
|
|
117
|
+
'timerfd_settime',
|
|
118
|
+
'timer_gettime',
|
|
119
|
+
'timer_settime',
|
|
120
|
+
'fstatat',
|
|
121
|
+
'fstat',
|
|
122
|
+
'__fxstatat',
|
|
123
|
+
'__fxstat',
|
|
124
|
+
'lstat',
|
|
125
|
+
'__lxstat',
|
|
126
|
+
'stat',
|
|
127
|
+
'__xstat',
|
|
128
|
+
'struct itimerval',
|
|
129
|
+
'setitimer',
|
|
130
|
+
'getitimer',
|
|
131
|
+
'ntp_gettime',
|
|
132
|
+
'getrusage',
|
|
133
|
+
'wait3',
|
|
134
|
+
'wait4',
|
|
135
|
+
'adjtimex',
|
|
136
|
+
'ntp_adjtime',
|
|
137
|
+
'getutent_r',
|
|
138
|
+
'getutent',
|
|
139
|
+
'getutid_r',
|
|
140
|
+
'getutid',
|
|
141
|
+
'getutline_r',
|
|
142
|
+
'getutline',
|
|
143
|
+
'login',
|
|
144
|
+
'pututline',
|
|
145
|
+
'updwtmp',
|
|
146
|
+
'getutxent',
|
|
147
|
+
'getutxid',
|
|
148
|
+
'getutxline',
|
|
149
|
+
'pututxline'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def check_y2038_safe(dumpfile, quiet=False):
|
|
154
|
+
# Assume that the code is Y2038 safe until proven otherwise
|
|
155
|
+
y2038safe = True
|
|
156
|
+
# load XML from .dump file
|
|
157
|
+
data = cppcheckdata.CppcheckData(dumpfile)
|
|
158
|
+
|
|
159
|
+
# Convert dump file path to source file in format generated by cppcheck.
|
|
160
|
+
# For example after the following call:
|
|
161
|
+
# cppcheck ./src/my-src.c --dump
|
|
162
|
+
# We got 'src/my-src.c' value for 'file' field in cppcheckdata.
|
|
163
|
+
srcfile = dumpfile.rstrip('.dump')
|
|
164
|
+
srcfile = os.path.expanduser(srcfile)
|
|
165
|
+
srcfile = os.path.normpath(srcfile)
|
|
166
|
+
|
|
167
|
+
# go through each configuration
|
|
168
|
+
for cfg in data.iterconfigurations():
|
|
169
|
+
if not quiet:
|
|
170
|
+
print('Checking %s, config %s...' % (srcfile, cfg.name))
|
|
171
|
+
safe_ranges = []
|
|
172
|
+
safe = -1
|
|
173
|
+
time_bits_defined = False
|
|
174
|
+
srclinenr = '0'
|
|
175
|
+
|
|
176
|
+
for directive in cfg.directives:
|
|
177
|
+
# track source line number
|
|
178
|
+
if directive.file == srcfile:
|
|
179
|
+
srclinenr = directive.linenr
|
|
180
|
+
# check for correct _TIME_BITS if present
|
|
181
|
+
if re_define_time_bits_64.match(directive.str):
|
|
182
|
+
time_bits_defined = True
|
|
183
|
+
elif re_define_time_bits.match(directive.str):
|
|
184
|
+
cppcheckdata.reportError(directive, 'error',
|
|
185
|
+
'_TIME_BITS must be defined equal to 64',
|
|
186
|
+
'y2038',
|
|
187
|
+
'type-bits-not-64')
|
|
188
|
+
time_bits_defined = False
|
|
189
|
+
y2038safe = False
|
|
190
|
+
elif re_undef_time_bits.match(directive.str):
|
|
191
|
+
time_bits_defined = False
|
|
192
|
+
# check for _USE_TIME_BITS64 (un)definition
|
|
193
|
+
if re_define_use_time_bits64.match(directive.str):
|
|
194
|
+
safe = int(srclinenr)
|
|
195
|
+
# warn about _TIME_BITS not being defined
|
|
196
|
+
if not time_bits_defined:
|
|
197
|
+
cppcheckdata.reportError(directive, 'warning',
|
|
198
|
+
'_USE_TIME_BITS64 is defined but _TIME_BITS was not',
|
|
199
|
+
'y2038',
|
|
200
|
+
'type-bits-undef')
|
|
201
|
+
elif re_undef_use_time_bits64.match(directive.str):
|
|
202
|
+
unsafe = int(srclinenr)
|
|
203
|
+
# do we have a safe..unsafe area?
|
|
204
|
+
if unsafe > safe > 0:
|
|
205
|
+
safe_ranges.append((safe, unsafe))
|
|
206
|
+
safe = -1
|
|
207
|
+
|
|
208
|
+
# check end of source beyond last directive
|
|
209
|
+
if len(cfg.tokenlist) > 0:
|
|
210
|
+
unsafe = int(cfg.tokenlist[-1].linenr)
|
|
211
|
+
if unsafe > safe > 0:
|
|
212
|
+
safe_ranges.append((safe, unsafe))
|
|
213
|
+
|
|
214
|
+
# go through all tokens
|
|
215
|
+
for token in cfg.tokenlist:
|
|
216
|
+
if token.str in id_Y2038:
|
|
217
|
+
if not any(lower <= int(token.linenr) <= upper
|
|
218
|
+
for (lower, upper) in safe_ranges):
|
|
219
|
+
cppcheckdata.reportError(token, 'warning',
|
|
220
|
+
token.str + ' is Y2038-unsafe',
|
|
221
|
+
'y2038',
|
|
222
|
+
'unsafe-call')
|
|
223
|
+
y2038safe = False
|
|
224
|
+
token = token.next
|
|
225
|
+
|
|
226
|
+
return y2038safe
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def get_args_parser():
|
|
230
|
+
parser = cppcheckdata.ArgumentParser()
|
|
231
|
+
return parser
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == '__main__':
|
|
235
|
+
parser = get_args_parser()
|
|
236
|
+
args = parser.parse_args()
|
|
237
|
+
|
|
238
|
+
exit_code = 0
|
|
239
|
+
quiet = not any((args.quiet, args.cli))
|
|
240
|
+
|
|
241
|
+
if not args.dumpfile:
|
|
242
|
+
if not args.quiet:
|
|
243
|
+
print("no input files.")
|
|
244
|
+
sys.exit(0)
|
|
245
|
+
|
|
246
|
+
for dumpfile in args.dumpfile:
|
|
247
|
+
if not args.quiet:
|
|
248
|
+
print('Checking ' + dumpfile + '...')
|
|
249
|
+
|
|
250
|
+
check_y2038_safe(dumpfile, quiet)
|
|
251
|
+
|
|
252
|
+
sys.exit(cppcheckdata.EXIT_CODE)
|