INCORPORATING CERT SECURE CODING STANDARDS IN TERMS OF UNDEFINED BEHAVIOR AND USELESS CONDITIONS INTO THE CPPCHECK PROJECT

A thesis submitted

to Kent State University in partial

fulfillment of the requirements for the

degree of Master of Science in Computer Science

By

Anwar Alsulaiman

August 16th, 2014

Thesis written by

Anwar Alsulaiman

B.S., King Faisal University, KSA 2009

M.S., Kent State University, USA 2014

Approved by

Dr. Michael Rothstein , Advisor

Dr. Javed I. Khan , Chair, Department of Computer Science

Dr. James Blank , Dean, College of Arts and Sciences

ii

TABLE OF CONTENTS

LIST OF FIGURES ...... VI

LIST OF TABLES ...... VII

ACKNOWLEDGEMENTS ...... VIII

CHAPTER 1 INTRODUCTION ...... 1

1.1 Open Source Software ...... 1

1.1.1 History ...... 1

1.1.2 What is Open Source? ...... 2

1.1.3 Open Source vs. Closed Source ...... 3

1.2 CPPCHECK ...... 5

1.2.1 How Does CPPCHECK Work? ...... 6

1.2.1.1 Control Flow Analysis ...... 6

1.2.2.2 Token List Method ...... 10

1.3 CERT ...... 13

1.3.1 What is CERT? ...... 13

1.3.2 CERT Secure Coding Standards ...... 13

CHAPTER 2 STATIC CODE ANALYSIS TOOLS ...... 15

2.1 What is Static Code Analysis? ...... 15

2.2 Static Code Analysis Methods ...... 15

2.3 Advantages and Disadvantages of Static Code Analysis ...... 16

2.4 Most Popular Commercial Static Code Analysis Tools ...... 17

2.4.1 PC-Lint ...... 17

2.4.2 Klockwork Insight ...... 18

2.4.3 Coverity Prevent ...... 18

2.5 Static Code Analysis Types ...... 18

2.5.1 Non-Annotated Static Code Analyzers ...... 19

2.5.2 Annotated Static Code Analyzers ...... 20

2.5.2.1 Splint ...... 20

2.5.2.2 CSSV ...... 21

2.5.2.3 CQUAL ...... 22

2.5.2.4 Meta-Complication (MC) ...... 22

2.5.2.5 CPPCHECK ...... 22

2.6 Why CPPCHECK? ...... 23

CHAPTER 3 CONTRIBUTION TO CPPCHECK ...... 24

3.1 CPPCHECK Environment ...... 24

3.2 The Procedure of New Checks Implementation ...... 25

3.3 Implemented CERT Rules Checks ...... 27

3.3.1 ARR02-/C++ (Explicitly specify array bounds, even if implicitly defined by

an initialize) ...... 27

3.3.2 EXP01-C/C++ (Do not take the size of a pointer to determine the size of the

pointed-to type): ...... 30

3.3.3 FIO01-C/C++ (Be careful using functions that use file names for

identification) ...... 32

3.3.4 FIO17-C/C++ (Prefer streams to C-style input and output) ...... 36

iv

3.3.5 INT06-C/C++ (Use strtol ( ) or a related function to convert string token to

an integer) ...... 38

3.3.6 CON30-C/C++ (Clean up thread-specific storage) ...... 39

3.3.7 SIG32-C/C++ (Do not call longjmo ( ) from inside signal handler) ...... 41

3.3.8 STR32-C/C++ (Do not pass a non-null-terminated character sequence to a

library function that expects a string) ...... 43

CHAPTER 4 FUTURE WORKS ...... 45

4.1 Adding a Setting in CPPCHECK Library ...... 45

Example (string copy) ...... 47

Example (buffer size smaller than size parameter) ...... 47

CHAPTER 5 CONCLUSION ...... 48

APPENDIX A ...... 49

APPENDIX B ...... 52

REFRENCES ...... 155

v

LIST OF FIGURES

Figure 1.1: Token list method mechanism ...... 10

Figure 1.2: Example of token list method process ...... 12

Figure 3.1: Flowchart of ARR02 rule ...... 29

Figure 3.2: Flowchart of EXP02 rule ...... 30

Figure 3.3: Flowchart of FIO01_1 rule ...... 33

Figure 3.4: Flowchart of FIO01_2 rule ...... 34

Figure 3.5: Flowchart of FIO17 rule ...... 36

vi

LIST OF TABLES

Table 1.1: Open source software project vs. closed source software project ...... 4

Table 2.1: Average cost of fixing defects depending on the time they were detected ...... 17

ACKNOWLEDGEMENTS

My master degree thesis would not have been possible without the support of many caring individuals in my life including supporting faculties and family members.

First in the list is my advisor, Dr. M. Rothstein, who helped me to make sense by directing my thinking to the right path and paving the road for this study. As a result, he added value to my work in order to accomplish this important milestone toward fulfilling my objective. Of course, many thanks go to my supporting thesis committee members,

Dr. M. Austin, and Dr. H. Peyravi, who valued and accepted my study. Moreover, I would not forget the support of my husband, parents, and numerous friends who endured this long process with me, always offering support and love. Cheers to all of them!

Anwar Alsulaiman

June 14, 2014, Kent, Ohio

viii

CHAPTER 1

Introduction

In this thesis, we propose a way to make the open source software project called

CPPCHECK as secure as possible by adding high-potential static checkers for program vulnerabilities. Therefore, we are looking to implement static checkers with high-level security standards. The Computer Emergency Response Team (CERT) secure coding standards meet our requirements. In particular, this thesis will address the security limitation of CPPCHECK in the detection of undefined behavior and useless conditions.

We will use new techniques of modeling these more elaborate rules’ checkers using the token list and initial rule file methods, which are feasible for this project, as discussed in chapter 3.

This chapter gives a background of open source, CPPCHECK, and CERT secure coding standards. In chapter 2, we survey static code analysis tools and explain why CPPCHECK has been chosen for this thesis. In chapter 3, we show our contribution to CPPCHECK and in chapter 4 we give suggestions for future work.

1.1 Open Source Software

1.1.1 History

Before the birth of the earliest form of the Internet, computer programs were freely distributed with their source code. They were shared and used with no cost.

Software users could access and modify source code according to their needs. However,

1 2

in 1969, IBM started to unbundle its products and charge separately for its software [2] [3].

These were the first non-free computer programs. Since then, accessing and modifying source code has become restricted.

In the 1980s, many users started to share computer program with source code through the (BBS) network [1]. Wayne Bell, creator of the WWIV

BBS system, notably shared his modifications of software’s source code on his system [1].

After that, the shared source code concept spread through Unix-to-Unix Copy (UUCP),

Usenet, Internet Relay Chat (IRC), and Gopher protocol [1].

In the early 1998s, a free software movement group called Netscape

Communications Corporation chose the term “Open Source” when they released their own source code under the Netscape Public License (NPL) [4]. Many other terms have been used thereafter, such as “Open Software,” “Open Domain,” “Free Open-Source

Software,” and “Commercial Open-Source Software.” In addition, many open source software projects have emerged [1].

1.1.2 What is Open Source?

The term “open source” refers to a computer program in which the source code is freely accessible for use or modification. Such modifications include adding new features, fixing bugs, reporting bugs, or submitting documentation [6]. Initially, this kind of software comes with information from the developers, such as files, features, news, support, and documentation giving users the proper environment for any development.

3

1.1.3 Open Source vs. Closed Source

Closed-source software or “Proprietary Software” includes any computer program restricted in use under certain terms and conditions. Users are not allowed to share, modify, and use the source code for any reason. Usually, this kind of software does not come with available source code [15].

According to the Open Source Business Conference survey of 2009, part of the Future of

Open Source survey [16], companies and public users prefer to work on open-source due to lower cost, security, lack of vendor “lock in”, and quality [5]. The following table makes the choice clear:

4

Table 1.1: Open source software project vs. closed source software project

Open source software project Close source software project - Initial cost: Less expensive - Initial cost: Expensive

- License: Optional - License: Required (costly)

- Maintenance and support: Initial - Maintenance and support: Needs

need for support may be high in-house support and someone who

depending on many factors, but will can maintain the system

decrease with time; maintenance

required

- Security and safety: Worms don’t - Security and safety: Extremely safe

often attack open-source systems, and secure system

but stealing passwords and personal

information is easy - Documentation: Well explained

- Documentation: Makes the system

difficult to understand - Examples: Microsoft Word, and

- Examples: GNU Image Adobe Photoshop

Manipulation Program (GIMP) and

Apache HTML server

5

1.2 CPPCHECK

One popular open-source software project is CPPCHECK. CPPCHECK is a static tool for C/C++ that handles analysis tasks to detect security vulnerabilities that the and other analysis tools could not catch [8]. Its main goal is to turn out only the real code’s errors, those with zero false positives [8]. As a result, it is rarely wrong in detecting errors in terms of accuracy [9]. Furthermore, it has been useful in many popular open-source projects where [17]:

• More than 40 bugs were found and fixed in the

• More than 20 bugs were found and fixed in the VLC media player

• Some bugs were found and fixed in other open-source projects such as 7-

zip, curl, or git.

CPPCHECK is effective in terms of detecting the most common real errors caused by enormous types of security vulnerabilities as it has the following main features

[8]:

• Checking for out of bounds

• Checking for the code for each class

• Checking for exception safety

• Checking for memory and resource leaks

• Warning of obsolete functions used

• Checking for invalid use of the Standard Template Library (STL)

• Checking for uninitialized variables and unused functions

6

CPPCHECK can be implemented and used across many platforms such as

Windows and Linux and through plugins from various IDEs like Eclipse, Code::Blocks,

Hudson, and Jenkins [17].

1.2.1 How Does CPPCHECK Work?

1.2.1.1 Control Flow Analysis

We might first think CPPCHECK uses symbolic analysis to determine control flow. However, there is no symbolic analysis of conditions in this kind of open-source project. Instead, we make some assumptions about control flow. In the following example, the assumptions are done without looking at the conditions: void f() { if (condition1) { x(); if (condition2) { y(); } }

if (condition3) { z(); } }

We assume that all the functions (x, y, and z) can be called, and y will only be called if x is called. Please note we do not assume that z will or will not be called when x is called; that is unknown. We do not write warnings that depend on this. Furthermore, in the following code example, we assume both x and y can be called. If x is called, then y cannot be called. Also, it is possible neither x nor y will be called: void f() {

7

if (condition1) { x(); return; }

if (condition2) { y(); } }

1.2.1.2 Buffer Overflows:

We describe the CPPCHECK’s buffer overflows through an example where it detected them. An array is accessed out of bounds somewhere in its scope: void f () { char a[10]; if ( x + y == 2) { a [20] = 0; } }

CPPCHECK will generate the following message: “Array ‘a [10]’ index 20 out of bounds” As in the example above, no control flow analysis is used as CPPCHECK does not try to determine how execution can reach the “Array ‘a [20] = 0;’” statement [10].

1.2.1.3 Memory Leak:

8

CPPCHECK uses a true/false control flow analysis to detect any leak at all statements. Consider the following example where CPPCHECK would detect a memory leak at the ‘return’ statement. However, it does not detect how the execution reaches the

‘return’ statement unless the memory leak occurs [10]:

Void f ( ) { char *a = malloc(10); if (x + y == 2) { return; } free(a); }

1.2.2 CPPCHECK Design:

CPPCHECK has two methods for new rules creation: the initial “rule file” method, used to catch a certain term or an operator usage, and the token list method for a more elaborate pattern. The following describes these methods.

1.2.2.1 Initial “rule file” Method

This method goes through two steps to create the regular expression and create an

XML-based rule file:

Step 1: Create the regular expression by validating the regular expressions. CPPCHECK uses the Perl Compatible Regular Expressions (PCRE) library to take care of this task.

The following are some examples of valid and invalid expressions [11]:

• Valid expressions patterns:

9

o /<\ / \ w+>/ - match any word character [a-zA-Z0-9_]

o | ( \ {3}) - \ d + | Sm - match a digit [0-9]

o / ^ (? i) php [34] / - match the remainder of the pattern

o {^ \ s + (\ s+) ? $} - match any white space character

• Invalid expressions patterns:

o / href=’ (.*)’ - missing ending delimiter

o / \w+\s*\w+ /J - unknown modifier ‘J’

o 1- \ d3- \ d3 - \ d4| - missing starting delimiter

Step 2: Create an XML rule file that contains a pattern and an error message that can be displayed when that particular pattern is detected. The file format should be formed as the following: [12]

LIST PATTERN ID SEVERITY

SUMMARY

As shown in the XML rule file format above, the element is optionally used to determine the tokens that need to be checked. The list of the token can be represented as the following [12]:

• Define: preprocessor statements checks

10

• Raw: preprocessor output check

• Normal: normal token list check

• Simple: simple token list check, most CPPCHECK checks use the simple token

list.

Furthermore, the element is an executable PCRE-Compatible Regular

Expression. In addition, the element specifies the user-defined message ID, the

reports one of the CPPCHECK severities such as style, warning, or error, and the

returns the written matching tokens unless the summary for the message appears [12].

1.2.2.2 Token List Method

The token list method is more advanced than the initial “rule file” method, with a more elaborate pattern. Figure 1 shows how the process of the token list method flow:

C++C++ Preprocessor

Tokenizer

Simplifier

Checks Result

Figure 1.1: Token list method mechanism

As shown in figure 1, in the first step, preprocessed code in the source file is tokenized. For example, if we have the following source code:

11

xyz = xy+z;

Then, its token list would be 6 tokens such that:

Token (1): xyz Token (2): = Token (3): xy Token (4): + Token (5): z Token (6): ;

The second step is to simplify the tokenized code by normalizing the indentation, spacing, NULL checks, and braces as a preparation for checks [17]. Next, in the checks stage, a necessary iteration is performed. Moving to specific tokens using simple indexing does that. Then there is a search for defects. The checkers in this method have full access to each token through iterations by looping through all tokens as the following

[12]:

// Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {

... }

12

Void foo(char* str) { if (str == 0) printf(str); else printf (“Whoa”); }

Tokenizer

Simplifier

Void foo (char * str ) { if ( ! str ) { printf ( str ) ; } else { printf ( “Whoa” ) ; } }

Checks Resul t

Figure 1.2: Example of token list method process

Along with the token list method process, there are some token expressions that can be used. The most common is Token::Match, which match tokens against expressions. There are several Token:: Match expressions, such as [13]:

• Tokens are either completely matched or not matched at all.

• Spaces are used as separators.

• There are special meanings for +*?() based on normal regular expressions.

However, these are just normal characters in Token::Match patterns.

13

1.3 CERT

1.3.1 What is CERT?

The Computer Emergency Response Team (CERT) is in the Software

Engineering Institute, part of Carnegie Mellon University. CERT concentrates on development of the computer and network security [14]. “The CERT mission is to identify insecure coding practices and develop secure alternatives that software developers can use to take practical steps to reduce or eliminate vulnerabilities before deployment” [14].

CERT’s first priority is to accomplish its mission so that valuable services are provided in various fields such as reactive, proactive, and security quality management services [18].

In addition to these services, it sets important standards that have assured secure coding practices for languages such as C, C++, and Java [14].

1.3.2 CERT Secure Coding Standards

In addition to its security standards, CERT addresses undefined behavior that can lead to exploitable vulnerabilities. Its short-term goals are to reduce vulnerabilities resulting from coding errors, identify the common programming errors that lead to software vulnerabilities, and educate software developers about secure coding practices

[19]. CERT standards thus limit the number of vulnerabilities to a level manageable in operational environments. CERT has been named one of the most successful divisions in terms of secure coding standards and its 92 rules named keys to a safe, reliable, and secure system [14]. Many standards exist in many fields, including the floating-point, arrays, characters and string, memory management, definitions, undefined behavior, and

14

unspecified behavior fields. Some analyzers, such as Coverity, ECLAIR, EDG, Fortify,

GCC, Klocwork, LDRA, PRQA QA-C, Rose, and Splint, generate some of them [14].

CHAPTER 2

Static Code Analysis Tools

The term “static analysis” refers to a tool that conducts analysis on computer software. Unlike dynamic analysis, it is performed without executing the software. In most cases, it checks open-source code. Its main purpose is to highlight the possible coding errors in a safety-critical computer system.

This chapter surveys static analysis tools that are currently available to assist the software’s implementations in both C/C++ programing languages. In fact, there are far more tools available for these programing languages than what will be covered in this section.

2.1 What is Static Code Analysis?

Static code analysis is a standard procedure used for discovering errors and defects in software’s source code [19][21]. This kind of analysis can work as simply as scanning the code for familiar vulnerable functions (e.g., strcpy and gets) or as complexly as completing an application model [23]. Static code analysis does not need to execute a compiler to do its job; it is an automated code review process that handles the common alternative of the source code, and then provides suggestions to enhance it [20].

2.2 Static Code Analysis Methods

Static analysis can be performed formally via one of the following methods:

• Model checking

15 16

• Data-flow analysis

• Abstract interpretation, which is a theory of sound approximation of the

semantics of computer programs

• Assertion, a predicate, true/false statement, in program code

• Symbolic execution

• Key performance indicator (KPI)-driven code analysis.

These methods usually require mathematical models and certain affirmations as they often ask for a finite-state machine of the modeling system, verifying each state and transition [17]. Such methods are too expensive as they are time-consuming to make, and static analysis process is supposed to run quickly and efficiently [17] [40].

Code metrics is another static code analysis method that works on searching areas where defects are likely to appear [17] [40]. It uses careful code complexity measures rooted in graph theory (e.g., cyclomatic complexity) [17]. Code metrics is considered a good static code analysis technique despite its limited capacity to directly identify errors [17].

In addition to the formal and code metrics methods, particular reviews and can be done by searching suspicious patterns [17], with similar results to a perfect basic code review.

2.3 Advantages and Disadvantages of Static Code Analysis

Static code analysis has one main advantage. It significantly reduces the cost of fixing defects, which would otherwise be too expensive [20]. According to McConnell [20], discovering a defect at the examination process costs ten times more than at the code- writing process (Table 2)[20].

17

Table 2.1: Average cost of fixing defects depending on the time they were detected

Time Detected Time Introduced Requirements Architecture Construction System Post- Test Release Requirements 1 3 5-10 10 10-100 Architecture - 1 10 15 25-100 Construction - - 1 10 10-25

Static code analysis

Furthermore, static code analysis has several advantages [19][21][22]. It covers the full code, including the fragments that cannot be examined via other analyzers. This advantage would help fully control working code and find defects in exception handlers and systems’ logging. Static analysis also finds hidden errors that might occur when switching to another compiler version or using any of the code optimization switches. It also easily discovers misprints effects such as copy-paste usage.

On the other hand, it has several disadvantages [19][21][22]. Unlike dynamic analysis, it is weak in diagnosing memory leaks and concurrency errors. Also, it can generate false positive when warning about odd code fragments.

2.4 Most Popular Commercial Static Code Analysis Tools

2.4.1 PC-Lint

PC-Lint is a commercial command-line tool for performing static code analysis that searches for code errors such as bugs, glitches, inconsistencies, non-portable

18

constructs, and redundant code in C and C++ languages [41]. It is used for quality purposes, as its main task is to check whether a certain source code meets the software development standards for C/C++ developed by Motor Industry Software Reliability

Association (MISRA C or MISRA C++) [17] [41]. Furthermore, it checks for unique defects such as problems with parallel built on POSIX threads [42]. Additional tools use PC-Lint output to generate warning reports in such an accessible form [42].

2.4.2 Klockwork Insight

Klocwork Insight is a commercial static code analysis tool to search for security and reliability vulnerabilities in C, C++, Java, or C# [17]. It helps find many problems in different areas (e.g. buffer overflows, memory leaks, and null pointer dereferences)

[17][43]. It works through many plug-ins for developers, metrics, and reporting [17].

2.4.3 Coverity Prevent

Coverity Prevent static code analysis is first developed from research at Stanford

University [17]. It discovers code errors by combining inter-procedural data flow analysis and statistical analysis mechanisms [44]. Its Inter-procedural data flow analysis analyzes each function and generates a brief context-sensitive summary, while statistical inference techniques discover the significant code directions [44]. Recently, Coverity Prevent identified over 6,000 bugs across 53 wide-range open-source projects.

2.5 Static Code Analysis Types

Static code analysis can be conducted with or without annotations. In non- annotated static code analysis, static determination for a possible input program is

19

conducted through what is implied from the source code. In annotated static code analysis, the analyzer performs static determination for any possible input program through assumptions [23].

2.5.1 Non-Annotated Static Code Analyzers

Non-annotated static code analyzers deal with safe codes without requiring any programmer annotation about trying particular assumptions [23]. To make certain that a definite code is safe, a model of the execution environment is built in each stage of the code [23]. Another way to assure the safety of code is to perform system buffers function as simply as possible, such as by using a set of constraints or integer ranges [23].

2.5.1.1 BREfix

BREfix is a static analyzer that searches for dynamic programming defects [36].

