Abstract
Overview
Objectives
State of the Art
The ZeroFault Approach
Normal Usage Procedures
Supported Environments
Summary of Reported Errors

Abstract

This paper describes ZeroFault, a Virtual Machine tool for analyzing and discovering errors and potential errors in executable software programs. ZeroFault monitors and validates every operation performed by a program with a focus on the program's usage of memory.

Overview

ZeroFault is a Virtual Machine in which the target application is run. The Virtual Machine provides access for the application to all system resources (memory, system calls, etc).

ZeroFault provides the software developer with the information necessary to prevent memory related problems from reaching the customer. ZeroFault also makes it easier to diagnose and fix problems that do reach the customer.

ZeroFault also makes it possible for the software consumer to test and evaluate the quality of software purchases in the environment in which they will be used.

The ZeroFault Virtual Machine runs without preparation of any sort. For example:


    $ zf /bin/vi /etc/passwd

The lack of preparation makes ZeroFault more effective by eliminating potential obstacles such as relinking or recompiling.

ZeroFault validates the operands and effects of every instruction or system call that the target process performs, whether directly, through a shared library, or through a dynamically loaded module. This results in a comprehensive list of errors made by the target application. This information is invaluable to both the developer attempting to create quality software and to the consumer trying to evaluate the quality and reliability of components of a mission-critical application.

The ZeroFault Virtual Machine is a dynamic process which rewrites the instructions of the target application as they are executing. This ability makes it possible to change what is monitored and how it is monitored while the process is executing.

Objectives

The goal of ZeroFault is to identify memory problems when and where they occur and provide the information necessary to resolve them.

In order to do that ZeroFault must run in almost any environment and provide the user with the information necessary to resolve any memory related problem.

State of the Art

The current state of the art for memory analysis has three primary thrusts:

  • malloc() wrapper
  • source code based instrumentation/interpretation
  • object code relinkers

The most primitive of these three is the malloc wrapper. The foundation of this approach is a custom malloc() implementation that uses a combination of pattern fill and boundary blocks to detect uninitialized memory references and memory overwrites. Some implementations of this strategy also provide wrappers for many library and system calls to try to validate the parameters of those calls. This implementation does not detect uninitialized heap references well or uninitialized stack references at all. Malloc wrappers can not identify the source line corresponding to the place where an overwrite took place.

Source code based instrumentation and interpretation can be very effective at pinpointing errors when source is available. Often source code is not available (for instance most developers do not have source for Motif). Additionally they typically do not work on assembler, fortran, pascal, etc. Even for C or C++ source not all compilers are compatible. These limitations make source code implementations incomplete, since they rarely are able to validate every operation performed by the application. Even if partial coverage is acceptable it takes significant effort to recompile and relink the application and each of its associated shared libraries.

Object code relinkers work by relinking the source object code files for an application into a new, larger application that has instructions that validate each instruction in the original object files. This approach enables much more complete coverage than either the malloc wrapper technology or the source code base technology, but it does require the ability to relink the application. In complex build environments relinking the application can be extremely difficult and time-consuming. In addition, the act of changing the build environment can change the execution of the program being tested.

The ZeroFault Approach

ZeroFault uses a virtual machine technology to monitor every machine instruction or system call performed by the application. The virtual machine technology requires no preparation and provides complete coverage of every action performed by the application. This coverage includes all shared libraries used by the application.

The ZeroFault virtual machine uses patent pending dynamic instrumentation to rewrite the instructions of a target executable process as they are executed.

These rewritten instructions examine the operands of every machine instruction performed by the process to validate the instruction in the current memory context.

Because instructions are rewritten at run time, only instructions that are actually executed are rewritten, which results in a significant resource savings.

There are several distinct advantages to the dynamic instrumentation strategy employed by ZeroFault. Because instructions are rewritten at run time it is possible to enable and disable memory analysis at will. Often an error occurs only late in the process' life cycle. It is possible to start the process with memory analysis disabled, then as the process nears the error point, enable memory analysis.

When ZeroFault detects and reports an error at a particular location that instruction is then rewritten back into the original uninstrumented form and left that way. Since the act of filtering out a duplicate error is very time intensive when compared with just executing the original instruction, this method can reduce instrumented run times from hours to seconds.

When the load() system call is used by the application to access a dynamic load module, ZeroFault reads the module in and performs the dynamic instrumentation necessary to detect errors that occur within the load module. This automatic instrumentation is very useful with complex applications that often make heavy use of the load() system call.

Because ZeroFault does not relink the application it is possible for ZeroFault to run even on stripped executables. This makes it possible to analyze the exact software that the customer is running.

In many organizations the area or department testing an application does not have the access, time, or expertise necessary to relink or recompile an application. This makes tools that require relinking or recompiling effectively useless in these circumstances. Since ZeroFault can run on the stripped production code ZeroFault can be used by anyone that can normally run the program.

Normal Usage Procedures

The normal method for using ZeroFault is as follows:


    $ zf /usr/bin/X11/xterm

This will run xterm within the ZeroFault virtual machine. Provided your DISPLAY is set you will see a ZeroFault window showing the errors for xterm and the xterm itself. You may type in and use the xterm just as you would any normal xterm.

The ZeroFault display contains a list of all errors discovered by ZeroFault in xterm. The list is updated as the errors occur. There are facilities for:

  • suppressing non-critical errors
  • sorting the error messages
  • condensing the error messages
  • viewing memory leaks
  • viewing and editing source code associated with errors
  • viewing each message at several levels of detail

Supported Environments

ZeroFault is extremely versatile, running on any program compiled from any language, stripped or not, symbols or not, pthreads, custom threads packages, DCE, etc.

The only requirement is that the target application must be a POWER- or PowerPC-compliant XCOFF executable.

Summary of Reported Errors

Bad Memory Read
Bad Memory Write
The process read from or wrote to memory not allocated to the process. This means that the memory was freed, unallocated, beyond the block boundary, or just never allocated.

Bad Function Call Parameter
Bad Function Call Target
The process made a library function or system call that reads from (parameter) or writes to (target) memory not allocated to the process. This means that the memory was freed, unallocated, beyond the block boundary, or just never allocated.

Null Pointer Read
Null Pointer Write
The process read from or wrote to a null pointer (or a near null pointer).

Bad Realloc
The process attempted a realloc of an address that does not correspond to a memory block.

Duplicate Free
Free of an already freed block.

Bad Free
Free of an address that was never allocated.

Leaked Block
Reported when garbage collection is done, or when the process exits: a block that cannot be referenced because no pointers exist in the process that point to or into the block.

Unfreed Block
Reported when the process exits: a block of memory that has not been freed. Because there are often large numbers of these blocks, this is only reported when the '-u' flag is used.

Uninitialized Function Call Parameter
A memory region that was passed as a source to a library function or system call was not initialized.

Uninitialized Stack Read
A stack variable was used before it was initialized.

Uninitialized Memory Read
A region in a malloc'ed block (heap memory) was used before it was initialized.

 


© Copyright 2007 The ZeroFault Group, LLC. All rights reserved. All logos and trademarks are property of their respective owners.