Skip to content

Commit f5e51ea

Browse files
authored
do not use string-to-integer conversions without error handling (#4906)
1 parent b04812b commit f5e51ea

22 files changed

Lines changed: 549 additions & 120 deletions

.clang-tidy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
Checks: '*,-abseil-*,-altera-*,-android-*,-boost-*,-cert-*,-cppcoreguidelines-*,-darwin-*,-fuchsia-*,-google-*,-hicpp-*,-linuxkernel-*,-llvm-*,-llvmlibc-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,google-explicit-constructor,-readability-braces-around-statements,-readability-magic-numbers,-bugprone-macro-parentheses,-readability-isolate-declaration,-readability-function-size,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-uppercase-literal-suffix,-modernize-use-auto,-readability-else-after-return,-modernize-use-default-member-init,-readability-redundant-member-init,-modernize-avoid-c-arrays,-modernize-use-equals-default,-readability-container-size-empty,-bugprone-branch-clone,-bugprone-narrowing-conversions,-modernize-raw-string-literal,-readability-convert-member-functions-to-static,-modernize-loop-convert,-readability-const-return-type,-modernize-return-braced-init-list,-performance-inefficient-string-concatenation,-misc-throw-by-value-catch-by-reference,-readability-avoid-const-params-in-decls,-misc-non-private-member-variables-in-classes,-clang-analyzer-*,-bugprone-signed-char-misuse,-misc-no-recursion,-readability-use-anyofallof,-performance-no-automatic-move,-readability-function-cognitive-complexity,-readability-redundant-access-specifiers,-concurrency-mt-unsafe,-bugprone-easily-swappable-parameters,-readability-suspicious-call-argument,-readability-identifier-length,-readability-container-data-pointer,-bugprone-assignment-in-if-condition,-misc-const-correctness,-portability-std-allocator-const,-modernize-deprecated-ios-base-aliases,-bugprone-unchecked-optional-access,-modernize-replace-auto-ptr,-readability-identifier-naming,-portability-simd-intrinsics,-misc-use-anonymous-namespace'
2+
Checks: '*,-abseil-*,-altera-*,-android-*,-boost-*,-cert-*,-cppcoreguidelines-*,-darwin-*,-fuchsia-*,-google-*,-hicpp-*,-linuxkernel-*,-llvm-*,-llvmlibc-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,google-explicit-constructor,-readability-braces-around-statements,-readability-magic-numbers,-bugprone-macro-parentheses,-readability-isolate-declaration,-readability-function-size,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-uppercase-literal-suffix,-modernize-use-auto,-readability-else-after-return,-modernize-use-default-member-init,-readability-redundant-member-init,-modernize-avoid-c-arrays,-modernize-use-equals-default,-readability-container-size-empty,-bugprone-branch-clone,-bugprone-narrowing-conversions,-modernize-raw-string-literal,-readability-convert-member-functions-to-static,-modernize-loop-convert,-readability-const-return-type,-modernize-return-braced-init-list,-performance-inefficient-string-concatenation,-misc-throw-by-value-catch-by-reference,-readability-avoid-const-params-in-decls,-misc-non-private-member-variables-in-classes,-clang-analyzer-*,-bugprone-signed-char-misuse,-misc-no-recursion,-readability-use-anyofallof,-performance-no-automatic-move,-readability-function-cognitive-complexity,-readability-redundant-access-specifiers,-concurrency-mt-unsafe,-bugprone-easily-swappable-parameters,-readability-suspicious-call-argument,-readability-identifier-length,-readability-container-data-pointer,-bugprone-assignment-in-if-condition,-misc-const-correctness,-portability-std-allocator-const,-modernize-deprecated-ios-base-aliases,-bugprone-unchecked-optional-access,-modernize-replace-auto-ptr,-readability-identifier-naming,-portability-simd-intrinsics,-misc-use-anonymous-namespace,cert-err34-c'
33
WarningsAsErrors: '*'
44
HeaderFilterRegex: '(cli|gui|lib|oss-fuzz|test|triage)\/[a-z]+\.h'
55
CheckOptions:

.selfcheck_suppressions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ bitwiseOnBoolean
55
# temporary suppressions - fix the warnings!
66
simplifyUsing:lib/valueptr.h
77
varid0:gui/projectfile.cpp
8+
templateInstantiation
89

910
# warnings in Qt generated code we cannot fix
1011
symbolDatabaseWarning:gui/temp/moc_*.cpp

cli/cmdlineparser.cpp

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
260260
}
261261

