cppcheck/addons/threadsafety.py

351 lines
7.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""
cppcheck addon for threadsafety detection.
This script analyses Cppcheck dump files to locate threadsafety issues.
It warns about
- static local objects
- MT-Unsafe symbols listed in the "Attributes" sections of man pages.
"""
import re
import sys
import cppcheckdata
# --------------------------------
# List of MT-Unsafe identifiers
# --------------------------------
# This is Work In Progress.
# Eventually it should contain all identifiers (types
# and functions) which are MT-Unsafe.
# The script tools/MT-Unsafe.py can help to re-generate this list.
# It reads a man-page tree and report identifiers marked as "MT-Unsafe"
# (see man 7 attributes for what this means), eg
# MT-Unsafe.py /usr/share/man/man3
id_MTunsafe_full = {
# MT-Unsafe types by definition
# 'pthread_t',
# Types marked MT-Unsafe
'const:env',
'const:hostid',
'const:mallopt',
'const:sigintr',
'race:LogMask',
'race:asctime',
'race:crypt',
'race:crypt_gensalt',
'race:cuserid/!string',
'race:dirstream',
'race:drand48',
'race:ecvt',
'race:exit',
'race:fcvt',
'race:fgetgrent',
'race:fgetpwent',
'race:fgetspent',
'race:fsent',
'race:getdate',
'race:getlogin',
'race:getopt',
'race:getspent',
'race:getspnam',
'race:grent',
'race:grgid',
'race:grnam',
'race:hostbyaddr',
'race:hostbyname',
'race:hostbyname2',
'race:hostent',
'race:hsearch',
'race:l64a',
'race:localeconv',
'race:mbrlen/!ps',
'race:mbrtowc/!ps',
'race:mbsnrtowcs/!ps',
'race:mbsrtowcs/!ps',
'race:mcheck',
'race:mntentbuf',
'race:netbyaddr',
'race:netbyname',
'race:netent',
'race:netgrent',
'race:protobyname',
'race:protobynumber',
'race:protoent',
'race:ptsname',
'race:pwent',
'race:pwnam',
'race:pwuid',
'race:qecvt',
'race:qfcvt',
'race:servbyname',
'race:servbyport',
'race:servent',
'race:sgetspent',
'race:signgam',
'race:stdin',
'race:stdout',
'race:streams',
'race:strerror',
'race:strsignal',
'race:strtok',
'race:tmbuf',
'race:tmpnam/!s',
'race:ttyent',
'race:ttyname',
'race:utent',
'race:wcrtomb/!ps',
'race:wcsnrtombs/!ps',
'race:wcsrtombs/!ps',
'sig:ALRM',
'sig:SIGCHLD/linux',
# APIs marked MT-Unsafe
'asctime',
'clearenv',
'ctime',
'cuserid',
'drand48',
'ecvt',
'encrypt',
'endfsent',
'endgrent',
'endhostent',
'endnetent',
'endnetgrent',
'endprotoent',
'endpwent',
'endservent',
'endspent',
'endttyent',
'endusershell',
'endutent',
'erand48',
'error_at_line',
'ether_aton',
'ether_ntoa',
'exit',
'fcloseall',
'fcvt',
'fgetgrent',
'fgetpwent',
'fgetspent',
'fts_children',
'fts_read',
'gamma',
'gammaf',
'gammal',
'getaliasbyname',
'getaliasent',
'getchar_unlocked',
'getdate',
'getfsent',
'getfsfile',
'getfsspec',
'getgrent',
'getgrent_r',
'getgrgid',
'getgrnam',
'gethostbyaddr',
'gethostbyname',
'gethostbyname2',
'gethostent',
'gethostent_r',
'getlogin',
'getlogin_r',
'getmntent',
'getnetbyaddr',
'getnetbyname',
'getnetent',
'getnetgrent',
'getnetgrent_r',
'getopt',
'getopt_long',
'getopt_long_only',
'getpass',
'getprotobyname',
'getprotobynumber',
'getprotoent',
'getpwent',
'getpwent_r',
'getpwnam',
'getpwuid',
'getrpcbyname',
'getrpcbynumber',
'getrpcent',
'getservbyname',
'getservbyport',
'getservent',
'getspent',
'getspent_r',
'getspnam',
'getttyent',
'getttynam',
'getusershell',
'getutent',
'getutid',
'getutline',
'getwchar_unlocked',
'glob',
'gmtime',
'hcreate',
'hdestroy',
'hsearch',
'innetgr',
'jrand48',
'l64a',
'lcong48',
'localeconv',
'localtime',
'login',
'login_tty',
'logout',
'logwtmp',
'lrand48',
'mallinfo',
'mallinfo2',
'mblen',
'mbrlen',
'mbrtowc',
'mbsnrtowcs',
'mbsrtowcs',
'mbtowc',
'mcheck',
'mcheck_check_all',
'mcheck_pedantic',
'mprobe',
'mrand48',
'mtrace',
'muntrace',
'nrand48',
'profil',
'ptsname',
'putchar_unlocked',
'putenv',
'pututline',
'putwchar_unlocked',
'pvalloc',
'qecvt',
'qfcvt',
'rcmd',
'rcmd_af',
're_comp',
're_exec',
'readdir',
'rexec',
'rexec_af',
'seed48',
'setenv',
'setfsent',
'setgrent',
'sethostent',
'sethostid',
'setkey',
'setlogmask',
'setnetent',
'setnetgrent',
'setprotoent',
'setpwent',
'setservent',
'setspent',
'setttyent',
'setusershell',
'setutent',
'sgetspent',
'siginterrupt',
'sleep',
'srand48',
'strerror',
'strsignal',
'strtok',
'tmpnam',
'ttyname',
'ttyslot',
'unsetenv',
'updwtmp',
'utmpname',
'valloc',
'wcrtomb',
'wcsnrtombs',
'wcsrtombs',
'wctomb',
'wordexp'
}
# From man 7 attributes
# the full token could be feature:function/condition - we just want function.
id_MTunsafe = [re.sub('^.*:', '', re.sub('/.*$', '', x))
for x in id_MTunsafe_full
]
def reportError(token, severity, msg, errid): # noqa: D103
cppcheckdata.reportError(token, severity, msg, 'threadsafety', errid)
def checkstatic(data): # noqa: D103
for var in data.variables:
if var.isStatic and var.isLocal:
vartype = None
if var.isClass:
vartype = 'object'
else:
vartype = 'variable'
if var.isConst:
if data.standards.cpp == 'c++03':
reportError(
var.typeStartToken,
'warning',
'Local constant static '
+ vartype + "'" + var.nameToken.str
+ "', dangerous if it is initialized"
+ ' in parallel threads',
'threadsafety-const')
else:
reportError(var.typeStartToken, 'warning',
'Local static ' + vartype + ': '
+ var.nameToken.str,
'threadsafety')
def check_MTunsafe(cfg):
"""
Look for functions marked MT-unsafe in their man pages.
The MT-unsafe functions are listed in id_MTunsafe (and id_MTunsafe_full).
That section of code can be regenerated by the external script MT-Unsafe.py
"""
for token in cfg.tokenlist:
if token.str in id_MTunsafe:
reportError(token, 'warning', token.str + ' is MT-unsafe',
'unsafe-call')
if __name__ == '__main__':
parser = cppcheckdata.ArgumentParser()
args = parser.parse_args()
quiet = args.quiet or args.cli
if not args.dumpfile:
if not args.quiet:
print('no input files.')
sys.exit(0)
for dumpfile in args.dumpfile:
# load XML from .dump file
data = cppcheckdata.CppcheckData(dumpfile)
for cfg in data.iterconfigurations():
if not args.quiet:
srcfile = data.files[0]
print('Checking %s, config %s...' % (srcfile, cfg.name))
check_MTunsafe(cfg)
checkstatic(cfg)
sys.exit(cppcheckdata.EXIT_CODE)