#!/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)