During analysis, BREfix explores determination of facts for each path to be taken. Also, it establishes preconditions for successful path execution and minimal false positives of returned values [36]. Such preconditions include an execution model to test a certain source code where it provides a sequential trace of accomplishable execution paths [37][38].

Then, the results are used to design a model for each function [37][38]. To minimize false positive values, tests can be delayed by adding a constraint in the function’s model [37][38].

2.5.1.2 BREfast

BREfast is an adaptation version of BREfix with speed as its top priority [38].

BREfix and BREfast are complementary tools where BREfast runs orderly during the

20

code development to point out simpler defects, and BREfix simultaneously runs in a particular interval schedule to perform as complete an analysis as possible [38].

2.5.1.3 ARCHER

ARCHER searches for unsafe memory access to enhance robustness as well as reliability of codes in C [39]. Its big task is to convert C into an intermediate representation in which all side effects are discarded by providing temporary variables, flattened nested function calls, and necessary operators [39]. In this procedure, a control flow graph is needed to measure each function that is being analyzed for possible defects [39]. ARCHER has a major limitation: it operates on arrays and pointers while ignoring string operations

[39].

2.5.2 Annotated Static Code Analyzers

This kind of analysis relies on the programmer’s help in terms of determining the possible assumptions where a specific code could produce [23]. Therefore, such analyzers do not search for all possible errors unless the programmer asks them to; they focus where the programmer wants them to focus [23]. They thus work as quickly as possible by looking for particular lines in which the agreed assumptions would not be met [23]. The following are examples of annotated static code analyzers:

2.5.2.1 Splint

Splint is a lightweight tool that searches for security vulnerabilities and coding errors checks in C [26]. It can be used as “a great lint” as it works with the minimal effort

[27]. It depends on annotations that describe assumptions about particular targets such as

21

particular value, or pre-conditions and post-conditions, which must be met by a particular function [24][25]. For example, giving built-in annotations for a particular vulnerable function strcpy: /* @requires maxSet(s1) <= maxRead(s2) @*/, s1 is larger than the data read from s2 (source) [24][25]. In Splint, taint analysis detects format string vulnerabilities where all inputs are considered tainted. A defect report arises if a tainted variable is used while an untainted one is expected [24][25]. Then the format function definitions tend to be changed to deal with the upcoming arguments of untainted format strings [24][25].

2.5.2.2 CSSV

CSSV is a static source code analyzer modeled to search for all buffer overflows in C [28][29]. It reports all defects at the expense of reporting false positives [28][29]. Also, it looks for the pre-conditions, post-conditions, and side effects of specific functions that a programmer should provide [28][29]. It first determines the information and then converts the source code to CoreC, a semantics-preserving subset of the C. As a result, source-to- source conversion works easily as the subset of C is supported instead of the complete language [30]. After that, pointer analysis detects the same base address pointers; then, the results from this detection convert the code to an integer code on which integer analysis is performed [31]. The goal of this analysis is to point out the differences between guaranteed variables. All asserts added by the inlining of information are verified unless an assert fails [31]. CSSV relies on how accurately the information is provided in the number of false positives it returns [28][29].

22

2.5.2.3 CQUAL

CQUAL is a type-based static analysis tool that searches for bugs and security vulnerabilities in C [32][33]. It supports user-defined type qualifier framework in the same way it supports the standard C type qualifier [32]. In CQUAL, a programmer specifies a qualifier type and it defines the sub-type relationships between qualifiers and this type

[32]. The following code presents such a type with checked and unchecked elements [32]:

Partial order{ $checked < $unchecked }

CQUAL point all variables to a value, based on the tainted data where it should not be used as a format string argument in order to avoid system defects [33].

2.5.2.4 Meta-Complication (MC)

MC performs static analysis and model checking [35]. Its metal language is for slice specification by pattern matching, code translation, and state machines [35]. In addition, MC analysis runs by allowing programmers to invent complier expansions where they can assign a source code to follow particular rules [34]. It then checks whether the source code adheres to these restrictions [34].

2.5.2.5 CPPCHECK

As discussed in Chapter 1, CPPCHECK searches for errors that a C/C++ compiler usually does not catch. It checks for bad usage of some functions, memory leaks, buffer overruns, use of old functions that ought to be avoided, invalid function usage, classes

23

constructor use, non-virtual destructor classes base detection, and division with signed and unsigned operands [27].

2.6 Why CPPCHECK?

We chose CPPCHECK static code analysis for this thesis for several reasons.

First of all, it can be used in both C and C++ languages, while most static tools work only in C. It preforms a numerous number of checks in various fields, while others focus on a particular area. Its analysis procedure is easy to follow as it works by parsing the source code, splitting it into tokens, and finding suspicious patterns in the tokens. Finally, it is open software.

CHAPTER 3

Contribution to CPPCHECK

Although the checks in CPPCHECK are considered an advantage in vulnerability detection, it does no CERT-secure coding standards detection unless its features are modified. It allows users to modify those features and we will take advantage of this. As

CERT-secure coding standard have numerous rules in many fields, we will focus on addressing the limitations of CPPCHECK in detecting undefined behavior and useless conditions by implementing them using either of the consistency methods, such as the token list method or the initial rule file method to make the coding practice as secure as possible.

3.1 CPPCHECK Environment

To execute our new checks, we must have a proper CPPCHECK environment. To achieve that environment, several important tools should be collected and implemented correctly:

• Download the latest version of CPPCHECK from

https://cppchecksourceforge.net (current version is 1.64).

• Download GitHub software from https://github.com/ in order to fork and

clone the cppcheck files via GitHub as described in Appendix A.

• Download Perl Compatible Regular Expressions (PCRE) from

http://pcre.org as described in Appendix A.

24 25

3.2 The Procedure of New Checks Implementation

We will use the token list and the initial rule file methods to catch the aforementioned violations. With the former method, we will model the new checks mainly in cppcheck/lib/checkother.cpp; whereas with the latter method, we will model each new check separately in rule files.

The following shows how the procedure works when using the token list method:

• Update the checkother.h class in the CPPCHECK directory with a new

class function, and model this token class in its appropriate location.

• Update the logic to fulfill the new function’s feature.

• Run the “make” command in CPPCHECK.