262262
else if (std::strncmp(argv[i], "--checks-max-time=", 18) == 0) {
263-
mSettings.checksMaxTime = std::atoi(argv[i] + 18);
263+
if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime))
264+
return false;
264265
}
265266

266267
else if (std::strcmp(argv[i], "--clang") == 0) {
@@ -286,6 +287,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
286287
}
287288

288289
else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) {
290+
// TODO: bail out when the folder does not exist? will silently do nothing
289291
mSettings.buildDir = Path::fromNativeSeparators(argv[i] + 21);
290292
if (endsWith(mSettings.buildDir, '/'))
291293
mSettings.buildDir.pop_back();
@@ -365,12 +367,8 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
365367

366368
// --error-exitcode=1
367369
else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) {
368-
const std::string temp = argv[i]+17;
369-
std::istringstream iss(temp);
370-
if (!(iss >> mSettings.exitCode)) {
371-
printError("argument must be an integer. Try something like '--error-exitcode=1'.");
370+
if (!parseNumberArg(argv[i], 17, mSettings.exitCode))
372371
return false;
373-
}
374372
}
375373

376374
// Exception handling inside cppcheck client
@@ -505,18 +503,19 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
505503
else
506504
numberString = argv[i]+2;
507505

508-
std::istringstream iss(numberString);
509-
if (!(iss >> mSettings.jobs)) {
510-
printError("argument to '-j' is not a number.");
506+
unsigned int tmp;
507+
std::string err;
508+
if (!strToInt(numberString, tmp, &err)) {
509+
printError("argument to '-j' is not valid - " + err + ".");
511510
return false;
512511
}
513-
514-
if (mSettings.jobs > 10000) {
512+
if (tmp > 10000) {
515513
// This limit is here just to catch typos. If someone has
516514
// need for more jobs, this value should be increased.
517515
printError("argument for '-j' is allowed to be 10000 at max.");
518516
return false;
519517
}
518+
mSettings.jobs = tmp;
520519
}
521520

522521
#ifdef THREADING_MODEL_FORK
@@ -538,11 +537,13 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
538537
else
539538
numberString = argv[i]+2;
540539

541-
std::istringstream iss(numberString);
542-
if (!(iss >> mSettings.loadAverage)) {
543-
printError("argument to '-l' is not a number.");
540+
int tmp;
541+
std::string err;
542+
if (!strToInt(numberString, tmp, &err)) {
543+
printError("argument to '-l' is not valid - " + err + ".");
544544
return false;
545545
}
546+
mSettings.loadAverage = tmp;
546547
}
547548
#endif
548549

@@ -577,37 +578,40 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
577578

578579
// Set maximum number of #ifdef configurations to check
579580
else if (std::strncmp(argv[i], "--max-configs=", 14) == 0) {
580-
mSettings.force = false;
581-
582-
std::istringstream iss(14+argv[i]);
583-
if (!(iss >> mSettings.maxConfigs)) {
584-
printError("argument to '--max-configs=' is not a number.");
581+
int tmp;
582+
if (!parseNumberArg(argv[i], 14, tmp))
585583
return false;
586-
}
587-
588-
if (mSettings.maxConfigs < 1) {
584+
if (tmp < 1) {
589585
printError("argument to '--max-configs=' must be greater than 0.");
590586
return false;
591587
}
592588

589+
mSettings.maxConfigs = tmp;
590+
mSettings.force = false;
593591
maxconfigs = true;
594592
}
595593

596594
// max ctu depth
597-
else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0)
598-
mSettings.maxCtuDepth = std::atoi(argv[i] + 16);
595+
else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0) {
596+
if (!parseNumberArg(argv[i], 16, mSettings.maxCtuDepth))
597+
return false;
598+
}
599599

600600
// Write results in file
601601
else if (std::strncmp(argv[i], "--output-file=", 14) == 0)
602602
mSettings.outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14));
603603

604604
// Experimental: limit execution time for extended valueflow analysis. basic valueflow analysis
605605
// is always executed.
606-
else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0)
607-
mSettings.performanceValueFlowMaxTime = std::atoi(argv[i] + 33);
606+
else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0) {
607+
if (!parseNumberArg(argv[i], 33, mSettings.performanceValueFlowMaxTime, true))
608+
return false;
609+
}
608610

609-
else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0)
610-
mSettings.performanceValueFlowMaxIfCount = std::atoi(argv[i] + 37);
611+
else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0) {
612+
if (!parseNumberArg(argv[i], 37, mSettings.performanceValueFlowMaxIfCount, true))
613+
return false;
614+
}
611615

