Klocwork products apply complex static analysis techniques to C, C++, Java and C# to automatically locate critical programming bugs and security vulnerabilities in source code.
By applying inter-procedural control flow, data flow, value-range propagation and symbolic logic evaluation, Klocwork can find hundreds of errors on well-validated, feasible execution paths. Issue types found by Klocwork are summarized below by programming language, along with selected bug examples which include a code snippet and detailed description.
Many bugs created within C-derived languages are common to all derivations as well as to the root language. The most notable problems derive from C's requirement for the programmer to manage memory explicitly. This places an ongoing lifetime maintenance burden on the original developer as well as on every developer who inherits the code over time. Klocwork finds a broad range of issues specific to C and C++ which fit into the following high-level categories:
NULL pointers (or null object references in Java) cause numerous problems when they are dereferenced, usually due to insufficient defenses being placed in code. This type of error shows itself most notably in larger systems where different developers are responsible for inter-dependent modules.
This is a simple example of NULL Pointer Dereference:
When both of these functions are physically resident within the same module or the same subsystem, it is trivial for even junior developers to find the error. But, when these functions are separated into dependent subsystems written by different developers or different development teams, these errors become much more difficult to spot manually.
Value projection also adds another layer of complexity:
In this example, certain values of the incoming parameter will cause errors and other values will not. Klocwork products are able to understand how these values affect the available space of code paths.
Buffer overflows, more generally referred to as "array bounds violations," are errors that occur when code addresses elements of an array outside of the bounds that are defined for that data object.
This is a simple example of a buffer overflow:
In this simple example, the programmer is explicitly addressing memory outside of the range of the stack-based variable "arr." This will cause memory to be overwritten, potentially including the stack frame information that is required for the function to successfully return to its caller, etc.
This coding pattern is at the heart of some of the most pernicious security vulnerabilities that exist in software today.
While the specifics of the vulnerability change from instance to instance, the underlying problem is the same: performing array copy operations that are incorrectly or insufficiently guarded against exploit. Consider the following example:
The "memcpy" call will copy up to 256 bytes (the result of taking the zero'th element of the "data" buffer as its length) into a fixed sized stack-based array of only 32 bytes. If that data stream is available to the outside world, the system can be hacked.
For a high-profile example of this error in a long line of cases of buffer overflow causing stack corruption and vulnerability to code injection, consider Microsoft's well-published problems with animated cursor files.
One of the major problems with C-derived languages is the requirement for the programmer to manage memory completely. Buffers or structures that are allocated on the heap must be released appropriately when all references to those buffers or structures are about to head out of scope.Check your own code for Memory Leaks now - Free
This is a simple example of a memory leak:
On return from this function, a data block of 32 bytes in length will languish unreferenced on the heap. If repeated enough times, the heap manager will fail.
By applying rigorous control and data flow analysis, Klocwork products can spot occurrences of memory leaks within code constructs that might easily pass manual inspection. For example:
Here, a simple singly linked list leaks memory whenever it is cleaned up because while the list elements themselves are released, the strings to which each element points are not released. This type of error is particularly pernicious in C++ with base and derived classes defining different elements that all must be cleaned up by the virtual destructor chain in order to guarantee no memory leaks.
C# developers face many of the same problems as users of other languages, plus many situations that are unique the language and the underlying .Net framework. Klocwork can detect a variety of situations in which code will cause failures, either immediately or over time, including:
For example, the following invalid use of a NULL object reference:
In this case, there are a set of conditions under which the unchecked usage of the returned object 'obj' will cause a runtime failure, and thus this code will result in a Klocwork report of a possible defect.
Likewise, considering contractual obligations, the IDispose interface requires referencing objects use the sole interface member Dispose) before allowing an implementing object to go out of scope. For example, the following code will cause a defect to be reported:
Modifying this code to either explicitly invoke the Dispose method on the object 'd', or by using the 'using' construct, will remove the defect:
Java programming also comes with its own unique set of common error vulnerabilities. Klocwork can detect a large number of issues in Java which fit into the following categories:
Concurrent programming, or the practice of dividing up a program's execution context into two or more threads, is becoming increasingly common due to the prevalence of multi-core and multi-CPU hardware environments. Regardless of the programming language being used, there are several basic requirements placed on any source code analysis engine that must be taken seriously in a concurrent context. Two of the most important of these are:
Without these capabilities, programmers are left to guess for themselves how their programs will operate at runtime.
Dead-locks and live-locks refer to situations in which programs use locking semantics to guard sections of code against two or more threads of execution attempting access simultaneously. Typically, this involves modifying global data, but is by no means limited to that context.
Consider the following example:
Here, several different locking scenarios are shown, any of which can cause blocking situations, including:
Significant debug time can also be spent chasing almost impossible-to-replicate behavior caused by the "race condition." This condition occurs when two or more threads each has an equal chance to modify data in each others' context. A simple example is static data in a re-entrant class, such as a servlet running within a J2EE container. Modifying class data on one thread that might be read or differently modified at the same time by another thread will lead to unexpected behavior.
Like memory leaks, resource leaks can be crippling to an application and, over time, will lead to denial-of-service scenarios. The family of resource leaks to be most concerned with are those that tie into either operating system handles or descriptors, or to framework memory that requires explicit release semantics.
Klocwork supports a wide variety of resource semantics, from basic types like file descriptors and streams to framework-specific semantics for environments such as Google's Web Toolkit, Struts, Java Mail, J2ME, ImageIO, Hibernate, and many more.
A very common mistake is to assume that the runtime garbage collector (GC) will look after resources in the same way that it looks after memory references. However, while the memory associated with the object itself is collected by the GC, the resources associated with that object may not be cleaned up.
Consider the following example:
There are at least two types of resource that are collected under the input stream object in the example above:
The GC will take care of the first aspect while leaving the underlying descriptor open, thus consuming valuable system resources over time and eventually resulting in a denial-of-service scenario.
With many potential pitfalls inherent in creating web applications, this has quickly become one of the most popular areas to investigate automated approaches to debugging. Klocwork's source code analysis products detect vulnerabilities like:
Each of these types of failure requires specific checks and analysis. Generally, however, a many of the attack vectors exposed by such weaknesses can be generalized to the propagation of tainted data around an under-defensive design. That is, taking input from a user or another process and using that data without rigorous validation of its format, its range, or whatever else might make sense for the data type in question.
In a servlet context, for example, consider the following snippet:
This example exhibits several different kinds of common errors. Ignoring the obvious resource leakage and management issues for now, these include:
A malicious user could provide suitably marked-up URL parameters that would cause many problems. Likewise, applications that fail to validate strings that end up being used as file names are also open to file or process injection. As a result of its comprehensive web vulnerability analysis, Klocwork can detect all OWASP Top 10 vulnerabilities which can be found through static source code analysis:
Klocwork helps developers create more secure and reliable software. Our tools analyze source code on-the-fly, simplify peer code reviews, and extend the life of complex software. Hundreds of customers in the mobile device, consumer [...]
Producing high-quality, feature-rich software while meeting regulatory guidelines presents a unique set of challenges for those developing medical device software. In this paper for medical device software managers [...]
Given the complexity of today's airborne software systems, the use of automated tools can assist in the successful on-time and on-budget delivery of these projects. Automated source code analysis tools locate [...]