• Create a sample source file in orde`r to test this new function.

• Run CPPCHECK against the test source file. This should trigger the test

source file.

If using the initial rule file, the following procedure is needed:

• Create XML rule file that includes a pattern to search for. Then create an

error message to be reported when the wanted pattern is found. The XML

file format is as follows [12].

LIST PATTERN ID

26

SEVERITY

SUMMARY • Run the “make” command in CPPCHECK.

• Run CPPCHECK against the test source file, which should trigger the test

source file.

Please note that when trying to get the new tokens to be triggered, if it were not catching the target token, running “make clean” on CPPCHECK would be necessary.

27

3.3 Implemented CERT Rules Checks

3.3.1 ARR02-C/C++ (Explicitly specify array bounds, even if implicitly defined by

an initialize)

The C/C++ standards let a variable of an array to be declared with a bound and initialization literal, or both, which in some cases presents an array bound with a specified number of elements [46]. For instance, the following array size is implied by an initialization literal that is specified with a restricted five-element number:

//5-element array int array [] = {1, 2, 3, 4, 5};

Therefore a specified array size should not be smaller than the number of initializers.

Otherwise, the residual array’s values would be zero-initialized [45]. In the following non- compliant code example, the compiler will allocate an array of four-integer elements.

However, if the initializer changes, the array bound may also change, causing false results: int main() { int a[] = {1, 2, 3, 4}; int b[]; b = {1, 2, 3, 4}; int *c; c = {1, 2, 3, 4}; }

This rule is important for secure coding practice. Specifying both the bound and the initialization makes it easy for the compiler to detect if these two sizes disagree. Our task is to model this CERT rule check in CPPCHECK to determine whether someone is

28

trying to initialize an array without putting an implicit bound on it. The following flowchart shows the criteria of creating CERT ARR02-C/C++ rule in cppcheck/lib/checkother.cpp:

29

Figure 3.1: Flowchart of ARR02 rule

30

3.3.2 EXP01-C/C++ (Do not take the size of a pointer to determine the size of the

pointed-to type):

EXP01-C/C++ is an important expression rule in terms of preventing security vulnerabilities. It states that when determining the size of a certain type, the size of the pointer that points to the type should not be added in [50]. The resulting inaccuracy would affect the size of an array. For example, the following is a noncompliant code that erroneously calls the sizeof ( ) operator expression on the variable d_array, which is declared as a pointer to double, where it should be *d_array [50]: double *allocate_array(size_t num_elems) { double *d_array;

if (num_elems > SIZE_MAX/sizeof(d_array)) { /* handle error condition */ } d_array = (double *)malloc(sizeof(d_array) * num_elems); if (d_array == NULL) { /* handle error condition */ } return d_array; }

In the previous code, the expression sizeof (*d_array) should return the actual size of the data structure referenced by d_array rather than the size of the pointer. Then, we model this rule via CPPCHECK in order to guarantee an accurate calculation of elements’ size to be included in the overall data structure. So, we generate the following flowchart that handles our purpose.

31

Figure 3.2 :Flowchart of EXP01 rule

32

3.3.3 FIO01-C/C++ (Be careful using functions that use file names for

identification)

In input and output operations, the chance of file-related security vulnerabilities increases, especially when mistakenly accessing unwanted file object names that are loosely bound to underlying file objects by the operating system [51]. Unlike the file descriptor and file pointers, the file name provides no information. The FIO01 CERT rule is designed to assure that accessing a certain file must be within either file descriptor or file pointers [51]. The noncompliant code example below shows that the function chmod (

) is called to edit the permission of a certain file [51]. The file object identified by file_name in the call to fopen ( ) is not the same as that called file_name in the call to chmod ( ) [51]. char *file_name; FILE *f_ptr;

/* initialize file_name */ f_ptr = fopen(file_name, "w"); if (f_ptr == NULL) { /* Handle error */ }

/* ... */ if (chmod(file_name, S_IRUSR) == -1) { /* Handle error */ }

33

Figure 3.3 and 3.4 show the appropriate way to model FIO01 and implement it into CPPCHECK to check for this security vulnerability. We will accomplish this goal with Portable Operating System Interface (POSIX) functions, fchmod () and open().

Implementing these two functions in the proposed check would guarantee that the files that would be operated on are the same ones as are opened. So, we are going to break our invented check into two parts. The first part will detect file-related race conditions by searching for functions such as chown ( ), stat ( ), and chmod ( ). When CPPCHECK gives a warning message about an error related to these functions, a programmer would try to fix these security vulnerabilities, perhaps replacing them with fchown ( ), fstate ( ), and fchmod ( ), respectively, to eliminate file-related race conditions [51]. The second part will use POSIX functions in the absence of file descriptors such as link ( ), unlink ( ), mkdir ( ), rmdir ( ), mount ( ), unmount ( ), lstat ( ), mknod ( ), symlink ( ), and utime ( ).

34

Figure3.3: Flowchart of FIO01_1 rule

35

Figure 3.4: Flowchart of FIO01_2 rule

36

3.3.4 FIO17-C/C++ (Prefer streams to C-style input and output)

Since the facilities available in C++ streams are considered to be more secure than

C-style of input/output facilities, streams should be used [52]. In the following code is a noncompliant code, C-style input/output facilities are highly insecure [52]:

#include int main(int argc, char *argv[]) { char filename[256]; FILE *f; char format[256];

fscanf(stdin, "%s", filename); f = fopen(filename, "r"); // read only

if (f == 0) { sprintf(format, "Error opening file %s\n", filename); fprintf(stderr, format); exit(-1); } fclose(f); }

Since the above code reads a file name using fscanf function, two vulnerabilities might appear. The first is when fscanf causes a buffer overflow because of the check of the input that would fit into the allocated space being missing [52]. Furthermore, the chance of format string exploits vulnerability would increase when using fscanf [52].

CERT rule FIO17 proposed a way to prevent these security vulnerabilities using both the string class and iostream library. As shown in figure 3.5, CPPCHECK would catch the use of fscanf ( ), fopen ( ), fclose ( ), sprint ( ), and fprintf ( ) functions.

37

Figure 3.5: Flowchart of FIO17 rule

38

3.3.5 INT06-C/C++ (Use strtol ( ) or a related function to convert string token to

an integer)

The strtol ( ) or related functions would provide more robust errors than other solutions [53]. CERT rule INT06 states that the functions strtol ( ), strtoll ( ), strtoul ( ), and strtoull ( ) convert a null-terminated byte string portion to the following integers respectively: long int, long long int, unsigned long int, and unsigned long long int [53]. In the following noncompliant code example, this code converts the string token stored in the static array buffer to a signed integer value using the function of atio ( ) [53]: int si; if (argc > 1) { si = atoi(argv[1]); }

The atio ( ) function would convert the initial portion of a string token to int. Therefore, undefined behavior would appear if the result’s value could not be represented [53].

Moreover, atio ( ) would return zero if the string represents a zero-denoting input string form integer [53]. Such vulnerabilities are the same when using sscanf ( ) and operator>> (

) functions [53]. The best way to implement INT06 is through the initial rule file method where we can specify the error, the severity level, and error message. The following is the implemented rule file for INT06 into CPPCHECK project:

\batoi()\bsscanf()\boperator>>\b CERT INT06

39

warning

CERT INT06. converts the string token stored in the static array buff to a signed integer value using the wrong () function.

3.3.6 CON30-C/C++ (Clean up thread-specific storage)

Using the test_create ( ) function would create thread-specific pointer storage specified by a certain key [54]. Therefore, other threads could allocate this particular thread by calling the test_set ( ) function [54]. However, if that thread_specific storage is not duly freed, the associated memory may be leaked [54]. Hence, CERT rule CON30 is modeled to make sure that the thread-specific storage is freed properly [54]. In the following non-compliant code example, when a thread terminates, the associated memory would be leaked due to the dynamic allocation for each thread in the get_data ( ) function. This would be as a result of being associated with the global key where the call to tss_set ( ) function is in the add_data ( ) function [54].

#include #include /* Global key to the thread-specific storage */ tss_t key; enum { MAX_THREADS = 3 }; int *get_data(void) { int *arr = (int *)malloc(2 * sizeof(int)); if (arr == NULL) { return arr; /* Report error */ } arr[0] = 10; arr[1] = 42; return arr; }

40

int add_data(void) { int *data = get_data(); if (data == NULL) { return -1; /* Report error */ } if (thrd_success != tss_set(key, (void *)data)) { /* Handle error */ } return 0; } void print_data(void) { /* Get this thread's global data from key */ int *data = tss_get(key); if (data != NULL) { /* Print data */ } } int function(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ } print_data(); return 0; } int main(void) { thrd_t thread_id[MAX_THREADS]; /* Create the key before creating the threads */ if (thrd_success != tss_create(&key, NULL)) { /* Handle error */ } /* Create threads that would store specific storage */ for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_create(&thread_id[i], function, NULL)) { /* Handle error */ } } for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_join(thread_id[i], NULL)) { /* Handle error */ } } tss_delete(key); return 0; }

41

As in the following code, using the initial rule file method is an efficient way to implement CON30 CERT rule into CPPCHECK.:

\bmalloc()\b CERTCON30 warning

CERT CON30.This memory is subsequently leaked when the threads return.

3.3.7 SIG32-C/C++ (Do not call longjmo ( ) from inside signal handler)

Calling the longjmp ( ) function via a signal handler would cause undefined behavior if it results in using any of the non-asynchronous-safe functions such as signal (

) and exit ( ) functions [55]. The below noncompliant code example shows that when executing the main ( ) function loop, it would log into some data[55]. Also, once obtaining

SIGNT, the program would be transferred out of the loop, then logging the error, and eventually terminating it. Furthermore, generating a SIGINT right before the second if statement in log_message ( ) function might lead to creating some security vulnerabilities

[55]. Therefore, the function longjmp ( ) would transfer control back to the main function, where log_message ( ) function is recalled again [55].

#include #include #include

42

enum { MAXLINE = 1024 }; static jmp_buf env; void handler(int signum) { longjmp(env, 1); } void log_message(char *info1, char *info2) { static char *buf = NULL; static size_t bufsize; char buf0[MAXLINE]; if (buf == NULL) { buf = buf0; bufsize = sizeof(buf0); } /* * Try to fit a message into buf, else re-allocate * it on the heap and then log the message. */ /*** VULNERABILITY IF SIGINT RAISED HERE ***/ if (buf == buf0) { buf = NULL; } } int main(void) { if (signal(SIGINT, handler) == SIG_ERR) { /* Handle error */ } char *info1; char *info2; /* info1 and info2 are set by user input here */ if (setjmp(env) == 0) { while (1) { /* Main loop program code */ log_message(info1, info2); /* More program code */ } } else { log_message(info1, info2); } return 0; }

43

To have CPPCHECK search and detect this kind of vulnerability, we would need to implement a rule file that sends a message warning whenever longjmp ( ) is used:

\blongjmp()\b CERTSIG32 warning

CERT SIG32. buf is not set to NULL as a result of the interrupt.

3.3.8 STR32-C/C++ (Do not pass a non-null-terminated character sequence to a

library function that expects a string)

Most library functions take a string or wide string argument in which the string would be completely null-terminated. This rule indicates that a character sequence or wide character sequence, which are not null-terminated, must not be passed to such a library [56]. Otherwise, they can cause access outside the bound of the object memory [56].

In this noncompliant code example, unless the first n source array character is null, the final result would not be null-terminated [56]. Therefore, undefined behavior caused by passing a non-null-terminated character sequence may occur [56]:

44

include enum { NTBS_SIZE = 32 }; void func(const char *source) { char ntbs[NTBS_SIZE]; ntbs[sizeof(ntbs) - 1] = '\0'; strncpy(ntbs, source, sizeof(ntbs)); }

Although strncpy ( ) function would accept a string as input, a returned null-terminated value would not be guaranteed[56].

The following is the implemented rule file regarding CERT rule STR32:

\bstrncpy()\b CERTSTR32 warning

CERT STR32.ensure that null terminator is intended and present

CHAPTER 4

Future Works

4.1 Adding a Setting in CPPCHECK Library

Another modification that has high potential for CPPCHECK is adding a setting in the library (CPPCHCECK/lib/library.cpp), and then writing a new check that uses this library. We will demonstrate this idea with the following procedure:

• In the library setting, we look at lib/library.h as there is an ArgumentChecks class

that contains various settings for argument checking. Then, we add an “int”

setting with the name “minsize.”

• In the lib/library.cpp, there is a code that loads ArgumentChecks settings from

XML data. We want to make sure that the minsize setting can be loaded. So we

search for “notnull”. Then we would like to have the following syntax:

With this XML, the minsize setting should get the value 2.

• We would like to have a unit test for this setting. The tests for ArgumentChecks

are in the file test/testlibrary.cpp. (We can create a new test function or modify the

existing function_arg()).

45 46

The following is an example how function_arg() can be:

void function_arg() { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " 1-\n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata));

Library library; library.load(doc); ASSERT_EQUALS(true, library.argumentChecks["foo"][1].notuninit); ASSERT_EQUALS(true, library.argumentChecks["foo"][2].notnull); ASSERT_EQUALS(true, library.argumentChecks["foo"][3].formatstr); ASSERT_EQUALS(true, library.argumentChecks["foo"][4].strz); ASSERT_EQUALS("1-", library.argumentChecks["foo"][5].valid); ASSERT_EQUALS(true, library.argumentChecks["foo"][6].notbool); ASSERT_EQUALS(true, library.argumentChecks["foo"][6].notbool); ASSERT_EQUALS(8, library.argumentChecks["foo"][7].minsize); }

• Run the tests with “make test.”

The following are two examples of buffer overflows using third-party functions that illustrate our suggestion in terms of undefined behavior and useless conditions. Please note that in the following examples, we will need no control flow or value flow analysis.

47

Example (string copy) void func() { char buffer[5]; strcpy_s(buffer, “hello world”); }

In this example, we would add a setting in the library

(CPPCHECK/lib/library.cpp) that says the par X buffer must be larger than the string in par Y. Then, we write a new check that uses the library and catches overflows when par

Y is a constant string literal larger than the buffer X (local array).

Example (buffer size smaller than size parameter) void func () { char buffers[5] zero memory(buffer,10); }

In this example, we would add a setting in the library

(CPPCHECK/lib/library.cpp) that says the par X buffer must be larger than the par Y value. Then, we write a new check that uses the library and catches overflows when a par

X buffer (local array) is smaller than the par Y value.

CHAPTER 5

Conclusion

This thesis provides an overview of open source software project CPPCHECK as well as the static code analysis tools and mechanisms. It also addresses the two possible methods we can use to contribute to CPPCHECK. We use these methods to incorporate

CERT-secure coding standards in terms of undefined behavior and useless conditions in

CPPCHECK after setting the proper environment to execute our new rules and, as a result, get an enhanced CPPCHECK model that provides a high-quality safe–critical computer system. We suggest that future works add some necessary settings in the

CPPCHECK library that allow other libraries to use these configuration settings to make

CPPCHECK perform buffer overflow checking for any arbitrary library function.

48 49

Appendix A

1- GitHub:

GitHub is essentially a repository for open source projects that can be

downloaded from https://github.com/. GitHub is a hosting service based on

the web for the software projects enhancement purposes. It provides regular

and prime repositories services. As of May 2011, GitHub was named one of

the most popular code repository sites for open source projects [48][49]. The

important features of GitHub are cloning repositories, browsing history,

committing changes, branching code, and finally, sharing code on the GitHub

website at http://github.com/. We will not need to push anything back to

Github; we will only be retrieving the code for our own purposes. Its

implementation would be as the following:

• Download GitHub software from https://github.com/

• Create a GitHub account from GitHub.com

• Set up GitHub through a terminal (figure3):

o Username and email address (the same that associated with

GitHub account)

o Password caching

50

Github setting

• Forking the code from Github

from https://github.com/danmar/cppcheck.git to your own repository

• Cloning the code to your machine using git clone with the following

web-address: https://github.com/cnewma4/cppcheck.git

51

2- Perl Compatible Regular Expressions (PCRE):

Perl Compatible Regular Expressions (PCRE) is a library that contains a

set of functions for implementation of regular expressions pattern, which is

used for patterns matching purposes by matching the same syntax and

semantics [9]. PCRE can be downloaded from https://pcre.org/. This library

can be obtained as the following.

• Download Perl Compatible Regular Expressions (PCRE) from

http://pcre.org (figure 4)

o Get the code

o Compile and install

o Set shell

o Verify the installation

Pcre setting

52

Appendix B

//Checkother.h: /* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */

//------#ifndef checkotherH #define checkotherH //------

#include "config.h" #include "check.h" #include "settings.h" class Token; class Function; class Variable;

/// @addtogroup Checks /// @{

/** @brief Various small checks */ class CPPCHECKLIB CheckOther : public Check { public: /** @brief This constructor is used when registering the CheckClass */

53

CheckOther() : Check(myName()) { }

/** @brief This constructor is used when running checks. */ CheckOther(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { }

/** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckOther checkOther(tokenizer, settings, errorLogger);

// Checks checkOther.warningOldStylePointerCast(); checkOther.invalidPointerCast(); checkOther.checkUnsignedDivision(); checkOther.checkCharVariable(); checkOther.strPlusChar(); checkOther.checkRedundantAssignment(); checkOther.checkRedundantAssignmentInSwitch(); checkOther.checkSuspiciousCaseInSwitch(); checkOther.checkSelfAssignment(); checkOther.checkDuplicateIf(); checkOther.checkDuplicateBranch(); checkOther.checkDuplicateExpression(); checkOther.checkUnreachableCode(); checkOther.checkSuspiciousSemicolon(); checkOther.checkVariableScope(); checkOther.clarifyCondition(); // not simplified because ifAssign checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) checkOther.checkIncompleteArrayFill(); checkOther.checkSuspiciousStringCompare(); checkOther.checkVarFuncNullUB(); checkOther.checkNanInArithmeticExpression(); checkOther.checkCommaSeparatedReturn(); checkOther.FIO17(); checkOther.EXP01(); checkOther.FIO01_1(); checkOther.FIO01_2(); }

/** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings,

54

ErrorLogger *errorLogger) { CheckOther checkOther(tokenizer, settings, errorLogger);

// Checks checkOther.oppositeInnerCondition(); checkOther.clarifyCalculation(); checkOther.clarifyStatement(); checkOther.checkConstantFunctionParameter(); checkOther.checkIncompleteStatement(); checkOther.checkCastIntToCharAndBack();

checkOther.invalidFunctionUsage(); checkOther.checkZeroDivision(); checkOther.checkMathFunctions();

checkOther.redundantGetAndSetUserId(); checkOther.checkIncorrectLogicOperator(); checkOther.checkMisusedScopedObject(); checkOther.checkMemsetZeroBytes(); checkOther.checkMemsetInvalid2ndParam(); checkOther.checkIncorrectStringCompare(); checkOther.checkSwitchCaseFallThrough(); checkOther.checkAlwaysTrueOrFalseStringCompare(); checkOther.checkModuloAlwaysTrueFalse(); checkOther.checkPipeParameterSize();

checkOther.checkInvalidFree(); checkOther.checkDoubleFree(); checkOther.checkRedundantCopy(); checkOther.checkNegativeBitwiseShift(); checkOther.checkSuspiciousEqualityComparison(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); checkOther.unboundArrayWithInit();

}

/** To check the dead code in a program, which is inaccessible due to the counter- conditions check in nested-if statements **/ void oppositeInnerCondition();

/** @brief Clarify calculation for ".. a * b ? .." */ void clarifyCalculation();

/** @brief Suspicious condition (assignment+comparison) */

55

void clarifyCondition();

/** @brief Suspicious statement like '*A++;' */ void clarifyStatement();

/** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast();

/** @brief Check for pointer casts to a type with an incompatible binary data representation */ void invalidPointerCast();

/** * @brief Invalid function usage (invalid input value / overlapping data) * * %Check that given function parameters are valid according to the standard * - wrong radix given for strtol/strtoul * - overlapping data when using sprintf/snprintf * - wrong input value according to library */ void invalidFunctionUsage();

/** @brief %Check for unsigned division */ void checkUnsignedDivision();

/** @brief %Check scope of variables */ void checkVariableScope(); static bool checkInnerScope(const Token *tok, const Variable* var, bool& used);

/** @brief %Check for comma separated statements in return */ void checkCommaSeparatedReturn();

/** @brief %Check for constant function parameter */ void checkConstantFunctionParameter();

/** @brief Using char variable as array index / as operand in bit operation */ void checkCharVariable();

/** @brief Incomplete statement. A statement that only contains a constant or variable */ void checkIncompleteStatement();

/** @brief str plus char (unusual pointer arithmetic) */ void strPlusChar();

56

/** @brief %Check zero division*/ void checkZeroDivision();

/** @brief %Check zero division / useless condition */ void checkZeroDivisionOrUselessCondition();

/** @brief Check for NaN (not-a-number) in an arithmetic expression */ void checkNanInArithmeticExpression();

/** @brief %Check for parameters given to math function that do not make sense*/ void checkMathFunctions();

/** @brief % Check for seteuid(geteuid()) or setuid(getuid())*/ void redundantGetAndSetUserId();

/** @brief copying to memory or assigning to a variable twice */ void checkRedundantAssignment();

/** @brief %Check for assigning to the same variable twice in a switch statement*/ void checkRedundantAssignmentInSwitch();

/** @brief %Check for code like 'case A||B:'*/ void checkSuspiciousCaseInSwitch();

/** @brief %Check for code like 'case A||B:'*/ void checkSuspiciousEqualityComparison();

/** @brief %Check for switch case fall through without comment */ void checkSwitchCaseFallThrough();

/** @brief %Check for assigning a variable to itself*/ void checkSelfAssignment();

/** @brief %Check for testing for mutual exclusion over ||*/ void checkIncorrectLogicOperator();

/** @brief %Check for objects that are destroyed immediately */ void checkMisusedScopedObject();

/** @brief %Check for filling zero bytes with memset() */ void checkMemsetZeroBytes();

/** @brief %Check for invalid 2nd parameter of memset() */ void checkMemsetInvalid2ndParam();

57

/** @brief %Check for using bad usage of strncmp and substr */ void checkIncorrectStringCompare();

/** @brief %Check for comparison of a string literal with a char* variable */ void checkSuspiciousStringCompare();

/** @brief %Check for suspicious code where multiple if have the same expression (e.g "if (a) { } else if (a) { }") */ void checkDuplicateIf();

/** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ void checkDuplicateBranch();

/** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ void checkDuplicateExpression();

/** @brief %Check for suspicious code that compares string literals for equality */ void checkAlwaysTrueOrFalseStringCompare();

/** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ void checkModuloAlwaysTrueFalse();

/** @brief %Check for code that gets never executed, such as duplicate break statements */ void checkUnreachableCode();

/** @brief %Check for testing sign of unsigned variable */ void checkSignOfUnsignedVariable();

/** @brief %Check for suspicious use of semicolon */ void checkSuspiciousSemicolon();

/** @brief %Check for free() operations on invalid memory locations */ void checkInvalidFree(); void invalidFreeError(const Token *tok, bool inconclusive);

/** @brief %Check for double free or double close operations */ void checkDoubleFree(); void doubleFreeError(const Token *tok, const std::string &varname);

/** @brief %Check for code creating redundant copies */ void checkRedundantCopy();

58

/** @brief %Check for bitwise operation with negative right operand */ void checkNegativeBitwiseShift();

/** @brief %Check for buffers that are filled incompletely with memset and similar functions */ void checkIncompleteArrayFill();

/** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ void checkVarFuncNullUB();

/** @brief %Check that calling the POSIX pipe() system call is called with an integer array of size two. */ void checkPipeParameterSize();

/** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ void checkCastIntToCharAndBack();

/** @brief %Check for using of comparison functions evaluating always to true or false. */ void checkComparisonFunctionIsAlwaysTrueOrFalse(void);

void FIO17(void); void EXP01(void); void STR05(void); void STR30(void); void FIO01_1(void); void FIO01_2(void); private: bool isUnsigned(const Variable *var) const; static bool isSigned(const Variable *var);

// Error messages.. void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result); void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); void checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim); void oppositeInnerConditionError(const Token *tok); void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyConditionError(const Token *tok, bool assign, bool boolop); void clarifyStatementError(const Token* tok);

59

void redundantGetAndSetUserIdError(const Token *tok); void cstyleCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive); void sprintfOverlappingDataError(const Token *tok, const std::string &varname); void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr); void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); void udivError(const Token *tok, bool inconclusive); void passedByValueError(const Token *tok, const std::string &parname); void constStatementError(const Token *tok, const std::string &type); void charArrayIndexError(const Token *tok); void charBitOpError(const Token *tok); void variableScopeError(const Token *tok, const std::string &varname); void strPlusCharError(const Token *tok); void zerodivError(const Token *tok); void zerodivcondError(const Token *tokcond, const Token *tokdiv); void nanInArithmeticExpressionError(const Token *tok); void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1); void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); void redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var); void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); void switchCaseFallThrough(const Token *tok); void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); void suspiciousEqualityComparisonError(const Token* tok); void selfAssignmentError(const Token *tok, const std::string &varname); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always); void redundantConditionError(const Token *tok, const std::string &text); void misusedScopeObjectError(const Token *tok, const std::string &varname); void memsetZeroBytesError(const Token *tok, const std::string &varname); void memsetFloatError(const Token *tok, const std::string &var_value); void memsetValueOutOfRangeError(const Token *tok, const std::string &value); void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); void incorrectStringBooleanError(const Token *tok, const std::string& string);

60

void duplicateIfError(const Token *tok1, const Token *tok2); void duplicateBranchError(const Token *tok1, const Token *tok2); void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op); void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); void suspiciousStringCompareError(const Token* tok, const std::string& var); void duplicateBreakError(const Token *tok, bool inconclusive); void unreachableCodeError(const Token* tok, bool inconclusive); void unsignedLessThanZeroError(const Token *tok, const std::string &varname, bool inconclusive); void pointerLessThanZeroError(const Token *tok, bool inconclusive); void unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive); void pointerPositiveError(const Token *tok, bool inconclusive); void SuspiciousSemicolonError(const Token *tok); void doubleCloseDirError(const Token *tok, const std::string &varname); void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); void negativeBitwiseShiftError(const Token *tok); void redundantCopyError(const Token *tok, const std::string &varname); void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); void varFuncNullUBError(const Token *tok); void commaSeparatedReturnError(const Token *tok); void FIO17Error(const Token *tok); void EXP01Error(const Token *tok); void STR05Error(const Token *tok); void STR30Error(const Token *tok); void FIO01_1Error(const Token *tok); void FIO01_2Error(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckOther c(0, settings, errorLogger);

// error c.sprintfOverlappingDataError(0, "varname"); c.invalidFunctionArgError(0, "func_name", 1, "1-4"); c.invalidFunctionArgBoolError(0, "func_name", 1); c.udivError(0, false); c.zerodivError(0); c.zerodivcondError(0,0); c.mathfunctionCallError(0); c.misusedScopeObjectError(NULL, "varname"); c.doubleFreeError(0, "varname");

61

c.invalidPointerCastError(0, "float", "double", false); c.negativeBitwiseShiftError(0); c.checkPipeParameterSizeError(0, "varname", "dimension");

//performance c.redundantCopyError(0, "varname"); c.redundantCopyError(0, 0, "var"); c.redundantAssignmentError(0, 0, "var", false);

// style/warning c.checkComparisonFunctionIsAlwaysTrueOrFalseError(0,"isless","varName",false); c.checkCastIntToCharAndBackError(0,"func_name"); c.oppositeInnerConditionError(0); c.cstyleCastError(0); c.passedByValueError(0, "parametername"); c.constStatementError(0, "type"); c.charArrayIndexError(0); c.charBitOpError(0); c.variableScopeError(0, "varname"); c.strPlusCharError(0); c.redundantAssignmentInSwitchError(0, 0, "var"); c.redundantCopyInSwitchError(0, 0, "var"); c.switchCaseFallThrough(0); c.suspiciousCaseInSwitchError(0, "||"); c.suspiciousEqualityComparisonError(0); c.selfAssignmentError(0, "varname"); c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true); c.redundantConditionError(0, "If x > 10 the condition x > 11 is always true."); c.memsetZeroBytesError(0, "varname"); c.memsetFloatError(0, "varname"); c.memsetValueOutOfRangeError(0, "varname"); c.clarifyCalculationError(0, "+"); c.clarifyConditionError(0, true, false); c.clarifyStatementError(0); c.incorrectStringCompareError(0, "substr", "\"Hello World\""); c.suspiciousStringCompareError(0, "foo"); c.incorrectStringBooleanError(0, "\"Hello World\""); c.duplicateIfError(0, 0); c.duplicateBranchError(0, 0); c.duplicateExpressionError(0, 0, "&&"); c.alwaysTrueFalseStringCompareError(0, "str1", "str2"); c.alwaysTrueStringVariableCompareError(0, "varname1", "varname2"); c.duplicateBreakError(0, false); c.unreachableCodeError(0, false);

62

c.unsignedLessThanZeroError(0, "varname", false); c.unsignedPositiveError(0, "varname", false); c.pointerLessThanZeroError(0, false); c.pointerPositiveError(0, false); c.SuspiciousSemicolonError(0); c.moduloAlwaysTrueFalseError(0, "1"); c.incompleteArrayFillError(0, "buffer", "memset", false); c.varFuncNullUBError(0); c.nanInArithmeticExpressionError(0); c.commaSeparatedReturnError(0); }

static std::string myName() { return "Other"; }

std::string classInfo() const { return "Other checks\n"

// error "* Assigning bool value to pointer (converting bool value to address)\n" "* division with zero\n" "* scoped object destroyed immediately after construction\n" "* assignment in an assert statement\n" "* incorrect length arguments for 'substr' and 'strncmp'\n" "* free() or delete of an invalid memory location\n" "* double free() or double closedir()\n" "* bitwise operation with negative right operand\n" "* provide wrong dimensioned array to pipe() system command (-- std=posix)\n" "* cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" "* invalid input values for functions\n"

// warning "* either division by zero or useless condition\n" "* memset() with a value out of range as the 2nd parameter\n"

// performance "* redundant data copying for const variable\n" "* subsequent assignment or copying to a variable or buffer\n"

// portability "* memset() with a float as the 2nd parameter\n"

63

// style "* Find dead code which is inaccessible due to the counter-conditions check in nested if statements\n" "* C-style pointer cast in cpp file\n" "* casting between incompatible pointer types\n" "* redundant if\n" "* [[CheckUnsignedDivision|unsigned division]]\n" "* passing parameter by value\n" "* [[IncompleteStatement|Incomplete statement]]\n" "* [[charvar|check how signed char variables are used]]\n" "* variable scope can be limited\n" "* condition that is always true/false\n" "* unusual pointer arithmetic. For example: \"abc\" + 'd'\n" "* redundant assignment in a switch statement\n" "* redundant pre/post operation in a switch statement\n" "* redundant bitwise operation in a switch statement\n" "* redundant strcpy in a switch statement\n" "* assignment of a variable to itself\n" "* Suspicious case labels in switch()\n" "* Suspicious equality comparisons\n" "* mutual exclusion over || always evaluating to true\n" "* Comparison of values leading always to true or false\n" "* Clarify calculation with parentheses\n" "* suspicious condition (assignment+comparison)\n" "* suspicious condition (runtime comparison of string literals)\n" "* suspicious condition (string literals as boolean)\n" "* suspicious comparison of a string literal with a char* variable\n" "* duplicate break statement\n" "* unreachable code\n" "* testing if unsigned variable is negative\n" "* testing is unsigned variable is positive\n" "* Suspicious use of ; at the end of 'if/for/while' statement.\n" "* Comparisons of modulo results that are always true/false.\n" "* Array filled incompletely using memset/memcpy/memmove.\n" "* redundant get and set function of user id (--std=posix).\n" "* Passing NULL pointer to function with variable number of arguments leads to UB on some platforms.\n" "* NaN (not a number) value used in arithmetic expression.\n" "* comma in return statement (the comma can easily be misread as a semicolon).\n"; }

void unboundArrayWithInit(); void unboundArrayWithInitWarning(const Token *tok);

64

}; /// @} //------#endif // checkotherH

//checkother.cpp

/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */

65

//------#include "checkother.h" #include "mathlib.h" #include "symboldatabase.h" #include "templatesimplifier.h"

#include // fabs() #include #include // find_if() //------

// Register this check class (by creating a static instance of it) namespace { CheckOther instance; } static bool astIsFloat(const Token *tok) { if (tok->astOperand1() && astIsFloat(tok->astOperand1())) return true; if (tok->astOperand2() && astIsFloat(tok->astOperand2())) return true;

// TODO: check function calls, struct members, arrays, etc also return !tok->variable() || Token::findmatch(tok->variable()->typeStartToken(), "float|double", tok->variable()->typeEndToken()->next(), 0); } static bool isConstExpression(const Token *tok, const std::set &constFunctions) { if (!tok) return true; if (tok->isName() && tok->next()->str() == "(") { if (!tok->function() && !Token::Match(tok->previous(), ".|::") && constFunctions.find(tok->str()) == constFunctions.end()) return false; else if (tok->function() && !tok->function()->isConst) return false; } if (Token::Match(tok, "++|--")) return false; // bailout when we see ({..}) if (tok->str() == "{") return false;

66

return isConstExpression(tok->astOperand1(),constFunctions) && isConstExpression(tok->astOperand2(),constFunctions); } static bool isSameExpression(const Token *tok1, const Token *tok2, const std::set &constFunctions) { if (tok1 == nullptr && tok2 == nullptr) return true; if (tok1 == nullptr || tok2 == nullptr) return false; if (tok1->str() != tok2->str()) return false; if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) return false; if (tok1->isName() && tok1->next()->str() == "(") { if (!tok1->function() && !Token::Match(tok1->previous(), ".|::") && constFunctions.find(tok1->str()) == constFunctions.end()) return false; else if (tok1->function() && !tok1->function()->isConst) return false; } // templates/casts if ((Token::Match(tok1, "%var% <") && tok1->next()->link()) || (Token::Match(tok2, "%var% <") && tok2->next()->link())) {

// non-const template function that is not a dynamic_cast => return false if (Token::Match(tok1->next()->link(), "> (") && !(tok1->function() && tok1->function()->isConst) && tok1->str() != "dynamic_cast") return false;

// some template/cast stuff.. check that the template arguments are same const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); const Token *end1 = tok1->next()->link(); const Token *end2 = tok2->next()->link(); while (t1 && t2 && t1 != end1 && t2 != end2) { if (t1->str() != t2->str()) return false; t1 = t1->next(); t2 = t2->next(); } if (t1 != end1 || t2 != end2) return false;

67

} if (Token::Match(tok1, "++|--")) return false; if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName()) { // cast => assert that the casts are equal const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); while (t1 && t2 && t1->str() == t2->str() && (t1->isName() || t1->str() == "*")) { t1 = t1->next(); t2 = t2->next(); } if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") return false; } // bailout when we see ({..}) if (tok1->str() == "{") return false; if (!isSameExpression(tok1->astOperand1(), tok2->astOperand1(), constFunctions)) return false; if (!isSameExpression(tok1->astOperand2(), tok2->astOperand2(), constFunctions)) return false; return true; }

//------// The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. // If this return value is stored in a character variable and then compared // to compared to EOF, which is an integer, the comparison maybe be false. // // Reference: // - Ticket #160 // - http://www.cplusplus.com/reference/cstdio/fgetc/ // - http://www.cplusplus.com/reference/cstdio/getc/ // - http://www.cplusplus.com/reference/cstdio/getchar/ // - http://www.cplusplus.com/reference/cstdio/ungetc/ ... //------void CheckOther::checkCastIntToCharAndBack() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

68

const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; std::map vars; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()- >isSigned()) { vars[tok->varId()] = tok->strAt(2); } } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()- >isSigned()) { checkCastIntToCharAndBackError(tok, tok->strAt(2)); } } else if (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get (")) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()- >isSigned()) { checkCastIntToCharAndBackError(tok, "cin.get"); } } else if (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get (")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()- >isSigned()) { vars[tok->varId()] = "cin.get"; } } if (Token::Match(tok, "%var% %comp% EOF")) { if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } else if (Token::Match(tok, "EOF %comp% %var%")) { tok = tok->tokAt(2);

69

if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } } } } void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) { reportError( tok, Severity::warning, "checkCastIntToCharAndBack", "Storing "+ strFunctionName +"() return value in char variable and then comparing with EOF.\n" "When saving "+ strFunctionName +"() return value in char variable there is loss of precision. " " When "+ strFunctionName +"() returns EOF this value is truncated. Comparing the char " "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = "+ strFunctionName +"());\" " "loops forever on some /platforms and on other compilers/platforms it will stop " "when the file contains a matching character." ); }

//------//------void CheckOther::clarifyCalculation() { if (!_settings->isEnabled("style")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { // ? operator where lhs is arithmetical expression if (tok->str() != "?" || !tok->astOperand1() || !tok->astOperand1()-

70

>isArithmeticalOp() || !tok->astOperand1()->isCalculation()) continue;

// Is code clarified by parentheses already? const Token *tok2 = tok->astOperand1(); for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")" || tok2->str() == "?") break; }

if (tok2 && tok2->str() == "?") clarifyCalculationError(tok, tok->astOperand1()->str()); } } } void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'");

// recommended calculation #1 const std::string s1("'(a" + op + "b)?c:d'");

// recommended calculation #2 const std::string s2("'a" + op + "(b?c:d)'");

reportError(tok, Severity::style, "clarifyCalculation", "Clarify calculation precedence for '" + op + "' and '?'.\n" "Suspicious calculation. Please use parentheses to clarify the code. " "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'."); }

//------// Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' //------void CheckOther::clarifyCondition() { if (!_settings->isEnabled("style"))

71

return;

const bool isC = _tokenizer->isC();

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "( %var% [=&|^]")) { for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (tok2->type() == Token::eComparisonOp) { // This might be a template if (!isC && tok2->link()) break;

clarifyConditionError(tok, tok->strAt(2) == "=", false); break; } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") break; } } } }

// using boolean result in bitwise operation ! x [&|^] for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%comp%|!")) { if (tok->link()) // don't write false positives when templates are used continue;

const Token *tok2 = tok->next();

// Todo: There are false positives if '(' if encountered. It // is assumed there is something like '(char *)&..' and therefore // it bails out. if (Token::Match(tok2, "(|&")) continue;

72

while (tok2 && (tok2->isName() || tok2->isNumber() || Token::Match(tok2,".|(|["))) { if (Token::Match(tok2, "(|[")) tok2 = tok2->link(); tok2 = tok2->next(); }

if (Token::Match(tok2, "[&|^]")) { // don't write false positives when templates are used if (Token::Match(tok2, "&|* ,|>") || Token::simpleMatch(tok2->previous(), "const &")) continue;

// #3609 - CWinTraits::.. if (!isC && Token::Match(tok->previous(), "%var% <")) { const Token *tok3 = tok2; while (Token::Match(tok3, "[&|^] %var%")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, ",|>")) continue; }

clarifyConditionError(tok,false,true); } } } } } void CheckOther::clarifyConditionError(const Token *tok, bool assign, bool boolop) { std::string errmsg;

if (assign) errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses.";

else if (boolop) errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' " "and the comparison operators have higher precedence than bitwise operators. " "It is recommended that the expression is clarified with parentheses.";

73

else errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "Suspicious condition. Comparison operators have higher precedence than bitwise operators. " "Please clarify the condition with parentheses.";

reportError(tok, Severity::style, "clarifyCondition", errmsg); }

//------// Clarify (meaningless) statements like *foo++; with parentheses. //------void CheckOther::clarifyStatement() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "* %var%")) { const Token *tok2=tok->previous();

while (tok2 && tok2->str() == "*") tok2=tok2->previous();

if (Token::Match(tok2, "[{};]")) { tok = tok->tokAt(2); for (;;) { if (tok->str() == "[") tok = tok->link()->next();

if (Token::Match(tok, ".|:: %var%")) { if (tok->strAt(2) == "(") tok = tok->linkAt(2)->next(); else tok = tok->tokAt(2); } else

74

break; } if (Token::Match(tok, "++|-- [;,]")) clarifyStatementError(tok); } } } } } void CheckOther::clarifyStatementError(const Token *tok) { reportError(tok, Severity::warning, "clarifyStatement", "Ineffective statement similar to '*A++;'. Did you intend to write '(*A)++;'?\n" "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?"); }

void CheckOther::checkSuspiciousSemicolon() { if (!_settings->inconclusive || !_settings->isEnabled("warning")) return;

const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();

// Look for "if(); {}", "for(); {}" or "while(); {}" for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type == Scope::eIf || i->type == Scope::eElse || i->type == Scope::eElseIf || i->type == Scope::eWhile || i->type == Scope::eFor) { // Ensure the semicolon is at the same line number as the if/for/while statement // and the {..} block follows it without an extra empty line. if (Token::simpleMatch(i->classStart, "{ ; } {") && i->classStart->previous()->linenr() == i->classStart->tokAt(2)->linenr() && i->classStart->linenr()+1 >= i->classStart->tokAt(3)->linenr()) { SuspiciousSemicolonError(i->classDef); } } } } void CheckOther::SuspiciousSemicolonError(const Token* tok) {

75

reportError(tok, Severity::warning, "suspiciousSemicolon", "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", true); }

//------//------void CheckOther::warningOldStylePointerCast() { // Only valid on C++ code if (!_settings->isEnabled("style") || !_tokenizer->isCPP()) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const| %type% * ) (| %var%") && !Token::Match(tok, "( const| %type% * ) (| new")) continue;

if (tok->strAt(1) == "const") tok = tok->next();

if (tok->strAt(4) == "const") continue;

// Is "type" a class? const std::string pattern("class|struct " + tok->strAt(1)); if (Token::findmatch(_tokenizer->tokens(), pattern.c_str(), tok)) cstyleCastError(tok); } } void CheckOther::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting"); }

//------// float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation //------static std::string analyzeType(const Token* tok) {

76

if (tok->str() == "double") { if (tok->isLong()) return "long double"; else return "double"; } if (tok->str() == "float") return "float"; if (Token::Match(tok, "int|long|short|char|size_t")) return "integer"; return ""; } void CheckOther::invalidPointerCast() { if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability")) return;

const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { const Token* toTok = 0; const Token* nextTok = 0; // Find cast if (Token::Match(tok, "( const| %type% const| * )") || Token::Match(tok, "( const| %type% %type% const| * )")) { toTok = tok->next(); nextTok = tok->link()->next(); if (nextTok && nextTok->str() == "(") nextTok = nextTok->next(); } else if (Token::Match(tok, "reinterpret_cast < const| %type% const| * > (") || Token::Match(tok, "reinterpret_cast < const| %type% %type% const| * > (")) { nextTok = tok->tokAt(5); while (nextTok->str() != "(") nextTok = nextTok->next(); nextTok = nextTok->next(); toTok = tok->tokAt(2); } if (toTok && toTok->str() == "const") toTok = toTok->next();

77

if (!nextTok || !toTok || !toTok->isStandardType()) continue;

// Find casted variable const Variable *var = nullptr; bool allocation = false; bool ref = false; if (Token::Match(nextTok, "new %type%")) allocation = true; else if (Token::Match(nextTok, "%var% !![")) var = nextTok->variable(); else if (Token::Match(nextTok, "& %var%") && !Token::Match(nextTok- >tokAt(2), "(|[")) { var = nextTok->next()->variable(); ref = true; }

const Token* fromTok = 0;

if (allocation) { fromTok = nextTok->next(); } else { if (!var || (!ref && !var->isPointer() && !var->isArray()) || (ref && (var- >isPointer() || var->isArray()))) continue; fromTok = var->typeStartToken(); }

while (Token::Match(fromTok, "static|const")) fromTok = fromTok->next(); if (!fromTok->isStandardType()) continue;

std::string fromType = analyzeType(fromTok); std::string toType = analyzeType(toTok); if (fromType != toType && !fromType.empty() && !toType.empty() && (toType != "integer" || _settings->isEnabled("portability")) && (toTok->str() != "char" || _settings->inconclusive)) invalidPointerCastError(tok, fromType, toType, toTok->str() == "char"); } } } void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive)

78

{ if (to == "integer") { // If we cast something to int*, this can be useful to play with its binary data representation if (!inconclusive) reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to integer* is not portable due to different binary data representations on different platforms."); else reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to char* is not portable due to different binary data representations on different platforms.", true); } else reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation."); }

//------// This check detects errors on POSIX systems, when a pipe command called // with a wrong dimensioned file descriptor array. The pipe command requires // exactly an integer array of dimension two as parameter. // // References: // - http://linux.die.net/man/2/pipe // - ticket #3521 //------void CheckOther::checkPipeParameterSize() { if (!_settings->standards.posix) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "pipe ( %var% )") || Token::Match(tok, "pipe2 ( %var% ,")) { const Token * const varTok = tok->tokAt(2);

const Variable *var = varTok->variable(); MathLib::bigint dim; if (var && var->isArray() && !var->isArgument() && ((dim=var- >dimension(0U)) < 2)) { const std::string strDim = MathLib::toString(dim);

79

checkPipeParameterSizeError(varTok,varTok->str(), strDim); } } } } } void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim) { reportError(tok, Severity::error, "wrongPipeParameterSize", "Buffer '" + strVarName + "' must have size of 2 integers if used as parameter of pipe().\n" "The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers.\n" "The variable '" + strVarName + "' is an array of size " + strDim + ", which does not match."); }

//------// Detect redundant assignments: x = 0; x = 4; //------static bool nonLocal(const Variable* var) { return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var- >isReference(); } static void eraseNotLocalArg(std::map& container, const SymbolDatabase* symbolDatabase) { for (std::map::iterator i = container.begin(); i != container.end();) { const Variable* var = symbolDatabase->getVariableFromVarId(i->first); if (!var || nonLocal(var)) { container.erase(i++); if (i == container.end()) break; } else ++i; } } static void eraseMemberAssignments(const unsigned int varId, std::map

80

std::set > &membervars, std::map &varAssignments) { const std::map >::const_iterator it = membervars.find(varId); if (it != membervars.end()) { const std::set v = it->second; for (std::set::const_iterator vit = v.begin(); vit != v.end(); ++vit) { varAssignments.erase(*vit); if (*vit != varId) eraseMemberAssignments(*vit, membervars, varAssignments); } } } void CheckOther::checkRedundantAssignment() { const bool performance = _settings->isEnabled("performance"); const bool warning = _settings->isEnabled("warning"); if (!warning && !performance) return;

const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();

for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (!scope->isExecutable()) continue;

std::map varAssignments; std::map memAssignments; std::map > membervars; std::set initialized; const Token* writtenArgumentsEnd = 0;

for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok == writtenArgumentsEnd) writtenArgumentsEnd = 0;

if (tok->str() == "{" && tok->strAt(-1) != "{" && tok->strAt(-1) != "=" && tok->strAt(-4) != "case" && tok->strAt(-3) != "default") { // conditional or non- executable inner scope: Skip it and reset status tok = tok->link(); varAssignments.clear();

81

memAssignments.clear(); } else if (Token::Match(tok, "for|if|while (")) { tok = tok->linkAt(1); } else if (Token::Match(tok, "break|return|continue|throw|goto")) { varAssignments.clear(); memAssignments.clear(); } else if (tok->type() == Token::eVariable && !Token::Match(tok,"%var% (")) { // Set initialization flag if (!Token::Match(tok, "%var% [")) initialized.insert(tok->varId()); else { const Token *tok2 = tok->next(); while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); if (tok2 && tok2->str() != ";") initialized.insert(tok->varId()); }

const Token *startToken = tok; while (Token::Match(startToken, "%var%|::|.")) { startToken = startToken->previous(); if (Token::Match(startToken, "%var% . %var%")) membervars[startToken->varId()].insert(startToken->tokAt(2)->varId()); }

std::map::iterator it = varAssignments.find(tok- >varId()); if (tok->next()->isAssignmentOp() && Token::Match(startToken, "[;{}]")) { // Assignment if (it != varAssignments.end()) { bool error = true; // Ensure that variable is not used on right side for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") break; else if (tok2->varId() == tok->varId()) error = false; else if (Token::Match(tok2, "%var% (") && nonLocal(tok->variable())) { // Called function might use the variable const Function* const func = tok2->function(); const Variable* const var = tok->variable(); if (!var || var->isGlobal() || var->isReference() || ((!func || func- >nestedIn) && tok2->strAt(-1) != ".")) // Global variable, or member function error = false; } }

82

if (error) { if (scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok) && warning) redundantAssignmentInSwitchError(it->second, tok, tok->str()); else if (performance) { const bool nonlocal = nonLocal(it->second->variable()); if (_settings->inconclusive || !nonlocal) // see #5089 - report inconclusive only when requested redundantAssignmentError(it->second, tok, tok->str(), nonlocal); // Inconclusive for non-local variables } } it->second = tok; } if (!Token::simpleMatch(tok->tokAt(2), "0 ;") || (tok->variable() && tok- >variable()->nameToken() != tok->tokAt(-2))) varAssignments[tok->varId()] = tok; memAssignments.erase(tok->varId()); eraseMemberAssignments(tok->varId(), membervars, varAssignments); } else if (tok->next()->type() == Token::eIncDecOp || (tok->previous()- >type() == Token::eIncDecOp && tok->strAt(1) == ";")) { // Variable incremented/decremented; Prefix-Increment is only suspicious, if its return value is unused varAssignments[tok->varId()] = tok; memAssignments.erase(tok->varId()); eraseMemberAssignments(tok->varId(), membervars, varAssignments); } else if (!Token::simpleMatch(tok->tokAt(-2), "sizeof (")) { // Other usage of variable if (it != varAssignments.end()) varAssignments.erase(it); if (!writtenArgumentsEnd) // Indicates that we are in the first argument of strcpy/memcpy/... function memAssignments.erase(tok->varId()); } } else if (Token::Match(tok, "%var% (")) { // Function call. Global variables might be used. Reset their status const bool memfunc = Token::Match(tok, "memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy |swprintf|wcscat|wcsncat"); if (memfunc) { const Token* param1 = tok->tokAt(2); writtenArgumentsEnd = param1->next(); if (param1->varId() && param1->strAt(1) == "," && !Token::Match(tok, "strcat|strncat|wcscat|wcsncat")) { if (tok->str() == "memset" && initialized.find(param1->varId()) ==

83

initialized.end() && param1->variable() && param1->variable()->isLocal() && param1->variable()->isArray()) initialized.insert(param1->varId()); else if (memAssignments.find(param1->varId()) == memAssignments.end()) memAssignments[param1->varId()] = tok; else { const std::map::iterator it = memAssignments.find(param1->varId()); if (scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok) && warning) redundantCopyInSwitchError(it->second, tok, param1->str()); else if (performance) redundantCopyError(it->second, tok, param1->str()); } } } else if (scope->type == Scope::eSwitch) { // Avoid false positives if noreturn function is called in switch const Function* const func = tok->function(); if (!func || !func->hasBody) { varAssignments.clear(); memAssignments.clear(); continue; } const Token* funcEnd = func->functionScope->classEnd; bool noreturn; if (!_tokenizer->IsScopeNoReturn(funcEnd, &noreturn) && !noreturn) { eraseNotLocalArg(varAssignments, symbolDatabase); eraseNotLocalArg(memAssignments, symbolDatabase); } else { varAssignments.clear(); memAssignments.clear(); } } else { // Noreturn functions outside switch don't cause problems eraseNotLocalArg(varAssignments, symbolDatabase); eraseNotLocalArg(memAssignments, symbolDatabase); } } } } } void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) {

84

std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::performance, "redundantCopy", "Buffer '" + var + "' is being written before its old content has been used."); } void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::warning, "redundantCopyInSwitch", "Buffer '" + var + "' is being written before its old content has been used. 'break;' missing?"); } void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); if (inconclusive) reportError(callstack, Severity::performance, "redundantAssignment", "Variable '" + var + "' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" "Variable '" + var + "' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", true); else reportError(callstack, Severity::performance, "redundantAssignment", "Variable '" + var + "' is reassigned a value before the old one has been used."); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::warning, "redundantAssignInSwitch", "Variable '" + var + "' is reassigned a value before the old one has been used.

85

'break;' missing?"); }

//------// switch (x) // { // case 2: // y = a; // <- this assignment is redundant // case 3: // y = b; // <- case 2 falls through and sets y twice // } //------static inline bool isFunctionOrBreakPattern(const Token *tok) { if (Token::Match(tok, "%var% (") || Token::Match(tok, "break|continue|return|exit|goto|throw")) return true;

return false; } void CheckOther::checkRedundantAssignmentInSwitch() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

// Find the beginning of a switch. E.g.: // switch (var) { ... for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eSwitch || !i->classStart) continue;

// Check the contents of the switch statement std::map varsWithBitsSet; std::map bitOperations;

for (const Token *tok2 = i->classStart->next(); tok2 != i->classEnd; tok2 = tok2- >next()) { if (tok2->str() == "{") { // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.:

86

// case 3: b = 1; // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { const Token* endOfConditional = tok2->link(); for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3- >next()) { if (tok3->varId() != 0) { varsWithBitsSet.erase(tok3->varId()); bitOperations.erase(tok3->varId()); } else if (isFunctionOrBreakPattern(tok3)) { varsWithBitsSet.clear(); bitOperations.clear(); } } tok2 = endOfConditional; } }

// Variable assignment. Report an error if it's assigned to twice before a break. E.g.: // case 3: b = 1; // <== redundant // case 4: b = 2;

if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;") && tok2- >varId() != 0) { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); }

// Bitwise operation. Report an error if it's performed twice before a break. E.g.: // case 3: b |= 1; // <== redundant // case 4: b |= 1; else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %var% %or%|& %num% ;") && tok2->varId() != 0 && tok2->varId() == tok2->tokAt(2)->varId()) { std::string bitOp = tok2->strAt(3) + tok2->strAt(4); std::map::iterator i2 = varsWithBitsSet.find(tok2->varId());

// This variable has not had a bit operation performed on it yet, so just make a note of it if (i2 == varsWithBitsSet.end()) { varsWithBitsSet[tok2->varId()] = tok2; bitOperations[tok2->varId()] = bitOp;

87

}

// The same bit operation has been performed on the same variable twice, so report an error else if (bitOperations[tok2->varId()] == bitOp) redundantBitwiseOperationInSwitchError(i2->second, i2->second->str());

// A different bit operation was performed on the variable, so clear it else { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } }

// Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: // case 3: b = 1; // case 4: b++; else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); }

// Reset our record of assignments if there is a break or function call. E.g.: // case 3: b = 1; break; if (isFunctionOrBreakPattern(tok2)) { varsWithBitsSet.clear(); bitOperations.clear(); } } } } void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "redundantBitwiseOperationInSwitch", "Redundant bitwise operation on '" + varname + "' in 'switch' statement. 'break;' missing?"); }

//------//------

88

void CheckOther::checkSwitchCaseFallThrough() { if (!(_settings->isEnabled("style") && _settings->experimental)) return;

const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();

for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eSwitch || !i->classStart) // Find the beginning of a switch continue;

// Check the contents of the switch statement std::stack > ifnest; std::stack loopnest; std::stack scopenest; bool justbreak = true; bool firstcase = true; for (const Token *tok2 = i->classStart; tok2 != i->classEnd; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "if (")) { tok2 = tok2->next()->link()->next(); if (tok2->link() == nullptr) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } ifnest.push(std::make_pair(tok2->link(), false)); justbreak = false; } else if (Token::simpleMatch(tok2, "while (")) { tok2 = tok2->next()->link()->next(); // skip over "do { } while ( ) ;" case if (tok2->str() == "{") { if (tok2->link() == nullptr) { std::ostringstream errmsg; errmsg << "unmatched while in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); } justbreak = false; } else if (Token::simpleMatch(tok2, "do {")) { tok2 = tok2->next();

89

if (tok2->link() == nullptr) { std::ostringstream errmsg; errmsg << "unmatched do in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::simpleMatch(tok2, "for (")) { tok2 = tok2->next()->link()->next(); if (tok2->link() == nullptr) { std::ostringstream errmsg; errmsg << "unmatched for in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::simpleMatch(tok2, "switch (")) { // skip over nested switch, we'll come to that soon tok2 = tok2->next()->link()->next()->link(); } else if (Token::Match(tok2, "break|continue|return|exit|goto|throw")) { if (loopnest.empty()) { justbreak = true; } tok2 = Token::findsimplematch(tok2, ";"); } else if (Token::Match(tok2, "case|default")) { if (!justbreak && !firstcase) { switchCaseFallThrough(tok2); } tok2 = Token::findsimplematch(tok2, ":"); justbreak = true; firstcase = false; } else if (tok2->str() == "{") { scopenest.push(tok2->link()); } else if (tok2->str() == "}") { if (!ifnest.empty() && tok2 == ifnest.top().first) { if (tok2->next()->str() == "else") { tok2 = tok2->tokAt(2); ifnest.pop(); if (tok2->link() == nullptr) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());

90

break; } ifnest.push(std::make_pair(tok2->link(), justbreak)); justbreak = false; } else { justbreak &= ifnest.top().second; ifnest.pop(); } } else if (!loopnest.empty() && tok2 == loopnest.top()) { loopnest.pop(); } else if (!scopenest.empty() && tok2 == scopenest.top()) { scopenest.pop(); } else { if (!ifnest.empty() || !loopnest.empty() || !scopenest.empty()) { std::ostringstream errmsg; errmsg << "unexpected end of switch: "; errmsg << "ifnest=" << ifnest.size(); if (!ifnest.empty()) errmsg << "," << ifnest.top().first->linenr(); errmsg << ", loopnest=" << loopnest.size(); if (!loopnest.empty()) errmsg << "," << loopnest.top()->linenr(); errmsg << ", scopenest=" << scopenest.size(); if (!scopenest.empty()) errmsg << "," << scopenest.top()->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); } // end of switch block break; } } else if (tok2->str() != ";") { justbreak = false; }

} } } void CheckOther::switchCaseFallThrough(const Token *tok) { reportError(tok, Severity::style, "switchCaseFallThrough", "Switch falls through case without comment. 'break;' missing?"); }

91

//------// Check for statements like case A||B: in switch() //------void CheckOther::checkSuspiciousCaseInSwitch() { if (!_settings->inconclusive || !_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eSwitch) continue;

for (const Token* tok = i->classStart->next(); tok != i->classEnd; tok = tok- >next()) { if (tok->str() == "case") { const Token* end = 0; for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ":") { end = tok2; break; } if (Token::Match(tok2, "[?;}{]")) { break; } }

if (end) { const Token* finding = Token::findmatch(tok->next(), "&&|%oror%", end); if (finding) suspiciousCaseInSwitchError(tok, finding->str()); } } } } } void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) { reportError(tok, Severity::warning, "suspiciousCase",

92

"Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", true); }

//------// if (x == 1) // x == 0; // <- suspicious equality comparison. //------void CheckOther::checkSuspiciousEqualityComparison() { if (!_settings->isEnabled("warning") || !_settings->inconclusive) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {

if (Token::simpleMatch(tok, "for (")) { const Token* const openParen = tok->next(); const Token* const closeParen = tok->linkAt(1);

// Search for any suspicious equality comparison in the initialization // or increment-decrement parts of the for() loop. // For example: // for (i == 2; i < 10; i++) // or // for (i = 0; i < 10; i == a) const Token* tok2 = Token::findmatch(openParen, "[;(] %var% == %any% [;)]", closeParen); if (tok2 && (tok2 == openParen || tok2->tokAt(4) == closeParen)) { suspiciousEqualityComparisonError(tok2->tokAt(2)); }

// Equality comparisons with 0 are simplified to negation. For instance, // (x == 0) is simplified to (!x), so also check for suspicious negation // in the initialization or increment-decrement parts of the for() loop. // For example: // for (!i; i < 10; i++) const Token* tok3 = Token::findmatch(openParen, "[;(] ! %var% [;)]", closeParen); if (tok3 && (tok3 == openParen || tok3->tokAt(3) == closeParen)) { suspiciousEqualityComparisonError(tok3->tokAt(2)); }

// Skip over for() loop conditions because "for (;running==1;)"

93

// is a bit strange, but not necessarily incorrect. tok = closeParen; } else if (Token::Match(tok, "[;{}] *| %var% == %any% ;")) {

// Exclude compound statements surrounded by parentheses, such as // printf("%i\n", ({x==0;})); // because they may appear as an expression in GNU C/C++. // See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html const Token* afterStatement = tok->strAt(1) == "*" ? tok->tokAt(6) : tok- >tokAt(5); if (!Token::simpleMatch(afterStatement, "} )")) suspiciousEqualityComparisonError(tok->next()); } } } void CheckOther::suspiciousEqualityComparisonError(const Token* tok) { reportError(tok, Severity::warning, "suspiciousEqualityComparison", "Found suspicious equality comparison. Did you intend to assign a value instead?", true); }

//------// int x = 1; // x = x; // <- redundant assignment to self // // int y = y; // <- redundant initialization to self //------static bool isTypeWithoutSideEffects(const Tokenizer *tokenizer, const Variable* var) { return ((var && (!var->isClass() || var->isPointer() || var->isStlType())) || !tokenizer- >isCPP()); } static inline const Token *findSelfAssignPattern(const Token *start) { return Token::findmatch(start, "%var% = %var% ;|=|)"); } void CheckOther::checkSelfAssignment() { if (!_settings->isEnabled("warning")) return;

94

const Token *tok = findSelfAssignPattern(_tokenizer->tokens()); while (tok) { if (Token::Match(tok->previous(), "[;{}.]") && tok->varId() && tok->varId() == tok->tokAt(2)->varId() && isTypeWithoutSideEffects(_tokenizer, tok->variable())) { bool err = true;

// no false positive for 'x = x ? x : 1;' // it is simplified to 'if (x) { x=x; } else { x=1; }'. The simplification // always write all tokens on 1 line (even if the statement is several lines), so // check if the linenr is the same for all the tokens. if (Token::Match(tok->tokAt(-2), ") { %var% = %var% ; } else { %varid% =", tok->varId())) { // Find the 'if' token const Token *tokif = tok->linkAt(-2)->previous();

// find the '}' that terminates the 'else'-block const Token *else_end = tok->linkAt(6);

if (tokif && else_end && tokif->linenr() == else_end->linenr()) err = false; }

if (err) selfAssignmentError(tok, tok->str()); }

tok = findSelfAssignPattern(tok->next()); } } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "Redundant assignment of '" + varname + "' to itself."); }

//------// if ((x != 1) || (x != 3)) // expression always true // if ((x == 1) && (x == 3)) // expression always false // if ((x < 1) && (x > 3)) // expression always false // if ((x > 3) || (x < 10)) // expression always true // if ((x > 5) && (x != 1)) // second comparison always true //

95

// Check for suspect logic for an expression consisting of 2 comparison // expressions with a shared variable and constants and a logical operator // between them. // // Suggest a different logical operator when the logical operator between // the comparisons is probably wrong. // // Inform that second comparison is always true when first comparison is true. //------static std::string invertOperatorForOperandSwap(std::string s) { for (std::string::size_type i = 0; i < s.length(); i++) { if (s[i] == '>') s[i] = '<'; else if (s[i] == '<') s[i] = '>'; } return s; } static bool checkIntRelation(const std::string &op, const MathLib::bigint value1, const MathLib::bigint value2) { return (op == "==" && value1 == value2) || (op == "!=" && value1 != value2) || (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } static bool checkFloatRelation(const std::string &op, const double value1, const double value2) { return (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } template static T getvalue(const int test, const T value1, const T value2) { // test: // 1 => return value that is less than both value1 and value2

96

// 2 => return value1 // 3 => return value that is between value1 and value2 // 4 => return value2 // 5 => return value that is larger than both value1 and value2 switch (test) { case 1: { T ret = std::min(value1, value2); if ((ret - (T)1) < ret) return ret - (T)1; else if ((ret / (T)2) < ret) return ret / (T)2; else if ((ret * (T)2) < ret) return ret * (T)2; return ret; } case 2: return value1; case 3: return (value1 + value2) / (T)2; case 4: return value2; case 5: { T ret = std::max(value1, value2); if ((ret + (T)1) > ret) return ret + (T)1; else if ((ret / (T)2) > ret) return ret / (T)2; else if ((ret * (T)2) > ret) return ret * (T)2; return ret; } }; return 0; } void CheckOther::checkIncorrectLogicOperator() { bool style = _settings->isEnabled("style"); bool warning = _settings->isEnabled("warning"); if (!style && !warning) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t ii = 0; ii < functions; ++ii) {

97

const Scope * scope = symbolDatabase->functionScopes[ii];

for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "&&|%oror%")) { // Comparison #1 (LHS) const Token *comp1 = tok->astOperand1(); if (comp1 && comp1->str() == tok->str()) comp1 = comp1->astOperand2();

// Comparison #2 (RHS) const Token *comp2 = tok->astOperand2();

if (!comp1 || !comp1->isComparisonOp() || !comp1->astOperand1() || !comp1->astOperand2()) continue; if (!comp2 || !comp2->isComparisonOp() || !comp2->astOperand1() || !comp2->astOperand2()) continue;

std::string op1, value1; const Token *expr1; if (comp1->astOperand1()->isLiteral()) { op1 = invertOperatorForOperandSwap(comp1->str()); value1 = comp1->astOperand1()->str(); expr1 = comp1->astOperand2(); } else if (comp1->astOperand2()->isLiteral()) { op1 = comp1->str(); value1 = comp1->astOperand2()->str(); expr1 = comp1->astOperand1(); } else { continue; }

std::string op2, value2; const Token *expr2; if (comp2->astOperand1()->isLiteral()) { op2 = invertOperatorForOperandSwap(comp2->str()); value2 = comp2->astOperand1()->str(); expr2 = comp2->astOperand2(); } else if (comp2->astOperand2()->isLiteral()) { op2 = comp2->str(); value2 = comp2->astOperand2()->str(); expr2 = comp2->astOperand1(); } else {

98

continue; }

// Only float and int values are currently handled if (!MathLib::isInt(value1) && !MathLib::isFloat(value1)) continue; if (!MathLib::isInt(value2) && !MathLib::isFloat(value2)) continue;

const std::set constStandardFunctions; if (isSameExpression(comp1, comp2, constStandardFunctions)) continue; // same expressions => only report that there are same expressions if (!isSameExpression(expr1, expr2, constStandardFunctions)) continue;

const bool isfloat = astIsFloat(expr1) || MathLib::isFloat(value1) || astIsFloat(expr2) || MathLib::isFloat(value2);

// don't check floating point equality comparisons. that is bad // and deserves different warnings. if (isfloat && (op1=="==" || op1=="!=" || op2=="==" || op2=="!=")) continue;

// evaluate if expression is always true/false bool alwaysTrue = true, alwaysFalse = true; bool firstTrue = true, secondTrue = true; for (int test = 1; test <= 5; ++test) { // test: // 1 => testvalue is less than both value1 and value2 // 2 => testvalue is value1 // 3 => testvalue is between value1 and value2 // 4 => testvalue value2 // 5 => testvalue is larger than both value1 and value2 bool result1, result2; if (isfloat) { const double d1 = MathLib::toDoubleNumber(value1); const double d2 = MathLib::toDoubleNumber(value2); const double testvalue = getvalue(test, d1, d2); result1 = checkFloatRelation(op1, testvalue, d1); result2 = checkFloatRelation(op2, testvalue, d2); } else { const MathLib::bigint i1 = MathLib::toLongNumber(value1); const MathLib::bigint i2 = MathLib::toLongNumber(value2); const MathLib::bigint testvalue = getvalue(test, i1, i2);

99

result1 = checkIntRelation(op1, testvalue, i1); result2 = checkIntRelation(op2, testvalue, i2); } if (tok->str() == "&&") { alwaysTrue &= (result1 && result2); alwaysFalse &= !(result1 && result2); } else { alwaysTrue &= (result1 || result2); alwaysFalse &= !(result1 || result2); } firstTrue &= !(!result1 && result2); secondTrue &= !(result1 && !result2); }

const std::string cond1str = (expr1->isName() ? expr1->str() : "EXPR") + " " + op1 + " " + value1; const std::string cond2str = (expr2->isName() ? expr2->str() : "EXPR") + " " + op2 + " " + value2; if (warning && (alwaysTrue || alwaysFalse)) { const std::string text = cond1str + " " + tok->str() + " " + cond2str; incorrectLogicOperatorError(tok, text, alwaysTrue); } else if (style && secondTrue) { const std::string text = "If " + cond1str + ", the comparison " + cond2str + " is always " + (secondTrue ? "true" : "false") + "."; redundantConditionError(tok, text); } else if (style && firstTrue) { //const std::string text = "The comparison " + cond1str + " is always " + // (firstTrue ? "true" : "false") + " when " + // cond2str + "."; const std::string text = "If " + cond2str + ", the comparison " + cond1str + " is always " + (firstTrue ? "true" : "false") + "."; redundantConditionError(tok, text); } } } } } void CheckOther::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always) { if (always) reportError(tok, Severity::warning, "incorrectLogicOperator", "Logical disjunction always evaluates to true: " + condition + ".\n" "Logical disjunction always evaluates to true: " + condition + ". "

100

"Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?"); else reportError(tok, Severity::warning, "incorrectLogicOperator", "Logical conjunction always evaluates to false: " + condition + ".\n" "Logical conjunction always evaluates to false: " + condition + ". " "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?"); } void CheckOther::redundantConditionError(const Token *tok, const std::string &text) { reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text); }

//------// strtol(str, 0, radix) <- radix must be 0 or 2-36 //------void CheckOther::invalidFunctionUsage() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "%var% ( !!)")) continue; const std::string functionName = tok->str(); int argnr = 1; const Token *argtok = tok->tokAt(2); while (argtok && argtok->str() != ")") { if (Token::Match(argtok,"%num% [,)]")) { if (MathLib::isInt(argtok->str()) && !_settings->library.isargvalid(functionName, argnr, MathLib::toLongNumber(argtok->str()))) invalidFunctionArgError(argtok,functionName,argnr,_settings- >library.validarg(functionName,argnr)); } else { const Token *top = argtok; while (top->astParent() && top->astParent()->str() != "," && top- >astParent() != tok->next()) top = top->astParent(); if (top->isComparisonOp() || Token::Match(top, "%oror%|&&")) {

101

if (_settings->library.isboolargbad(functionName, argnr)) invalidFunctionArgBoolError(top, functionName, argnr);

// Are the values 0 and 1 valid? else if (!_settings->library.isargvalid(functionName, argnr, 0)) invalidFunctionArgError(top, functionName, argnr, _settings- >library.validarg(functionName,argnr)); else if (!_settings->library.isargvalid(functionName, argnr, 1)) invalidFunctionArgError(top, functionName, argnr, _settings- >library.validarg(functionName,argnr)); } } argnr++; argtok = argtok->nextArgument(); } } }

// sprintf|snprintf overlapping data for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Get variable id of target buffer.. unsigned int varid = 0;

if (Token::Match(tok, "sprintf|snprintf|swprintf ( %var% ,")) varid = tok->tokAt(2)->varId();

else if (Token::Match(tok, "sprintf|snprintf|swprintf ( %var% . %var% ,")) varid = tok->tokAt(4)->varId();

if (varid == 0) continue;

// goto "," const Token *tok2 = tok->tokAt(3); while (tok2->str() != ",") tok2 = tok2->next();

tok2 = tok2->next(); // Jump behind ","

if (tok->str() == "snprintf" || tok->str() == "swprintf") { // Jump over second parameter for snprintf and swprintf tok2 = tok2->nextArgument(); if (!tok2) continue; }

102

// is any source buffer overlapping the target buffer? do { if (Token::Match(tok2, "%varid% [,)]", varid)) { sprintfOverlappingDataError(tok2, tok2->str()); break; } } while (nullptr != (tok2 = tok2->nextArgument())); } } void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "sprintfOverlappingData", "Undefined behavior: Variable '" + varname + "' is used as parameter and destination in s[n]printf().\n" "The variable '" + varname + "' is used both as a parameter and as destination in " "s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C- library) " "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output- Functions): " "\"If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.\""); } void CheckOther::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr) { std::ostringstream errmsg; errmsg << "Invalid " << functionName << "() argument nr " << argnr; if (!tok) ; else if (tok->isNumber()) errmsg << ". The value is " << tok->str() << " but the valid values are '" << validstr << "'."; else if (tok->isComparisonOp()) errmsg << ". The value is 0 or 1 (comparison result) but the valid values are '" << validstr << "'."; reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str()); } void CheckOther::invalidFunctionArgBoolError(const Token *tok, const std::string

103

&functionName, int argnr) { std::ostringstream errmsg; errmsg << "Invalid " << functionName << "() argument nr " << argnr << ". A non- boolean value is required."; reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str()); }

//------// Find consecutive return, break, continue, goto or throw statements. e.g.: // break; break; // Detect dead code, that follows such a statement. e.g.: // return(0); foo(); //------void CheckOther::checkUnreachableCode() { if (!_settings->isEnabled("style")) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Token* secondBreak = 0; const Token* labelName = 0; if (tok->str() == "(") tok = tok->link(); else if (Token::Match(tok, "break|continue ;")) secondBreak = tok->tokAt(2); else if (Token::Match(tok, "[;{}:] return|throw")) { tok = tok->next(); // tok should point to return or throw for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); if (tok2->str() == ";") { secondBreak = tok2->next(); break; } } } else if (Token::Match(tok, "goto %any% ;")) { secondBreak = tok->tokAt(3); labelName = tok->next(); } else if (Token::Match(tok, "%var% (") && _settings->library.isnoreturn(tok- >str())) { secondBreak = tok->linkAt(1)->tokAt(2); }

// Statements follow directly, no line between them. (#3383)

104

// TODO: Try to find a better way to avoid false positives due to preprocessor configurations. bool inconclusive = secondBreak && (secondBreak->linenr()-1 > secondBreak- >previous()->linenr());

if (secondBreak && (_settings->inconclusive || !inconclusive)) { if (Token::Match(secondBreak, "continue|goto|throw") || (secondBreak->str() == "return" && (tok->str() == "return" || secondBreak- >strAt(1) == ";"))) { // return with value after statements like throw can be necessary to make a function compile duplicateBreakError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning if (tok->str() == "break") // If the previous was a break, too: Issue warning duplicateBreakError(secondBreak, inconclusive); else { if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch duplicateBreakError(secondBreak, inconclusive); } tok = Token::findmatch(secondBreak, "[}:]"); } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak- >strAt(1) != ":") { // TODO: No bailout for unconditional scopes // If the goto label is followed by a loop construct in which the label is defined it's quite likely // that the goto jump was intended to skip some code on the first loop iteration. bool labelInFollowingLoop = false; if (labelName && Token::Match(secondBreak, "while|do|for")) { const Token *scope = Token::findsimplematch(secondBreak, "{"); if (scope) { for (const Token *tokIter = scope; tokIter != scope->link() && tokIter; tokIter = tokIter->next()) { if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { labelInFollowingLoop = true; break; } } } } if (!labelInFollowingLoop) unreachableCodeError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else

105

tok = secondBreak;

if (!tok) break; } } } void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" "Consecutive return, break, continue, goto or throw statements are unnecessary. " "The second statement can never be executed, and so should be removed.", inconclusive); } void CheckOther::unreachableCodeError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "unreachableCode", "Statements following return, break, continue, goto or throw will never be executed.", inconclusive); }

//------// Check for unsigned divisions //------bool CheckOther::isUnsigned(const Variable* var) const { return (var && var->typeStartToken()->isUnsigned() && !var->isPointer() && !var- >isArray() && _tokenizer->sizeOfType(var->typeStartToken()) >= _settings- >sizeof_int); } bool CheckOther::isSigned(const Variable* var) { return (var && !var->typeStartToken()->isUnsigned() && Token::Match(var- >typeEndToken(), "int|char|short|long") && !var->isPointer() && !var->isArray()); } void CheckOther::checkUnsignedDivision() { bool warning = _settings->isEnabled("warning");

106

const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; const Token* ifTok = 0; // Check for "ivar / uvar" and "uvar / ivar" for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {

if (Token::Match(tok, "[).]")) // Don't check members or casted variables continue;

if (Token::Match(tok->next(), "%var% / %num%")) { if (tok->strAt(3)[0] == '-' && isUnsigned(tok->next()->variable())) { udivError(tok->next(), false); } } else if (Token::Match(tok->next(), "%num% / %var%")) { if (tok->strAt(1)[0] == '-' && isUnsigned(tok->tokAt(3)->variable())) { udivError(tok->next(), false); } } else if (Token::Match(tok->next(), "%var% / %var%") && _settings- >inconclusive && warning && !ifTok) { const Variable* var1 = tok->next()->variable(); const Variable* var2 = tok->tokAt(3)->variable(); if ((isUnsigned(var1) && isSigned(var2)) || (isUnsigned(var2) && isSigned(var1))) { udivError(tok->next(), true); } } else if (!ifTok && Token::simpleMatch(tok, "if (")) ifTok = tok->next()->link()->next()->link(); else if (ifTok == tok) ifTok = 0; } } } void CheckOther::udivError(const Token *tok, bool inconclusive) { if (inconclusive) reportError(tok, Severity::warning, "udivError", "Division with signed and unsigned operators. The result might be wrong.", true); else reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong."); }

107

//------// memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //------void CheckOther::checkMemsetZeroBytes() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "memset (")) { const Token* lastParamTok = tok->next()->link()->previous(); if (lastParamTok->str() == "0") memsetZeroBytesError(tok, tok->strAt(2)); } } } } void CheckOther::memsetZeroBytesError(const Token *tok, const std::string &varname) { const std::string summary("memset() called to fill 0 bytes of '" + varname + "'."); const std::string verbose(summary + " The second and third arguments might be inverted." " The function memset ( void * ptr, int value, size_t num ) sets the" " first num bytes of the block of memory pointed by ptr to the specified value."); reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose); } void CheckOther::checkMemsetInvalid2ndParam() { const bool portability = _settings->isEnabled("portability"); const bool warning = _settings->isEnabled("warning"); if (!warning && !portability) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) {

108

const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok && (tok != scope- >classEnd); tok = tok->next()) { if (Token::simpleMatch(tok, "memset (")) { const Token* firstParamTok = tok->tokAt(2); if (!firstParamTok) continue; const Token* secondParamTok = firstParamTok->nextArgument(); if (!secondParamTok) continue; const Variable* secondParamVar = secondParamTok->variable();

// Check if second parameter is a float variable or a float literal != 0.0f // TODO: Use AST with astIsFloat() for catch expressions like 'a + 1.0f' if (portability && ((secondParamVar && Token::Match(secondParamVar->typeStartToken(), "float|double")) || (secondParamTok->isNumber() && MathLib::isFloat(secondParamTok- >str()) && MathLib::toDoubleNumber(secondParamTok->str()) != 0.0 && secondParamTok->next()->str() == ","))) { memsetFloatError(secondParamTok, secondParamTok->str()); } else if (warning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range const long long int value = MathLib::toLongNumber(secondParamTok- >str()); if (value < -128 || value > 255) memsetValueOutOfRangeError(secondParamTok, secondParamTok- >str()); } } } } } void CheckOther::memsetFloatError(const Token *tok, const std::string &var_value) { const std::string message("The 2nd memset() argument '" + var_value + "' is a float, its representation is implementation defined."); const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and" " the actual representation of a floating-point value is implementation defined."); reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose); }

109

void CheckOther::memsetValueOutOfRangeError(const Token *tok, const std::string &value) { const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose); }

//------// Check scope of variables.. //------void CheckOther::checkVariableScope() { if (!_settings->isEnabled("style")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); if (!var || !var->isLocal() || (!var->isPointer() && !var->typeStartToken()- >isStandardType() && !var->typeStartToken()->next()->isStandardType())) continue;

if (var->isConst()) continue;

bool forHead = false; // Don't check variables declared in header of a for loop for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { if (tok->str() == "(") { forHead = true; break; } else if (tok->str() == "{" || tok->str() == ";" || tok->str() == "}") break; } if (forHead) continue;

const Token* tok = var->nameToken()->next(); if (Token::Match(tok, "; %varid% = %any% ;", var->declarationId())) { tok = tok->tokAt(3);

110

if (!tok->isNumber() && tok->type() != Token::eString && tok->type() != Token::eChar && !tok->isBoolean()) continue; } bool reduce = true; bool used = false; // Don't warn about unused variables for (; tok != var->scope()->classEnd; tok = tok->next()) { if (tok->str() == "{" && tok->strAt(-1) != "=") { if (used) { bool used2 = false; if (!checkInnerScope(tok, var, used2) || used2) { reduce = false; break; } } else if (!checkInnerScope(tok, var, used)) { reduce = false; break; }

tok = tok->link();

// parse else if blocks.. } else if (Token::simpleMatch(tok, "else { if (") && Token::simpleMatch(tok- >linkAt(3), ") {")) { const Token *endif = tok->linkAt(3)->linkAt(1); bool elseif = false; if (Token::simpleMatch(endif, "} }")) elseif = true; else if (Token::simpleMatch(endif, "} else {") && Token::simpleMatch(endif- >linkAt(2),"} }")) elseif = true; if (elseif && Token::findmatch(tok->next(), "%varid%", tok->linkAt(1), var- >declarationId())) reduce = false; } else if (tok->varId() == var->declarationId() || tok->str() == "goto") { reduce = false; break; } }

if (reduce && used) variableScopeError(var->nameToken(), var->name()); } }

111

bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) { const Scope* scope = tok->next()->scope(); bool loopVariable = scope->type == Scope::eFor || scope->type == Scope::eWhile || scope->type == Scope::eDo; bool noContinue = true; const Token* forHeadEnd = 0; const Token* end = tok->link(); if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok- >previous()->isName())) // Might be an unknown macro like BOOST_FOREACH loopVariable = true;

if (scope->type == Scope::eDo) { end = end->linkAt(2); } else if (loopVariable && tok->strAt(-1) == ")") { tok = tok->linkAt(-1); // Jump to opening ( of for/while statement } else if (scope->type == Scope::eSwitch) { for (std::list::const_iterator i = scope->nestedList.begin(); i != scope- >nestedList.end(); ++i) { if (used) { bool used2 = false; if (!checkInnerScope((*i)->classStart, var, used2) || used2) { return false; } } else if (!checkInnerScope((*i)->classStart, var, used)) { return false; } } }

for (; tok != end; tok = tok->next()) { if (tok->str() == "goto") return false; if (tok->str() == "continue") noContinue = false;

if (Token::simpleMatch(tok, "for (")) forHeadEnd = tok->linkAt(1); if (tok == forHeadEnd) forHeadEnd = 0;

if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var- >declarationId())) { // Assigned in outer scope. loopVariable = false;

112

unsigned int indent = 0; for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // Ensure that variable isn't used on right side of =, too if (tok2->str() == "(") indent++; else if (tok2->str() == ")") { if (indent == 0) break; indent--; } else if (tok2->str() == ";") break; else if (tok2->varId() == var->declarationId()) { loopVariable = true; break; } } }

if (loopVariable && Token::Match(tok, "%varid% !!=", var->declarationId())) // Variable used in loop return false;

if (Token::Match(tok, "& %varid%", var->declarationId())) // Taking address of variable return false;

if (Token::Match(tok, "= %varid%", var->declarationId()) && (var->isArray() || var->isPointer())) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope return false;

if (tok->varId() == var->declarationId()) { used = true; if (scope->type == Scope::eSwitch && scope == tok->scope()) return false; // Used in outer switch scope - unsafe or impossible to reduce scope } }

return true; } void CheckOther::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok,

113

Severity::style, "variableScope", "The scope of the variable '" + varname + "' can be reduced.\n" "The scope of the variable '" + varname + "' can be reduced. Warning: Be careful " "when fixing this message, especially when there are inner loops. Here is an " "example where cppcheck will write that the scope for 'i' can be reduced:\n" "void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " // it's safe to move 'int i = 0;' here\n" " for (int n = 0; n < 10; ++n) {\n" " // it is possible but not safe to move 'int i = 0;' here\n" " do_something(&i);\n" " }\n" " }\n" "}\n" "When you see this message it is always safe to reduce the variable scope 1 level."); } void CheckOther::checkCommaSeparatedReturn() { // This is experimental for now. See #5076 if (!_settings->experimental) return;

if (!_settings->isEnabled("style")) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "return") { while (tok && tok->str() != ";") { if (Token::Match(tok, "[([{<]") && tok->link()) tok = tok->link();

if (!tok->isExpandedMacro() && tok->str() == "," && tok->linenr() != tok- >next()->linenr()) commaSeparatedReturnError(tok);

tok = tok->next(); } // bailout: missing semicolon (invalid code / bad tokenizer) if (!tok)

114

break; } } } void CheckOther::commaSeparatedReturnError(const Token *tok) { reportError(tok, Severity::style, "commaSeparatedReturn", "Comma is used in return statement. The comma can easily be misread as a ';'.\n" "Comma is used in return statement. When comma is used in a return statement it can " "easily be misread as a semicolon. For example in the code below the value " "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is " "returned:\n" " if (x)\n" " return a + 1,\n" " b++;\n" "However it can be useful to use comma in macros. Cppcheck does not warn when such a " "macro is then used in a return statement, it is less likely such code is misunderstood."); }

//------// Check for constant function parameters //------void CheckOther::checkConstantFunctionParameter() { if (!_settings->isEnabled("performance") || _tokenizer->isC()) return;

const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();

for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); if (!var || !var->isArgument() || !var->isClass() || !var->isConst() || var- >isPointer() || var->isArray() || var->isReference()) continue;

const Token* const tok = var->typeStartToken(); // TODO: False negatives. This pattern only checks for string. // Investigate if there are other classes in the std

115

// namespace and add them to the pattern. There are // streams for example (however it seems strange with // const stream parameter). if (Token::Match(tok, "std :: string|wstring")) { passedByValueError(tok, var->name()); } else if (Token::Match(tok, "std :: %type% <") && !Token::simpleMatch(tok- >linkAt(3), "> ::")) { passedByValueError(tok, var->name()); } else if (var->type()) { // Check if type is a struct or class. passedByValueError(tok, var->name()); } } } void CheckOther::passedByValueError(const Token *tok, const std::string &parname) { reportError(tok, Severity::performance, "passedByValue", "Function parameter '" + parname + "' should be passed by reference.\n" "Parameter '" + parname + "' is passed by value. It could be passed " "as a (const) reference which is usually faster and recommended in C++."); }

//------// Check usage of char variables.. //------static bool isChar(const Variable* var) { return (var && !var->isPointer() && !var->isArray() && var->typeStartToken()- >str() == "char"); } static bool isSignedChar(const Variable* var) { return (isChar(var) && !var->typeStartToken()->isUnsigned()); } void CheckOther::checkCharVariable() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i];

116

for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok- >next()) { if ((tok->str() != ".") && Token::Match(tok->next(), "%var% [ %var% ]")) { const Variable* arrayvar = tok->next()->variable(); const Variable* indexvar = tok->tokAt(3)->variable(); const MathLib::bigint arraysize = (arrayvar && arrayvar->isArray()) ? arrayvar->dimension(0U) : 0; if (isSignedChar(indexvar) && arraysize > 0x80) charArrayIndexError(tok->next()); }

else if (Token::Match(tok, "[;{}] %var% = %any% [&^|] %any% ;")) { // is a char variable used in the calculation? if (!isSignedChar(tok->tokAt(3)->variable()) && !isSignedChar(tok->tokAt(5)->variable())) continue;

// it's ok with a bitwise and where the other operand is 0xff or less.. if (tok->strAt(4) == "&") { if (tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok- >strAt(3))) continue; if (tok->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok- >strAt(5))) continue; }

// is the result stored in a short|int|long? const Variable *var = tok->next()->variable(); if (var && Token::Match(var->typeStartToken(), "short|int|long") && !var- >isPointer() && !var->isArray()) charBitOpError(tok->tokAt(4)); // This is an error.. }

else if (Token::Match(tok, "[;{}] %var% = %any% [&^|] ( * %var% ) ;")) { const Variable* var = tok->tokAt(7)->variable(); if (!var || !var->isPointer() || var->typeStartToken()->str() != "char" || var- >typeStartToken()->isUnsigned()) continue; // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok->strAt(4) == "&" && tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok->strAt(3))) continue;

// is the result stored in a short|int|long?

117

var = tok->next()->variable(); if (var && Token::Match(var->typeStartToken(), "short|int|long") && !var- >isPointer() && !var->isArray()) charBitOpError(tok->tokAt(4)); // This is an error.. } } } } void CheckOther::charArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "charArrayIndex", "Signed 'char' type used as array index.\n" "Signed 'char' type used as array index. If the value " "can be greater than 127 there will be a buffer underflow " "because of sign extension."); } void CheckOther::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, "charBitOp", "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" " char c = 0x80;\n" " int i = 0 | c;\n" " if (i & 0x8000)\n" " printf(\"not expected\");\n" "The \"not expected\" will be printed on the screen."); }

//------// Incomplete statement.. //------void CheckOther::checkIncompleteStatement() { if (!_settings->isEnabled("warning")) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {

118

if (tok->str() == "(") { tok = tok->link(); if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->next()->link(), "} ;")) tok = tok->next()->link(); }

else if (Token::simpleMatch(tok, "= {")) tok = tok->next()->link();

// C++11 struct/array/etc initialization in initializer list else if (Token::Match(tok->previous(), "%var% {") && !Token::findsimplematch(tok,";",tok->link())) tok = tok->link();

// C++11 vector initialization / return { .. } else if (Token::Match(tok,"> %var% {") || Token::Match(tok, "[;{}] return {")) tok = tok->linkAt(2);

// C++11 initialize set in initalizer list : [,:] std::set{1} [{,] else if (Token::Match(tok,"> {") && tok->link()) tok = tok->next()->link();

else if (Token::Match(tok, "[;{}] %str%") || Token::Match(tok, "[;{}] %num%")) { // No warning if numeric constant is followed by a "." or "," if (Token::Match(tok->next(), "%num% [,.]")) continue;

// bailout if there is a "? :" in this statement bool bailout = false; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "?") bailout = true; else if (tok2->str() == ";") break; } if (bailout) continue;

constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string"); } } }

119

void CheckOther::constStatementError(const Token *tok, const std::string &type) { reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant."); }

//------// str plus char //------void CheckOther::strPlusChar() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "[=(] %str% + %any%")) { // char constant.. if (tok->tokAt(3)->type() == Token::eChar) strPlusCharError(tok->next());

// char variable.. if (isChar(tok->tokAt(3)->variable())) strPlusCharError(tok->next()); } } } } void CheckOther::strPlusCharError(const Token *tok) { reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type 'char' is added to a string literal."); }

//------//------void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") && MathLib::isInt(tok->strAt(4)) && MathLib::toLongNumber(tok->strAt(4)) == 0L) {

120

if (tok->str() == "div") { if (tok->strAt(-1) == ".") continue; if (tok->variable() || tok->function()) continue; } zerodivError(tok); } else if (Token::Match(tok, "[/%]") && tok->astOperand2() && !tok- >astOperand2()->values.empty()) { // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); if (value) { if (value->condition == nullptr) zerodivError(tok); else if (_settings->isEnabled("warning")) zerodivcondError(value->condition,tok); } } } } void CheckOther::zerodivError(const Token *tok) { reportError(tok, Severity::error, "zerodiv", "Division by zero."); } void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv) { std::list callstack; while (Token::Match(tokcond, "(|%oror%|&&")) tokcond = tokcond->next(); if (tokcond && tokdiv) { callstack.push_back(tokcond); callstack.push_back(tokdiv); } std::string condition; if (!tokcond) { // getErrorMessages } else if (Token::Match(tokcond, "%num% <|<=")) { condition = tokcond->strAt(2) + ((tokcond->strAt(1) == "<") ? ">" : ">=") + tokcond->str(); } else if (tokcond->isComparisonOp()) { condition = tokcond->expressionString(); } else { if (tokcond->str() == "!")

121

condition = tokcond->next()->str() + "==0"; else condition = tokcond->str() + "!=0"; } const std::string linenr(MathLib::toString(tokdiv ? tokdiv->linenr() : 0)); reportError(callstack, Severity::warning, "zerodivcond", "Either the condition '"+condition+"' is useless or there is division by zero at line " + linenr + "."); } //------//------

/** @brief Check for NaN (not-a-number) in an arithmetic expression * @note e.g. double d = 1.0 / 0.0 + 100.0; */ void CheckOther::checkNanInArithmeticExpression() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "inf.0 +|-") || Token::Match(tok, "+|- inf.0") || Token::Match(tok, "+|- %num% / 0.0")) { nanInArithmeticExpressionError(tok); } } } void CheckOther::nanInArithmeticExpressionError(const Token *tok) { reportError(tok, Severity::style, "nanInArithmeticExpression", "Using NaN/Inf in a computation.\n" "Using NaN/Inf in a computation. " "Although nothing bad really happens, it is suspicious."); }

//------//------void CheckOther::checkMathFunctions() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->varId()) continue;

122

if (Token::Match(tok, "log|logf|logl|log10|log10f|log10l ( %num% )")) { bool isNegative = MathLib::isNegative(tok->strAt(2)); bool isInt = MathLib::isInt(tok->strAt(2)); bool isFloat = MathLib::isFloat(tok->strAt(2)); if (isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { mathfunctionCallError(tok); // case log(-2) } else if (isNegative && isFloat && MathLib::toDoubleNumber(tok->strAt(2)) <= 0.) { mathfunctionCallError(tok); // case log(-2.0) } else if (!isNegative && isFloat && MathLib::toDoubleNumber(tok- >strAt(2)) <= 0.) { mathfunctionCallError(tok); // case log(0.0) } else if (!isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { mathfunctionCallError(tok); // case log(0) } }

// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond else if (Token::Match(tok, "acos|acosl|acosf|asin|asinf|asinl ( %num% )") && std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0) { mathfunctionCallError(tok); } // sqrt( x ): if x is negative the result is undefined else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") && MathLib::isNegative(tok->strAt(2))) { mathfunctionCallError(tok); } // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )") && MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) { mathfunctionCallError(tok, 2); } // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). else if (Token::Match(tok, "fmod|fmodf|fmodl ( %any%")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg- >str())) mathfunctionCallError(tok, 2); } // pow ( x , y) If x is zero, and y is negative --> division by zero else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )") &&

123

MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) { mathfunctionCallError(tok, 2); } } } } void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok- >strAt(2) + " to " + tok->str() + "() leads to undefined result."); else if (numParam == 2) reportError(tok, Severity::error, "wrongmathcall", "Passing values " + tok- >strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to undefined result."); } else reportError(tok, Severity::error, "wrongmathcall", "Passing value '#' to #() leads to undefined result."); } //------//------void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files if (_tokenizer->isC()) { return; }

const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% (") && Token::Match(tok->linkAt(2), ") ; !!}") && symbolDatabase->isClassOrStruct(tok->next()->str()) && (!tok->next()->function() || // is not a function on this scope (tok->next()->function() && tok->next()->function()->isConstructor()))) { // or is function in this scope and it's a ctor tok = tok->next();

124

misusedScopeObjectError(tok, tok->str()); tok = tok->next(); } } } } void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately."); }

//------//------void CheckOther::checkIncorrectStringCompare() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { // skip "assert(str && ..)" and "assert(.. && str)" if (Token::Match(tok, "%var% (") && (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()- >link()->tokAt(-2), "&& %str% )")) && (tok->str().find("assert")+6U==tok->str().size() || tok- >str().find("ASSERT")+6U==tok->str().size())) tok = tok->next()->link();

if (Token::simpleMatch(tok, ". substr (") && Token::Match(tok->tokAt(3)- >nextArgument(), "%num% )")) { MathLib::bigint clen = MathLib::toLongNumber(tok->linkAt(2)->strAt(-1)); const Token* begin = tok->previous(); for (;;) { // Find start of statement while (begin->link() && Token::Match(begin, "]|)|>")) begin = begin->link()->previous(); if (Token::Match(begin->previous(), ".|::")) begin = begin->tokAt(-2);

125

else break; } begin = begin->previous(); const Token* end = tok->linkAt(2)->next(); if (Token::Match(begin->previous(), "%str% ==|!=") && begin->strAt(-2) != "+") { std::size_t slen = Token::getStrLength(begin->previous()); if (clen != (int)slen) { incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); } } else if (Token::Match(end, "==|!= %str% !!+")) { std::size_t slen = Token::getStrLength(end->next()); if (clen != (int)slen) { incorrectStringCompareError(tok->next(), "substr", end->strAt(1)); } } } else if (Token::Match(tok, "&&|%oror%|( %str% &&|%oror%|)") && !Token::Match(tok, "( %str% )")) { incorrectStringBooleanError(tok->next(), tok->strAt(1)); } else if (Token::Match(tok, "if|while ( %str% )")) { incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2)); } } } } void CheckOther::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) { reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "()."); } void CheckOther::incorrectStringBooleanError(const Token *tok, const std::string& string) { reportError(tok, Severity::warning, "incorrectStringBooleanError", "Conversion of string literal " + string + " to bool always evaluates to true."); }

//------// check for duplicate expressions in if statements // if (a) { } else if (a) { } //------

126

static bool expressionHasSideEffects(const Token *first, const Token *last) { for (const Token *tok = first; tok != last->next(); tok = tok->next()) { // check for assignment if (tok->isAssignmentOp()) return true;

// check for inc/dec else if (tok->type() == Token::eIncDecOp) return true;

// check for function call else if (Token::Match(tok, "%var% (") && !(Token::Match(tok, "c_str|string") || tok->isStandardType())) return true; }

return false; } void CheckOther::checkDuplicateIf() { if (!_settings->isEnabled("style")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { const Token* const tok = scope->classDef; // only check if statements if (scope->type != Scope::eIf || !tok) continue;

std::map expressionMap;

// get the expression from the token stream std::string expression = tok->tokAt(2)->stringifyList(tok->next()->link());

// save the expression and its location expressionMap.insert(std::make_pair(expression, tok));

// find the next else if (...) statement const Token *tok1 = scope->classEnd;

127

// check all the else if (...) statements while ((Token::simpleMatch(tok1, "} else if (") && Token::simpleMatch(tok1->linkAt(3), ") {")) || (Token::simpleMatch(tok1, "} else { if (") && Token::simpleMatch(tok1->linkAt(4), ") {"))) { int conditionIndex=(tok1->strAt(3)=="(") ? 3 : 4; // get the expression from the token stream expression = tok1->tokAt(conditionIndex+1)->stringifyList(tok1- >linkAt(conditionIndex));

// try to look up the expression to check for duplicates std::map::iterator it = expressionMap.find(expression);

// found a duplicate if (it != expressionMap.end()) { // check for expressions that have side effects and ignore them if (!expressionHasSideEffects(tok1->tokAt(conditionIndex+1), tok1- >linkAt(conditionIndex)->previous())) duplicateIfError(it->second, tok1->next()); }

// not a duplicate expression so save it and its location else expressionMap.insert(std::make_pair(expression, tok1->next()));

// find the next else if (...) statement tok1 = tok1->linkAt(conditionIndex)->next()->link(); } } } void CheckOther::duplicateIfError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1);

reportError(toks, Severity::style, "duplicateIf", "Duplicate conditions in 'if' and related 'else if'.\n" "Duplicate conditions in 'if' and related 'else if'. This is suspicious and might indicate " "a cut and paste or logic error. Please examine this code carefully to determine "

128

"if it is correct."); }

//------// check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //------void CheckOther::checkDuplicateBranch() { // This is inconclusive since in practice most warnings are noise: // * There can be unfixed low-priority todos. The code is fine as it // is but it could be possible to enhance it. Writing a warning // here is noise since the code is fine (see cppcheck, abiword, ..) // * There can be overspecified code so some conditions can't be true // and their conditional code is a duplicate of the condition that // is always true just in case it would be false. See for instance // abiword. if (!_settings->isEnabled("style") || !_settings->inconclusive) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

std::list::const_iterator scope;

for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase- >scopeList.end(); ++scope) { if (scope->type != Scope::eIf && scope->type != Scope::eElseIf) continue;

// check all the code in the function for if (..) else if (Token::simpleMatch(scope->classEnd, "} else {")) { // Make sure there are no macros (different macros might be expanded // to the same code) bool macro = false; for (const Token *tok = scope->classStart; tok != scope->classEnd->linkAt(2); tok = tok->next()) { if (tok->isExpandedMacro()) { macro = true; break; } } if (macro) continue;

// save if branch code

129

std::string branch1 = scope->classStart->next()->stringifyList(scope- >classEnd);

// save else branch code std::string branch2 = scope->classEnd->tokAt(3)->stringifyList(scope- >classEnd->linkAt(2));

// check for duplicates if (branch1 == branch2) duplicateBranchError(scope->classDef, scope->classEnd->next()); } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1);

reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" "Finding the same code in an 'if' and related 'else' branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct.", true); }

//------// Check for a free() of an invalid address // char* p = malloc(100); // free(p + 10); //------void CheckOther::checkInvalidFree() { std::map allocatedVariables; for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) {

// Keep track of which variables were assigned addresses to newly-allocated memory if (Token::Match(tok, "%var% = malloc|g_malloc|new")) { allocatedVariables.insert(std::make_pair(tok->varId(), false)); }

// If a previously-allocated pointer is incremented or decremented, any subsequent

130

// free involving pointer arithmetic may or may not be invalid, so we should only // report an inconclusive result. else if (Token::Match(tok, "%var% = %var% +|-") && tok->varId() == tok->tokAt(2)->varId() && allocatedVariables.find(tok->varId()) != allocatedVariables.end()) { if (_settings->inconclusive) allocatedVariables[tok->varId()] = true; else allocatedVariables.erase(tok->varId()); }

// If a previously-allocated pointer is assigned a completely new value, // we can't know if any subsequent free() on that pointer is valid or not. else if (Token::Match(tok, "%var% = ")) { allocatedVariables.erase(tok->varId()); }

// If a variable that was previously assigned a newly-allocated memory location is // added or subtracted from when used to free the memory, report an error. else if (Token::Match(tok, "free|g_free|delete ( %any% +|- %any%") || Token::Match(tok, "delete [ ] ( %any% +|- %any%") || Token::Match(tok, "delete %any% +|- %any%")) {

const int varIdx = tok->strAt(1) == "(" ? 2 : tok->strAt(3) == "(" ? 4 : 1; const unsigned int var1 = tok->tokAt(varIdx)->varId(); const unsigned int var2 = tok->tokAt(varIdx + 2)->varId(); const std::map::iterator alloc1 = allocatedVariables.find(var1); const std::map::iterator alloc2 = allocatedVariables.find(var2); if (alloc1 != allocatedVariables.end()) { invalidFreeError(tok, alloc1->second); } else if (alloc2 != allocatedVariables.end()) { invalidFreeError(tok, alloc2->second); } }

// If the previously-allocated variable is passed in to another function // as a parameter, it might be modified, so we shouldn't report an error // if it is later used to free memory else if (Token::Match(tok, "%var% (")) { const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); while (tok2 != nullptr) { allocatedVariables.erase(tok2->varId());

131

tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); } } } } void CheckOther::invalidFreeError(const Token *tok, bool inconclusive) { reportError(tok, Severity::error, "invalidFree", "Invalid memory address freed.", inconclusive); }

//------// Check for double free // free(p); free(p); //------void CheckOther::checkDoubleFree() { std::set freedVariables; std::set closeDirVariables;

for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Keep track of any variables passed to "free()", "g_free()" or "closedir()", // and report an error if the same variable is passed twice. if (Token::Match(tok, "free|g_free|closedir ( %var% )")) { unsigned int var = tok->tokAt(2)->varId(); if (var) { if (Token::Match(tok, "free|g_free")) { if (freedVariables.find(var) != freedVariables.end()) doubleFreeError(tok, tok->strAt(2)); else freedVariables.insert(var); } else if (tok->str() == "closedir") { if (closeDirVariables.find(var) != closeDirVariables.end()) doubleCloseDirError(tok, tok->strAt(2)); else closeDirVariables.insert(var); } } }

// Keep track of any variables operated on by "delete" or "delete[]" // and report an error if the same variable is delete'd twice. else if (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ]

132

%var% ;")) { int varIdx = (tok->strAt(1) == "[") ? 3 : 1; unsigned int var = tok->tokAt(varIdx)->varId(); if (var) { if (freedVariables.find(var) != freedVariables.end()) doubleFreeError(tok, tok->strAt(varIdx)); else freedVariables.insert(var); } }

// If this scope doesn't return, clear the set of previously freed variables else if (tok->str() == "}" && _tokenizer->IsScopeNoReturn(tok)) { freedVariables.clear(); closeDirVariables.clear(); }

// If this scope is a "for" or "while" loop that contains "break" or "continue", // give up on trying to figure out the flow of execution and just clear the set // of previously freed variables. // TODO: There are false negatives. This bailout is only needed when the // loop will exit without free()'ing the memory on the last iteration. else if (tok->str() == "}" && tok->link() && tok->link()->previous() && tok->link()->linkAt(-1) && Token::Match(tok->link()->linkAt(-1)->previous(), "while|for") && Token::findmatch(tok->link()->linkAt(-1), "break|continue ;", tok) != nullptr) { freedVariables.clear(); closeDirVariables.clear(); }

// If a variable is passed to a function, remove it from the set of previously freed variables else if (Token::Match(tok, "%var% (") && !Token::Match(tok, "printf|sprintf|snprintf|fprintf|wprintf|swprintf|fwprintf")) {

// If this is a new function definition, clear all variables if (Token::simpleMatch(tok->next()->link(), ") {")) { freedVariables.clear(); closeDirVariables.clear(); } // If it is a function call, then clear those variables in its argument list else if (Token::simpleMatch(tok->next()->link(), ") ;")) { for (const Token* tok2 = tok->tokAt(2); tok2 != tok->linkAt(1); tok2 = tok2- >next()) {

133

if (tok2->varId()) { unsigned int var = tok2->varId(); freedVariables.erase(var); closeDirVariables.erase(var); } } } }

// If a pointer is assigned a new value, remove it from the set of previously freed variables else if (Token::Match(tok, "%var% =")) { unsigned int var = tok->varId(); if (var) { freedVariables.erase(var); closeDirVariables.erase(var); } }

// Any control statements in-between delete, free() or closedir() statements // makes it unclear whether any subsequent statements would be redundant. if (Token::Match(tok, "if|else|for|while|break|continue|goto|return|throw|switch")) { freedVariables.clear(); closeDirVariables.clear(); } } } void CheckOther::doubleFreeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname +"' is freed twice."); } void CheckOther::doubleCloseDirError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "doubleCloseDir", "Directory handle '" + varname +"' closed twice."); } namespace { bool notconst(const Function* func) { return !func->isConst; }

134

void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list &constFunctions) { std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase- >scopeList.end(); ++scope) { std::list::const_iterator func; // only add const functions that do not have a non-const overloaded version // since it is pretty much impossible to tell which is being called. std::map > StringFunctionMap; StringFunctionMap functionsByName; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { functionsByName[func->tokenDef->str()].push_back(&*func); } for (StringFunctionMap::iterator it = functionsByName.begin(); it != functionsByName.end(); ++it) { std::list::const_iterator nc = std::find_if(it- >second.begin(), it->second.end(), notconst); if (nc == it->second.end()) { // ok to add all of them constFunctions.splice(constFunctions.end(), it->second); } } } } }

//------// check for the same expression on both sides of an operator // (x == x), (x && x), (x || x) // (x.y == x.y), (x.y && x.y), (x.y || x.y) //------void CheckOther::checkDuplicateExpression() { if (!_settings->isEnabled("style")) return;

// Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

std::list::const_iterator scope; std::list constFunctions; getConstFunctions(symbolDatabase, constFunctions);

135

for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase- >scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue;

std::set constStandardFunctions; constStandardFunctions.insert("strcmp");

// Experimental implementation // TODO: check for duplicate separated expressions: (a==1 || a==2 || a==1) for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|- |*|/|%|=|<<|>>")) { if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1())) continue; if (isSameExpression(tok->astOperand1(), tok->astOperand2(), constStandardFunctions)) duplicateExpressionError(tok, tok, tok->str()); else if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(tok->astOperand2(), tok->astOperand1()->astOperand2(), constStandardFunctions)) duplicateExpressionError(tok->astOperand2(), tok->astOperand2(), tok- >str()); else if (tok->astOperand2()) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { if (isSameExpression(ast1->astOperand1(), tok->astOperand2(), constStandardFunctions)) duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok->str()); else if (isSameExpression(ast1->astOperand2(), tok->astOperand2(), constStandardFunctions)) duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok->str()); if (!isConstExpression(ast1->astOperand2(), constStandardFunctions)) break; ast1 = ast1->astOperand1(); } } } } }

136

} void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op) { std::list toks; toks.push_back(tok2); toks.push_back(tok1);

reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n" "Finding the same expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct."); }

//------// Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //------void CheckOther::checkAlwaysTrueOrFalseStringCompare() { if (!_settings->isEnabled("warning")) return;

const Token *tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, "strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %str% , %str% ")) != nullptr) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); }

tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, "QString :: compare ( %str% , %str% )")) != nullptr) { const std::string &str1 = tok->strAt(4); const std::string &str2 = tok->strAt(6); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(7); }

137

tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, "strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %var% , %var% ")) != nullptr) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); if (str1 == str2) alwaysTrueStringVariableCompareError(tok, str1, str2); tok = tok->tokAt(5); }

tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, "!!+ %str% ==|!= %str% !!+")) != nullptr) { const std::string &str1 = tok->strAt(1); const std::string &str2 = tok->strAt(3); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } } void CheckOther::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) { const std::size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + "..");

reportError(tok, Severity::warning, "staticStringCompare", "Unnecessary comparison of static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are always " + (str1==str2?"identical":"unequal") + ". " "Therefore the comparison is unnecessary and looks suspicious."); } void CheckOther::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) { reportError(tok, Severity::warning, "stringCompare", "Comparison of identical string variables.\n" "The compared strings, '" + str1 + "' and '" + str2 + "', are identical. " "This could be a logic bug."); }

138

//------//------void CheckOther::checkSuspiciousStringCompare() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->next()->type() != Token::eComparisonOp) continue;

const Token* varTok = tok; const Token* litTok = tok->tokAt(2);

if (varTok->strAt(-1) == "+" || litTok->strAt(1) == "+") continue;

if ((varTok->type() == Token::eString || varTok->type() == Token::eVariable) && (litTok->type() == Token::eString || litTok->type() == Token::eVariable) && litTok->type() != varTok->type()) { if (varTok->type() == Token::eString) std::swap(varTok, litTok);

const Variable *var = varTok->variable(); if (var) { if (_tokenizer->isC() || (var->isPointer() && varTok->strAt(-1) != "*" && !Token::Match(varTok->next(), "[.([]"))) suspiciousStringCompareError(tok, var->name()); } } } } } void CheckOther::suspiciousStringCompareError(const Token* tok, const std::string& var) {

139

reportError(tok, Severity::warning, "literalWithCharPtrCompare", "String literal compared with variable '" + var + "'. Did you intend to use strcmp() instead?"); }

//------// Check is a comparison of two variables leads to condition, which is // always true or false. // For instance: int a = 1; if(isless(a,a)){...} // In this case isless(a,a) evaluates always to false. // // Reference: // - http://www.cplusplus.com/reference/cmath/ //------void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse(void) { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) { const std::string functionName = tok->str(); // store function name const std::string varNameLeft = tok->tokAt(2)->str(); // get the left variable name const unsigned int varidLeft = tok->tokAt(2)->varId();// get the left varid const unsigned int varidRight = tok->tokAt(4)->varId();// get the right varid // compare varids: if they are not zero but equal // --> the comparison function is calles with the same variables if (varidLeft != 0 && varidLeft == varidRight) { if (functionName == "isgreater" || functionName == "isless" || functionName == "islessgreater") { // e.g.: isgreater(x,x) --> (x)>(x) --> false checkComparisonFunctionIsAlwaysTrueOrFalseError(tok,functionName,varNameLeft,fa lse); } else { // functionName == "isgreaterequal" || functionName == "islessequal" // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true

140

checkComparisonFunctionIsAlwaysTrueOrFalseError(tok,functionName,varNameLeft,tr ue); } } } } } } void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) { const std::string strResult = result ? "true" : "false"; reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", "Comparison of two identical variables with "+functionName+"("+varName+","+varName+") evaluates always to "+strResult+".\n" "The function "+functionName+" is designed to compare two variables. Calling this function with one variable ("+varName+") " "for both parameters leads to a statement which is always "+strResult+"."); }

//------//------void CheckOther::checkModuloAlwaysTrueFalse() { if (!_settings->isEnabled("warning")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if ((Token::Match(tok, "% %num% %comp% %num%")) && (!tok->tokAt(4) || !tok->tokAt(4)->isArithmeticalOp())) { if (MathLib::isLessEqual(tok->strAt(1), tok->strAt(3))) moduloAlwaysTrueFalseError(tok, tok->strAt(1)); } } } } void CheckOther::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) {

141

reportError(tok, Severity::warning, "moduloAlwaysTrueFalse", "Comparison of modulo result is predetermined, because it is always less than " + maxVal + "."); }

//------// Check for code like: // seteuid(geteuid()) or setuid(getuid()), which first gets and then sets the // (effective) user id to itself. Very often this indicates a copy and paste // error. //------void CheckOther::redundantGetAndSetUserId() { if (_settings->isEnabled("warning") && _settings->standards.posix) {

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "setuid ( getuid ( ) )") || Token::simpleMatch(tok, "seteuid ( geteuid ( ) )") || Token::simpleMatch(tok, "setgid ( getgid ( ) )") || Token::simpleMatch(tok, "setegid ( getegid ( ) )")) { redundantGetAndSetUserIdError(tok); } } } } void CheckOther::redundantGetAndSetUserIdError(const Token *tok) { reportError(tok, Severity::warning, "redundantGetAndSetUserId", "Redundant get and set of user id.\n" "Redundant statement without any effect. First the user id is retrieved" "by get(e)uid() and then set with set(e)uid().", false); }

//------// Check testing sign of unsigned variables and pointers. //------void CheckOther::checkSignOfUnsignedVariable() { if (!_settings->isEnabled("style")) return;

const bool inconclusive = _tokenizer->codeWithTemplates(); if (inconclusive && !_settings->inconclusive)

142

return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // check all the code in the function for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% <|<= 0") && tok->varId() && !Token::Match(tok->tokAt(3), "+|-")) { // TODO: handle a[10].b , a::b , (unsigned int)x , etc const Token *prev = tok->previous(); while (prev && (prev->isName() || prev->str() == ".")) prev = prev->previous(); if (!Token::Match(prev, "(|&&|%oror%")) continue; const Variable *var = tok->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZeroError(tok, var->name(), inconclusive); else if (var && (var->isPointer() || var->isArray())) pointerLessThanZeroError(tok, inconclusive); } else if (Token::Match(tok, "0 >|>= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|- |<<|>>|~")) { const Variable *var = tok->tokAt(2)->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZeroError(tok, var->name(), inconclusive); else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[(]")) pointerLessThanZeroError(tok, inconclusive); } else if (Token::Match(tok, "0 <= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|- |<<|>>|~")) { const Variable *var = tok->tokAt(2)->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedPositiveError(tok, var->name(), inconclusive); else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[]")) pointerPositiveError(tok, inconclusive); } else if (Token::Match(tok, "%var% >= 0") && tok->varId() && !Token::Match(tok->previous(), "++|--|)|+|-|*|/|~|<<|>>") && !Token::Match(tok- >tokAt(3), "+|-")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedPositiveError(tok, var->name(), inconclusive);

143

else if (var && var->isPointer() && tok->strAt(-1) != "*") pointerPositiveError(tok, inconclusive); } } } } void CheckOther::unsignedLessThanZeroError(const Token *tok, const std::string &varname, bool inconclusive) { if (inconclusive) { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero. This might be a false warning.\n" "Checking if unsigned variable '" + varname + "' is less than zero. An unsigned " "variable will never be negative so it is either pointless or an error to check if it is. " "It's not known if the used constant is a template parameter or not and therefore " "this message might be a false warning.", true); } else { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero.\n" "The unsigned variable '" + varname + "' will never be negative so it " "is either pointless or an error to check if it is."); } } void CheckOther::pointerLessThanZeroError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "pointerLessThanZero", "A pointer can not be negative so it is either pointless or an error to check if it is.", inconclusive); } void CheckOther::unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive) { if (inconclusive) { reportError(tok, Severity::style, "unsignedPositive", "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.\n" "The unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it. "

144

"It's not known if the used constant is a " "template parameter or not and therefore this message might be a false warning", true); } else { reportError(tok, Severity::style, "unsignedPositive", "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it."); } } void CheckOther::pointerPositiveError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "pointerPositive", "A pointer can not be negative so it is either pointless or an error to check if it is not.", inconclusive); }

/* check if a constructor in given class scope takes a reference */ static bool constructorTakesReference(const Scope * const classScope) { for (std::list::const_iterator func = classScope->functionList.begin(); func != classScope->functionList.end(); ++func) { if (func->isConstructor()) { const Function &constructor = *func; for (std::size_t argnr = 0U; argnr < constructor.argCount(); argnr++) { if (constructor.getArgumentVar(argnr)->isReference()) return true; } } } return false; }

/* This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". In most scenarios, "const A & a = getA()" will be more efficient. */ void CheckOther::checkRedundantCopy() { if (!_settings->isEnabled("performance") || _tokenizer->isC()) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

145

for (std::size_t i = 0; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i);

if (!var || var->isReference() || !var->isConst() || var->isPointer() || !var->type()) // bailout if var is of standard type, if it is a pointer or non-const continue;

const Token* startTok = var->nameToken(); if (startTok->strAt(1) == "=") // %type% %var% = ... ; ; else if (startTok->strAt(1) == "(" && var->isClass() && var->typeScope()) { // Object is instantiated. Warn if constructor takes arguments by value. if (constructorTakesReference(var->typeScope())) continue; } else continue;

const Token* tok = startTok->tokAt(2); while (tok && Token::Match(tok, "%var% .|::")) tok = tok->tokAt(2); if (!Token::Match(tok, "%var% (")) continue; if (!Token::Match(tok->linkAt(1), ") )| ;")) // bailout for usage like "const A a = getA()+3" continue;

const Function* func = tok->function(); if (func && func->tokenDef->strAt(-1) == "&") { redundantCopyError(startTok, startTok->str()); } } } void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) { reportError(tok, Severity::performance, "redundantCopyLocalConst", "Use const reference for '" + varname + "' to avoid unnecessary data copying.\n" "The const variable '"+varname+"' is assigned a copy of the data. You can avoid " "the unnecessary data copying by converting '" + varname + "' to const reference."); }

//------// Checking for shift by negative values

146

//------void CheckOther::checkNegativeBitwiseShift() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {

if ((Token::Match(tok,"%var% >>|<< %num%") || Token::Match(tok,"%num% >>|<< %num%")) && !Token::Match(tok->previous(),">>|<<")) { if (tok->isName()) { const Variable *var = tok->variable(); if (var && var->typeStartToken()->isStandardType() && (tok->strAt(2))[0] == '-') negativeBitwiseShiftError(tok); } else { if ((tok->strAt(2))[0] == '-') negativeBitwiseShiftError(tok); } } } } }

void CheckOther::negativeBitwiseShiftError(const Token *tok) { reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value."); }

//------// Check for incompletely filled buffers. //------void CheckOther::checkIncompleteArrayFill() { bool warning = _settings->isEnabled("warning"); bool portability = _settings->isEnabled("portability"); if (!_settings->inconclusive || (!portability && !warning)) return;

147

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "memset|memcpy|memmove ( %var% ,") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) { const Variable *var = tok->tokAt(2)->variable(); if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) continue;

if (MathLib::toLongNumber(tok->linkAt(1)->strAt(-1)) == var- >dimension(0)) { unsigned int size = _tokenizer->sizeOfType(var->typeStartToken()); if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (warning) incompleteArrayFillError(tok, var->name(), tok->str(), false); } else if (var->typeStartToken()->str() == "bool" && portability) // sizeof(bool) is not 1 on all platforms incompleteArrayFillError(tok, var->name(), tok->str(), true); } } } } } void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) { if (boolean) reportError(tok, Severity::portability, "incompleteArrayFill", "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", true); else reportError(tok, Severity::warning, "incompleteArrayFill", "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", true);

148

}

void CheckOther::oppositeInnerCondition() { // FIXME: This check is experimental because of #4170 and #4186. Fix those tickets and remove the "experimental". if (!_settings->isEnabled("warning") || !_settings->inconclusive || !_settings- >experimental) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();

for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { const Token* const toke = scope->classDef;

if (scope->type == Scope::eIf && toke) {

const Token *op1Tok, *op2Tok; op1Tok = scope->classDef->tokAt(2); op2Tok = scope->classDef->tokAt(4);

if (scope->classDef->strAt(6) == "{") {

const char *oppositeCondition = nullptr;

if (scope->classDef->strAt(3) == "==") oppositeCondition = "if ( %any% !=|<|>|<=|>= %any% )"; else if (scope->classDef->strAt(3) == "!=") oppositeCondition = "if ( %any% ==|>=|<= %any% )"; else if (scope->classDef->strAt(3) == "<") oppositeCondition = "if ( %any% >|>=|== %any% )"; else if (scope->classDef->strAt(3) == "<=") oppositeCondition = "if ( %any% > %any% )"; else if (scope->classDef->strAt(3) == ">") oppositeCondition = "if ( %any% <|<=|== %any% )"; else if (scope->classDef->strAt(3) == ">=") oppositeCondition = "if ( %any% < %any% )";

if (oppositeCondition) { int flag = 0;

for (const Token* tok = scope->classStart; tok != scope->classEnd && flag

149

== 0; tok = tok->next()) { if ((tok->str() == op1Tok->str() || tok->str() == op2Tok->str()) && tok- >strAt(1) == "=") break; else if (Token::Match(tok, "%any% ( %any% )")) { if ((tok->strAt(2) == op1Tok->str() || tok->strAt(2) == op2Tok- >str())) break; } else if (Token::Match(tok, "%any% ( %any% , %any%")) { for (const Token* tok2 = tok->next(); tok2 != tok->linkAt(1); tok2 = tok2->next()) { if (tok2->str() == op1Tok->str()) { flag = 1; break; } } } else if (Token::Match(tok, oppositeCondition)) { if ((tok->strAt(2) == op1Tok->str() && tok->strAt(4) == op2Tok- >str()) || (tok->strAt(2) == op2Tok->str() && tok->strAt(4) == op1Tok->str())) oppositeInnerConditionError(toke); } } } } } } } void CheckOther::oppositeInnerConditionError(const Token *tok) { reportError(tok, Severity::warning, "oppositeInnerCondition", "Opposite conditions in nested 'if' blocks lead to a dead code block.", true); }

void CheckOther::checkVarFuncNullUB() { if (!_settings->isEnabled("portability")) return;

const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok-

150

>next()) { // Is NULL passed to a function? if (Token::Match(tok,"[(,] NULL [,)]")) { // Locate function name in this function call. const Token *ftok = tok; std::size_t argnr = 1; while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++argnr; ftok = ftok->previous(); } ftok = ftok ? ftok->previous() : nullptr; if (ftok && ftok->isName()) { // If this is a variadic function then report error const Function *f = ftok->function(); if (f && f->argCount() <= argnr) { const Token *tok2 = f->argDef; tok2 = tok2 ? tok2->link() : nullptr; // goto ')' if (Token::simpleMatch(tok2->tokAt(-3), ". . .")) varFuncNullUBError(tok); } } } } } } void CheckOther::varFuncNullUBError(const Token *tok) { reportError(tok, Severity::portability, "varFuncNullUB", "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "The standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" "In practice on common architectures, this will cause real crashes if sizeof(int)

151

!= sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" "#include \n" "#include \n" "\n" "void f(char *s, ...) {\n" " va_list ap;\n" " va_start(ap,s);\n" " for (;;) {\n" " char *p = va_arg(ap,char*);\n" " printf(\"%018p, %s\n\", p, (long)p & 255 ? p : \"\");\n" " if(!p) break;\n" " }\n" " va_end(ap);\n" "}\n" "\n" "void g() {\n" " char *s2 = \"x\";\n" " char *s3 = \"ERROR\";\n" "\n" " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" "}\n" "\n" "void h() {\n" " int i;\n" " volatile unsigned char a[1000];\n" " for (i = 0; i

//ARR02 CERT rule

152

void CheckOther::unboundArrayWithInit() { if (!_settings->isEnabled("warning")) return;

for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%type%" )) { Token *tok2 = tok->next(); if (tok2 && Token::Match(tok2, "%var%" )) { Token *tok3 = tok2->next(); Token *tok4; if(tok3) tok4 = tok3->next(); if(tok3 && Token::Match(tok3, "[" ) && tok4 && Token::Match(tok4, "]" ) ){ unboundArrayWithInitWarning(tok); } } } }

} void CheckOther::unboundArrayWithInitWarning(const Token *tok) { reportError(tok, Severity::warning, "unbound Array", "Should not use unbound [] for array"); }

//FIO17 CERT rule void CheckOther::FIO17(void){ for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "fscanf|fopen|fclose|sprintf|fprintf")){ FIO17Error(tok); } } } void CheckOther::FIO17Error(const Token *tok) { reportError(tok, Severity::warning, "FIO17", "Prefer streams to C-style input and output", true);

153

}

//EXP01 CERT rule void CheckOther::EXP01(void){ for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %var%")){ Token *var = tok->next()->next(); const Variable *v = var->variable(); if (v->isPointer()){ EXP01Error(tok); } } } }

void CheckOther::EXP01Error(const Token *tok) { reportError(tok, Severity::warning, "EXP01", "Do not take the size of a pointer to determine the size of the pointed-to type", true); }

//FIO01 CERT rule void CheckOther::FIO01_1(void){ for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "chown|stat|chmod")){ FIO01_1Error(tok); } } } void CheckOther::FIO01_1Error(const Token *tok) { reportError(tok, Severity::warning, "FIO01", "Do not use chown, stat or chmod. Use fchown, fstat or fchmod instead.", true); } void CheckOther::FIO01_2(void){ for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "link|unlink|mkdir|rmdir|mount|unmount|lstat|mknod|symlink|utime")){ FIO01_2Error(tok);

154

} } } void CheckOther::FIO01_2Error(const Token *tok) { reportError(tok, Severity::warning, "FIO01", "Be careful using functions that use file names for identification", true); }

155

REFRENCES

[1] Open Source “History” [Online], http://en.wikipedia.org/wiki/Open_source#History

(Accessed: Apr. 1, 2014).

[2]Fisher, Franklin M.; James W. McKie, Richard B. Mancke (1983). IBM and the U.S.

Data Processing Industry: An Economic History. Praeger. ISBN 0-03-063059-2. IBM unbundled (began charging for) software June 23, 1969 (Cited on pages 9 and 172).

[3] Dave Pitts' IBM 7090 support – An example of distributed source: Page contains a link to IBM 7090/94 IBSYS source, including COBOL and FORTRAN compilers.

[4] M. Tiemann (2006, Sep. 19). “History of the OSI”. Open Source Initiative. [Online], http://opensource.org/history (Accessed: Jan. 21, 2014).

[5] Open Source Software “Open-source-software”. [Online], http://en.wikipedia.org/wiki/Open-source_software#cite_note-cmswire1-38 (Accessed

Apr.3, 2014).

[6] Open source. “What is Open Source?,” opensource.com. [Online], http://opensource.com/resources/what-open-source (Accessed: Feb. 1, 2014).

[7] V. Backaitis (2013, Jul. 17). “Open Source vs. Proprietary Software: There is No

Clear Winner”. CMS Wire. [Online], http://www.cmswire.com/cms/information- management/open-source-vs-proprietary-software-there-is-no-clear-winner-021752.php.

(Accessed: Jan. 21. 2014).

[8] cppcheck. “cppcheck” http://cppcheck.sourceforge.net. [Online], http://cppcheck.sourceforge.net/#features (Accessed: Jan. 23,2014).

156

[9] D. Marjamaki, “Cppcheck 1.63” http://cppcheck.sourcefroge.net. [Online], http://cppcheck.sourcefroge.net/manual.pdf (Accessed: Nov. 29, 2013).

[10] D. Marjamaki (2010), “ Writing cppcheck rules” http://cppcheck.sourceforge.net.

[Online], http://sourceforge.net/projects/cppcheck/files/Articles/writing-rules-2.pdf

(Accessed: Feb. 1, 2014).

[11] pcre “PCRE manual” http://php.net. [Online], http://www.php.net/manual/en/pcre.examples.php (Accessed: Feb. 23, 2014).

[12] D. Marjamaki, “Cppcheck 1.63” http://cppcheck.sourcefroge.net. [Online], http://cppcheck.sourcefroge.net/manual.pdf (Accessed: Nov. 29, 2013).

[12] D. Marjamaki (2014, Jan. 21), “ cppcheck Design” http://cppcheck.sourceforge.net.

[Online], http://sourceforge.net/projects/cppcheck/files/Articles/cppcheck-design.pdf

(Accessed: Feb. 22, 2014).

[13] D. Marjamaki (2011), “ Writing cppcheck rules” http://cppcheck.sourceforge.net.

[Online], http://sourceforge.net/projects/cppcheck/files/Articles/ writing-rules-3.pdf pdf

(Accessed: Mar. 3rd, 2014).

[14] CERT. “CERT coding Standards,” cert.org. [Online], https://www.securecoding.cert.org/confluence/display/seccode/CERT+Coding+Standards

(Accessed Feb. 1, 2014).

[15] Proprietary software is opposite of free software. Linfo.org (2005-07-03). Retrieved on 2013-06-16. (Accessed Apr. 6, 2014).

[16] Irina Guseva (2009-03-26). "Bad Economy Is Good for Open Source".

Cmswire.com. Retrieved 2012-03-25 (Accessed Apr. 1, 2014).

157

[17] CPPCHECK “Static code Analysis Tools and CPPCHECK” . [Online], http://www.slideshare.net/zblair/cppcheck-10316379 (Accessed Apr. 6, 2014).

[18] CERT “ CERT Certification”. [Online], http://www.slideshare.net/vaceituno/cert- certification?qid=76f33974-accc-44e6-936a- ba5317470a08&v=default&b=&from_search=3 (Accessed Apr.6, 2014).

[19] B. Wichmann, A. Canning, D. Clutterbuck, L. Winsborrow (1995) “Industrial

Perspective on Static Analysis”. [Online], https://web.archive.org/web/20110927010304/http://www.ida.liu.se/~TDDC90/papers/in dustrial95.pdf (Accessed Apr.29, 2014).

[20] Steve McConnell, "Code Complete, 2nd Edition" Microsoft Press, Paperback, 2nd edition, Published June 2004, 914 pages, ISBN: 0-7356-1967-0.

[21] A. Bessey, K. Block, (2010) “A few Billion Lines of Code Later: Using Static

Analysis to Find Bugs in the Real World”. [Online], http://dl.acm.org/citation.cfm?id=1646374 (Accessed Apr.27, 2014).

[22] Static Analysis “Static Code Analysis”. [Online], http://www.viva64.com/en/t/0046/

(Accessed May 4, 2014).

[23] Y.Younan, W. Joosen, and Frank Piesens (2004) “Code Injection in C and C++: A

Survey of Vulnerabilities and Countermeasures”. [Online], http://ftp.cs.kuleuven.be/publicaties/rapporten/cw/CW386.pdf (Accessed May 6, 2014).

[24] D. Evans and D. Larochelle. “Improving Security Using Extensi- ble Lightweight

Static Analysis. IEEE Software”, 19(1):42–51, January- February 2002. (Cited on pages

25 and 65.)

158

[25] D. Evans and D. Larochelle. “Statically Detecting Likely Buffer Overflow

Vulnerabilities". In Proceedings of the 10th USENIX Security Symposium, Washington,

District of Columbia, U.S.A., August 2001. USENIX Association. (Cited on pages 25 and 65.)

[26] Splint “Splint”. [Online], http://www.splint.org (Accessed May 11, 2014).

[27] D. Worth, G. Greenough and L. Chin (2009) “A Survey of C and C++ Software

Tools for Computational Science”. [Online], http://www.softeng.rl.ac.uk/media/uploads/publications/2010/03/c-c_tools_report.pdf

(Accessed May 12, 2014).

[28] N. Dor, M. Rodeh, and M. Sagiv. “Cleanness Checking of String Manipulation in C

Programs via Integer Analysis”. In Proceed- ings of the Eight International Static

Analysis Symposium, volume 2126 of Lecture Notes in Computer Science, Paris, France,

July 2001. Springer-Verlag. (Cited on pages 25, 28 and 65.)

[29] N. Dor, M. Rodeh, and M. Sagiv. “CSSV: Towards a Realistic Tool for Statically

Detecting All Buffer Overflows in C”. In Proceedings of the ACM SIGPLAN 2003

Conference on Design and Implementation, San Diego,

California, U.S.A., 2003. ACM. (Cited on pages 25 and 65.)

[30] G. Yorsh and N. Dor. “The Design of CoreC”. Technical report, Tel-Aviv

University, April 2003. (Cited on page 25.)

[31] P. Couscot and N. Halbwachs. “Automatic Discovery of Linear Restraints Among

Variables of a Program”. In Conference Record of the Fifth Annual ACM Symposium on

Principles of Programming Languages, Tucson, Arizona, U.S.A., January 1978. ACM.

(Cited on page 26.)

159

[32] CQUAL “CQUAL Background”.[Online], https://www.usenix.org/legacy/event/sec02/full_papers/zhang/zhang_html/node7.html

(Accessed May 15, 2014).

[33] S Jeffrey, F. Manuel, and A. Aiken. “A Theory of Type Qualifiers”. In Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and

Implementation, Atlanta, Georgia, U.S.A., May 1999. ACM. (Cited on page 26.)

[34] K. Ashcraft and D. Engler. “Using Programmer-Written Compiler Extensions to

Catch Security Holes”. In Proceedings of the IEEE Sympo- sium on Security and

Privacy, Berkeley, California, USA, May 2002. IEEE Computer Society, IEEE Press.

(Cited on pages 26 and 65.)

[35] D. Englers, M. Musuvathi “Static Analysis Versus Software Model Checking for

Bug Finding”. [Online], http://se.inf.ethz.ch/old/teaching/2009-S/0276/slides/agarwal.pdf

(Accessed May 13, 2014).

[36] J. Aldrich (2008), “Reading: A Static analyzer for Finding Dynamic Programming

Erors”. [Online], http://www.cs.cmu.edu/~aldrich/courses/654-sp08/slides/14-prefix.pdf

(Accessed May 11, 2013).

[37] W. Bush, J. Pincus, and D. Sielaff. “A static analyzer for finding dynamic programming errors”. Software: Practice and Experience, 30(7):775–802, June 2000.

ISSN: 0038-0644. (Cited on pages 3, 27 and 65.)

[38] J. Larus, T. Ball, M. Das, and R. DeLine, . “Righting Software. IEEE Software”,

21(3):92–100, May-June 2004. (Cited on pages 3, 22, 27, 65 and 66.)

[39] Y. Xie, A. Chou and D. Engler. “ARCHER: Using Symbolic, Path-sensitive

Analysis to Detect Memory Access Errors”. In Proceedings of the 9th European Software

160

Engineering Conference, held jointly with 10th ACM SIGSOFT International

Symposium on Foundations of Soft- ware Engineering, Helsinki, Finland, 2003. ACM

Press. (Cited on pages 28 and 65.)

[40] R. C (2013). “ Secure Coding in C and C++” 2nd Edition

[41] PC-Lint “PC-lint and FlexeLint”. [Online], http://www.gimpel.com/html/products.htm (Accessed May 10, 2014).

[42] A. Karpov ( 2009). "Parallel Lint". [Online], http://www.drdobbs.com/parallel- lint/218000153 (Accessed May 10, 2014).

[43] Klocwork “Klocwork”. [Online], http://en.wikipedia.org/wiki/Klocwork (Accessed

May 10, 2014).

[44] A. Almossawi, K. Lim, and T. Sinha (2006). “Analysis Tool Evaluation”. [Online], http://www.cs.cmu.edu/~aldrich/courses/654-sp09/tools/cure-coverity-06.pdf (Accessed

May 11, 2014).

[45] ARR02 CERT rule “ARR02-CPP. Explicitly specify array bounds, even if implicitly defined by an initializer”. [Online], https://www.securecoding.cert.org/confluence/display/cplusplus/ARR02-

CPP.+Explicitly+specify+array+bounds,+even+if+implicitly+defined+by+an+initializer

(Accessed May 11, 2014).

[46] ARR02 CERT rule “ARR02-C. Explicitly specify array bounds, even if implicitly defined by an initializer”. [Online], https://www.securecoding.cert.org/confluence/display/seccode/ARR02-

C.+Explicitly+specify+array+bounds,+even+if++implicitly+defined+by+an+initializer

(Accessed May 12, 2014).

161

[47] DCL01 CERT rule “DCL01-C. Do not reuse variable names in subscopes”.

[Online], https://www.securecoding.cert.org/confluence/display/seccode/DCL01-

C.+Do+not+reuse+variable+names+in+subscopes (Accessed May 8, 2014).

[48] K. Finley (2011, Jun. 2). “ Github Has Surpassed Sourceforge and Google Code in

Popularity”. ReadWrite [Online], http://readwrite.com/2011/06/02/github-has-passed- sourceforge#awesm=~oy2ImSaGXepWgF (Accessed Feb, 1. 2014).

[49] Jump up ^ "GitHub, Collaboration, and Haters". Sourceforge. 3 June 2011.

Retrieved 23 November 2013.

[50] EXP01 CERT rule “VOID EXPO01-C. Do not take the size of a pointer to determine the size of the pointed-to type”. [Online], https://www.securecoding.cert.org/confluence/display/seccode/VOID+EXP01-

C.+Do+not+take+the+size+of+a+pointer+to+determine+the+size+of+the+pointed- to+type (Accessed May 12, 2014).

[51] FIO01 CERT rule “FIO01-C. Be careful using functions that use file names for identification”. [Online], https://www.securecoding.cert.org/confluence/display/seccode/FIO01-

C.+Be+careful+using+functions+that+use+file+names+for+identification (Accessed May

12, 2014).

[52] FIO17 CERT rule”FIO17-CPP. Prefer streams to C-style input and output”.

[Online], https://www.securecoding.cert.org/confluence/display/cplusplus/FIO17-

CPP.+Prefer+streams+to+C-style+input+and+output (Accessed May 13, 2014).

[53] INT06 CERT rule”INT06-C. Use strtol() or a related function to convert a string token to an integer”. [Online],

162

https://www.securecoding.cert.org/confluence/display/seccode/INT06-

C.+Use+strtol()+or+a+related+function+to+convert+a+string+token+to+an+integer

(Accessed May 13, 2014).

[54] CON30 CERT rule”CON30-C. Clean up thread-specific storage”.[Online], https://www.securecoding.cert.org/confluence/display/seccode/CON30-

C.+Clean+up+thread-specific+storage (Accessed May 13, 2014).

[55] SIG32 CERT rule”VOID SIG32-C. Do not call longjmp() from inside a signal handler”. [Online], https://www.securecoding.cert.org/confluence/display/seccode/VOID+SIG32-

C.+Do+not+call+longjmp()+from+inside+a+signal+handler (Accessed May 13, 2014).

[56] STR32 CERT rule”STR32-C. Do not pass a non-null-terminated character sequence to a library function that expects a string”. [Online], https://www.securecoding.cert.org/confluence/display/seccode/STR32-

C.+Do+not+pass+a+non-null- terminated+character+sequence+to+a+library+function+that+expects+a+string

(Accessed May 13, 2014).