612616
// Specify platform
613617
else if (std::strncmp(argv[i], "--platform=", 11) == 0) {
@@ -941,26 +945,18 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
941945
}
942946

943947
else if (std::strncmp(argv[i], "--template-max-time=", 20) == 0) {
944-
mSettings.templateMaxTime = std::atoi(argv[i] + 20);
948+
if (!parseNumberArg(argv[i], 20, mSettings.templateMaxTime))
949+
return false;
945950
}
946951

947952
else if (std::strncmp(argv[i], "--typedef-max-time=", 19) == 0) {
948-
mSettings.typedefMaxTime = std::atoi(argv[i] + 19);
953+
if (!parseNumberArg(argv[i], 19, mSettings.typedefMaxTime))
954+
return false;
949955
}
950956

951957
else if (std::strncmp(argv[i], "--valueflow-max-iterations=", 27) == 0) {
952-
long tmp;
953-
try {
954-
tmp = std::stol(argv[i] + 27);
955-
} catch (const std::invalid_argument &) {
956-
printError("argument to '--valueflow-max-iteration' is invalid.");
958+
if (!parseNumberArg(argv[i], 27, mSettings.valueFlowMaxIterations))
957959
return false;
958-
}
959-
if (tmp < 0) {
960-
printError("argument to '--valueflow-max-iteration' needs to be at least 0.");
961-
return false;
962-
}
963-
mSettings.valueFlowMaxIterations = static_cast<std::size_t>(tmp);
964960
}
965961

966962
else if (std::strcmp(argv[i], "-v") == 0 || std::strcmp(argv[i], "--verbose") == 0)
@@ -979,20 +975,16 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
979975

