As with every language, Java programming comes with its own unique set of common errors and vulnerabilities. Klocwork can detect a large number of Java errors in the following categories:
- Resource management (See detailed example)
- Concurrency violations (See detailed example)
- Web application vulnerabilities (See detailed example)
- Uses of un-validated user input
- Device-specific coding practices
- JSP interactions
- Invalid object references
- Improper collection usage
Concurrent programming, or the practice of dividing up a program’s execution context into two or more threads, is more common with multicore and multi-CPU hardware environments. Regardless of the programming language, there are several basic requirements that source code analysis must identify and address in a concurrent context.
Two of the most important concurrent programming requirements:
- The ability to spot deadlock or livelock situations
- The ability to spot race conditions
Deadlocks and livelocks 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. Without these capabilities, developers are left to guess for themselves how their programs will operate at runtime.
Consider the following example:
GeSHi Error: GeSHi could not find the language javas (using path /home/sdcsyste/public_html/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
Here, several different locking scenarios are shown, any of which can cause blocking situations, including:
- Explicitly blocking one thread while holding a process-wide lock
- Lock contention through potentially inconsistent acquisition semantics
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 other’s’ 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.
Example: Resource leaks
Consider the following example:
There are at least two types of resource that are collected under the input stream object in the example above:
- Memory associated with the stream object itself, and with managing state within the stream
- An operating system file descriptor or handle that relates to the underlying file within the file system
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.
Web application vulnerabilities
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 static code analysis detects vulnerabilities like:
- SQL injection
- Process or file injection
- Code injection
- Cross-site scripting (XSS)
- Request forging
Each of these types of failure requires specific checks and analysis. However, 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.
Example: Web application vulnerabilities
In a servlet context, for example, consider the following snippet:
public void doGet(HttpServletRequest req, HttpServletResponse res)
String name = req.getParameter("username");
String pwd = req.getParameter("password");
// SQL Injection
int id = validateUser(username, password);
String retstr = "User : " + name + " has ID: " + id;
private int validateUser(String user, String pwd)
Statement stmt = myConnection.createStatement();
rs = stmt.executeQuery("select id from users where
user='" + user + "' and key='" + pwd + "'");
return rs.next() ? rs.getInt(1) : -1;
This example exhibits several different kinds of common errors. Putting aside the obvious resource leakage and management issues, these include:
- SQL injection via the use of an unfiltered incoming URL parameter as both username and password
- Cross-site scripting, or request mirroring, by including the unfiltered incoming URL parameter in our response to the user
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.