980976
// Define the XML file version (and enable XML output)
981977
else if (std::strncmp(argv[i], "--xml-version=", 14) == 0) {
982-
const std::string numberString(argv[i]+14);
983-
984-
std::istringstream iss(numberString);
985-
if (!(iss >> mSettings.xml_version)) {
986-
printError("argument to '--xml-version' is not a number.");
978+
int tmp;
979+
if (!parseNumberArg(argv[i], 14, tmp))
987980
return false;
988-
}
989-
990-
if (mSettings.xml_version != 2) {
981+
if (tmp != 2) {
991982
// We only have xml version 2
992983
printError("'--xml-version' can only be 2.");
993984
return false;
994985
}
995986

987+
mSettings.xml_version = tmp;
996988
// Enable also XML if version is set
997989
mSettings.xml = true;
998990
}

cli/cmdlineparser.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <string>
2323
#include <vector>
2424

25+
#include "utils.h"
26+
2527
class Settings;
2628

2729
/// @addtogroup CLI
@@ -118,6 +120,24 @@ class CmdLineParser {
118120
private:
119121
bool isCppcheckPremium() const;
120122

123+
// TODO: get rid of is_signed
124+
template<typename T>
125+
static bool parseNumberArg(const char* const arg, std::size_t offset, T& num, bool is_signed = false)
126+
{
127+
T tmp;
128+
std::string err;
129+
if (!strToInt(arg + offset, tmp, &err)) {
130+
printError("argument to '" + std::string(arg, offset) + "' is not valid - " + err + ".");
131+
return false;
132+
}
133+
if (is_signed && tmp < 0) {
134+
printError("argument to '" + std::string(arg, offset) + "' needs to be a positive integer.");
135+
return false;
136+
}
137+
num = tmp;
138+
return true;
139+
}
140+
121141
std::vector<std::string> mPathNames;
122142
std::vector<std::string> mIgnoredPaths;
123143
Settings &mSettings;

lib/checkbufferoverrun.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,14 @@ static int getMinFormatStringOutputLength(const std::vector<const Token*> &param
143143
outputStringSize++;
144144

145145
if (handleNextParameter) {
146+
// NOLINTNEXTLINE(cert-err34-c) - intentional use
146147
int tempDigits = std::abs(std::atoi(digits_string.c_str()));
147148
if (i_d_x_f_found)
148149
tempDigits = std::max(tempDigits, 1);
149150

150151
if (digits_string.find('.') != std::string::npos) {
151152
const std::string endStr = digits_string.substr(digits_string.find('.') + 1);
152-
const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1);
153+
const int maxLen = std::max(std::abs(strToInt<int>(endStr)), 1);
153154

154155
if (formatString[i] == 's') {
155156
// For strings, the length after the dot "%.2s" will limit

lib/checkclass.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,9 +3224,9 @@ Check::FileInfo * CheckClass::loadFileInfoFromXml(const tinyxml2::XMLElement *xm
32243224
MyFileInfo::NameLoc nameLoc;
32253225
nameLoc.className = name;
32263226
nameLoc.fileName = file;
3227-
nameLoc.lineNumber = std::atoi(line);
3228-
nameLoc.column = std::atoi(col);
3229-
nameLoc.hash = MathLib::toULongNumber(hash);
3227+
nameLoc.lineNumber = strToInt<int>(line);
3228+
nameLoc.column = strToInt<int>(col);
3229+
nameLoc.hash = strToInt<std::size_t>(hash);
32303230
fileInfo->classDefinitions.push_back(std::move(nameLoc));
32313231
}
32323232
}

lib/checkio.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ void CheckIO::checkFormatString(const Token * const tok,
643643
} else if (std::isdigit(*i)) {
644644
width += *i;
645645
} else if (*i == '$') {
646-
parameterPosition = std::atoi(width.c_str());
646+
parameterPosition = strToInt<int>(width);
647647
hasParameterPosition = true;
648648
width.clear();
649649
}
@@ -695,7 +695,7 @@ void CheckIO::checkFormatString(const Token * const tok,
695695
specifier += (*i == 's' || bracketBeg == formatString.end()) ? std::string{ 's' } : std::string{ bracketBeg, i + 1 };
696696
if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
697697
if (!width.empty()) {
698-
const int numWidth = std::atoi(width.c_str());
698+
const int numWidth = strToInt<int>(width);
699699
if (numWidth != (argInfo.variableInfo->dimension(0) - 1))
700700
invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, specifier);
701701
}
@@ -718,7 +718,7 @@ void CheckIO::checkFormatString(const Token * const tok,
718718
case 'c':
719719
if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
720720
if (!width.empty()) {
721-
const int numWidth = std::atoi(width.c_str());
721+
const int numWidth = strToInt<int>(width);
722722
if (numWidth > argInfo.variableInfo->dimension(0))
723723
invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, std::string(1, *i));
724724
}

lib/checkunusedfunctions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLo
442442
} else if (std::strcmp(e2->Name(),"functiondecl") == 0) {
443443
const char* lineNumber = e2->Attribute("lineNumber");
444444
if (lineNumber)
445-
decls[functionName] = Location(sourcefile, std::atoi(lineNumber));
445+
decls[functionName] = Location(sourcefile, strToInt<int>(lineNumber));
446446
}
447447
}
448448
}

lib/clangimport.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -501,19 +501,20 @@ void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line
501501
{
502502
for (const std::string &ext: mExtTokens) {
503503
if (ext.compare(0, 5, "<col:") == 0)
504-
col = std::atoi(ext.substr(5).c_str());
504+
col = strToInt<int>(ext.substr(5, ext.find_first_of(",>", 5) - 5));
505505
else if (ext.compare(0, 6, "<line:") == 0) {
506-
line = std::atoi(ext.substr(6).c_str());
507-
if (ext.find(", col:") != std::string::npos)
508-
col = std::atoi(ext.c_str() + ext.find(", col:") + 6);
506+
line = strToInt<int>(ext.substr(6, ext.find_first_of(":,>", 6) - 6));
507+
const auto pos = ext.find(", col:");
508+
if (pos != std::string::npos)
509+
col = strToInt<int>(ext.substr(pos+6, ext.find_first_of(":,>", pos+6) - (pos+6)));
509510
} else if (ext[0] == '<') {
510511
const std::string::size_type colon = ext.find(':');
511512
if (colon != std::string::npos) {
512513
const bool windowsPath = colon == 2 && ext.size() > 4 && ext[3] == '\\';
513514
const std::string::size_type sep1 = windowsPath ? ext.find(':', 4) : colon;
514515
const std::string::size_type sep2 = ext.find(':', sep1 + 1);
515516
file = tokenList->appendFileIfNew(ext.substr(1, sep1 - 1));
516-
line = MathLib::toLongNumber(ext.substr(sep1 + 1, sep2 - sep1 - 1));
517+
line = strToInt<int>(ext.substr(sep1 + 1, sep2 - sep1 - 1));
517518
}
518519
}
519520
}
@@ -1551,7 +1552,7 @@ static void setValues(Tokenizer *tokenizer, SymbolDatabase *symbolDatabase)
15511552
for (const Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) {
15521553
const std::string &a = arrtok->str();
15531554
if (a.size() > 2 && a[0] == '[' && a.back() == ']')
1554-
mul *= std::atoi(a.substr(1).c_str());
1555+
mul *= strToInt<long long>(a.substr(1));
15551556
else
15561557
break;
15571558
}

0 commit comments

Comments
 (0)