-
Outsourced Software Testing Services | Software Test Automation | QA Training | Quality Assurance Consulting | Our Clients | Downloads | About Us | Contact Us
#
LogiGear
search: Search

home >> resources >> Common Software Errors >> Errors in Handling or Interpreting Data

>> Home
>> QA City
>>
Latest articles
Classic articles
Articles by others
Resource directory
>> White papers
>> Newsletter archives
>> RSS feed
>> Books
>> Contact us


For more information:
Contact Us

Printer friendly:
PDF version

QA City

AddThis Social Bookmark Button

Testing Computer Software Second Edition

Common Software Errors - Errors in Handling or Interpreting Data


This is the appendix from the best-selling book
Testing Computer Software, 2nd ed.

Copyright © 1988 by Cem Kaner
Copyright © 1993 by Cem Kaner, Jack Falk, Hung Quoc Nguyen

This is part 8 of 13.

[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ]
[ 10 ] [ 11 ] [ 12 ] [ 13 ]

ERRORS IN HANDLING OR INTERPRETING DATA

Data are passed from one part of a program to another, and from one program to another. In the process, the data might be misinterpreted or corrupted.

PROBLEMS WHEN PASSING DATA BETWEEN ROUTINES

The program calls a subroutine and passes it data, perhaps like so:

DO SUB(VAR_1, VAR_2, VAR_3)

The three variables, VAR_1, VAR_2, and VAR_3 are passed from the program to the subroutine. They are called the subroutine's parameters. The subroutine itself might refer to these variables by different names. The statement at the start of the subroutine definition might look like this:

SUB(INPUT_1, INPUT_2, INPUT_3)

The subroutine receives the first variable in the list passed by the program (VAR_1) and calls it INPUT_1. It calls the second variable in the list (VAR_2) INPUT_2. INPUT_3 is its name for the last variable (VAR_3).

The program's and subroutine's definitions of these variables must match. If VAR_1 is an integer, INPUT_1 should be as well. If VAR_2 is a floating point value for someone's temperature, that's what the subroutine had better expect to find in INPUT_2.

Parameter list variables out of order or missing

If the program says DO SUB(VAR_2, VAR_1, VAR_3), the subroutine will associate INPUT_1 with VAR_2, and INPUT_2 with VAR_1. Programmers routinely type the variable names in the wrong order in these lists.

Missing parameters are less common in many, but not all, languages because their compilers catch this problem.

Data type errors

Suppose the program defines VAR_1 and VAR_2 as two-byte integers but the subroutine defines INPUT_1 and INPUT_2 as one-byte integers. What happens is language dependent, but it would be no surprise if INPUT_1 got the first byte of VAR_1 and INPUT_2 got the second byte.

The data type specifies how the data are stored. Integers, floating points, and character strings are simple examples. Arrays, records (like a record in a database, with fieldsósee Chapter 12) and arrays of records are common examples of slightly more complex data structures. There are also stacks, trees, linked lists, and others. (See Elson, 1975, for descriptions of these.)

Sometimes a mismatch between the structure of data in the calling and called routines is deliberate. The calling program might pass a three-dimensional array which the subroutine treats as a bigger one-dimensional array. The calling routine might pass an array of characters to a subroutine that treats them as an array of numbers. Some languages ban this, but it is standard form in others. As you can imagine, it can lead to a big mess, especially when the variable(s) being reinterpreted are part of a larger list. If the calling and called routines differ in the amount of memory they expect these mismatched variables to use, anything that comes after these in the parameter list might be misread.

Aliases and shifting interpretations of the same area of memory

If two different names refer to the same area of memory at the same time, they are aliases. If VAR_1 and FOO are aliases for each other, then if the program says SET FOO = 20, VAR_1 becomes 20. Some aliases are trickier. Suppose VAR_1 and VAR_2 are both one-byte integers and FOOVAR is a two-byte integer whose first (high order) byte just happens to be VAR_1 and whose second byte is VAR_2. In this case, SET FOOVAR = 20 sets VAR_2 to 20 and VAR_1 to 0.

It is easy to forget an alias. Expect maintenance programmers to miss it. In either case, the programmer will freely change one variable without realizing the effect on the "other." This can cause all sorts of unexpected results, even more so when the aliasing is more complex than two variables with the same name.

Misunderstood data values

The program passes temperature in centigrade to a subroutine which interprets the value as fahrenheit. The subroutine puts a 1 into an error flag to indicate that the flag is clear. It uses - 1 to indicate a set flag. The program expects 0 for a clear flag and any other value if the flag is set.

Inadequate error information

The subroutine fails to set an error flag (maybe there is no error flag). Or it does signal an error but doesn't say enough else for the calling program to decide how to handle it.

Failure to clean up data on exception-handling exit

The subroutine detects an error or a special case and exits quickly. Before it detected the problem, it changed the values of variables passed to it. If possible, it should reset these to their original values before returning to the calling program.

Outdated copies of data

Two processes may keep their own copies of the same data. Two routines within the same process might do the same. When the data change, both (all) copies have to be updated. It's common to find one process or routine working with an outdated copy of the data, because another changed the data without indicating that it did so.

Related variables get out of synch

One variable is usually a multiple of another, but one was changed and the other was not updated. In contrast to the outdated copy problem just described, which is more likely a problem between processes, this one is common within the same routine.

Local setting of global data

Global variables are defined in the main program. Any subroutine can use them or read their values or change them. Subroutines' changes of a global variable are often accidental. The programmer thought that a second variable, local to the subroutine, had this name and that the change would be to that local variable.

Global use of local variables

A variable is local to a subroutine if only that subroutine can use it. Most languages distinguish between global and local variables, but not all. In many BASICs, for example, all variables are global. In these, variables are kept local only by usage: they appear only in one subroutine. Especially if the variables are not carefully named, the programmer might unintentionally refer to a local variable in other places in the program.

Wrong mask in bit field

To save a few bytes and microseconds, some processes pass data in bit fields. Each byte might hold eight variables, with one bit assigned to each. Or, the processes might use the first two bits for one variable, the next three for a second, leaving one bit each for third, fourth, and fifth variables. A mask is a bit pattern that allows the programmer to focus only on the bits of interest. It has 0's in the other bit positions. The program refers to these to zero out the irrelevant bits in the bit field. If the mask has the wrong bits set, the program looks at the wrong "variable."

Wrong value from a table

Data are often organized in tables (arrays or records). Pointer variables indicate where in the table a value should be stored or retrieved. The program might look in the wrong place (bad pointer) or it might look in the right place but find an incorrect value.

DATA BOUNDARIES

The program may use the wrong starting or ending address of a set of data.

Unterminated null terminated strings

STRING_VAR is a string variable. It can hold the string Hello or the string I am a string variable or a much longer string. The number of characters stored by a string variable isn't fixed, so there must be a way to indicate the end of the string. One approach puts a null character (all bits zero) after the last character. This is called the string terminator. In languages which use null terminators, all string handling routines look for the null. Occasionally, the null character is forgotten, overwritten, or just not copied with the rest of the string. A routine that works with this unterminated string, perhaps copying or printing it, will include the string and everything past what should be the string's end until it reaches a null or the end of the computer's memory. In copying the string to another variable, the routine might fill the new variable's data space plus hundreds of bytes past it, overwriting other variables or code.

Early end of string

STRING_VAR is supposed to hold I am a string variable but a routine that operates on STRING_VAR (perhaps copying or printing it), behaves as if it held I am a str. Perhaps a null character was copied into the middle of the string. If string length is kept in a separate byte (the length byte), perhaps this value was miscalculated or overwritten.

Read/write past end of a data structure, or an element in it

An array is a good enough example of a data structure for this description. The program might miscalculate the length of each element and so err when it tries to read the value of a specific element. In doing so, it might read a memory location well past the end of the array. The routine will probably also overshoot the end of the array if it expects the array to have more elements than it does. This kind of error usually happens when one routine sends the array to another, and their definitions of the data stored in it don't match.

READ OUTSIDE THE LIMITS OF A MESSAGE BUFFER

A buffer is an area of memory used for temporary storage. Messages between processes often include buffers: the message includes a pointer to the start of the buffer and, in effect, says "for more details, read this. " When the process receiving the message is done with it, it "releases" the buffer which becomes "free memory" again, ready for other uses as needed by the operating system.

The receiving routine might get the address or the length of the message buffer wrong. It could start reading memory locations that precede the start of the buffer or it could keep reading data out of locations past the buffer's end.

Compiler padding to word boundaries

Depending on the computer, a word might be 12 bits, 1, 2, 3, or 4 bytes long, or some other length. A word is a computer's most natural unit of storage. Some compilers "pad" all onebyte variables to make them a word long. For example, a variable whose value was 255 now has the value 00255: the leading zeros are padding. The variable takes more space but keeps the same value. Such a compiler might pad individual variables, individual elements of arrays, or the full array itself, to ensure that variables (or the array) have their first byte at the start of a word. Another compiler for the same language might not do this type of padding. Some routines calculate how many bytes a piece of data should be from the start of a data structure (perhaps the start of a message buffer). Such routines will fail when the programmer switches from a compiler that uses one set of padding rules to another compiler whose rules are different.

Value stack under/overflow

Earlier in the Appendix ("Control flow errors") we described stack problems as they relate to subroutine calls. The programmer might also store data on the stack. A stack that holds data only, no return addresses is a value stack.

Suppose a stack can hold 256 bytes and the programmer tries to store 300 bytes on it. The stack overflows: the last 256 bytes stored are usually kept, and the first 44 values lost, overwritten by the others. When the program tries to retrieve these data from the stack, it can only get the last 256. When it tries to pop the 257th value off the stack (the 44th pushed onto the stack) there is an underflow condition the program is trying to retrieve a value from a stack that is now empty.

Trampling another process' code or data

This is especially common when processes share areas of memory, rather than passing data back and forth using messages. One process loses a null terminator from a string or miscalculates the length of a data structure or just runs amok. It writes junk into areas of memory that it shares with another process, or it writes into areas it can reach even though they should be private to that other process.

MESSAGING PROBLEMS

The safest way for two processes to communicate is via messages. If they pass data through shared memory areas instead, a bug in one process can trash data used by both, no matter how defensively the other process was written. The most prevalent problems arising out of messaging architectures are race conditions, which are discussed in the next section. There are also errors in sending and receiving the data in a message.

Messages sent to wrong process or port

A message can go to the wrong place. Even if it goes to the right process, that process may expect messages from this one to arrive only at certain ports (think of ports as virtual receiving areas). Even a message that goes to the right process and port may carry an invalid ID (such as the name of the communication protocol in use between the processes). In any of these cases, the message will be rejected.

Failure to validate an incoming message

A process must check messages that it receives to make sure that they are intended for it, that they contain the right identifiers, etc. The process that sent this message may have sent it to the wrong place or it may be running amok. It is up to the receiving process to ensure that it accepts and acts on no garbage.

Lost or out of synch messages

One process may send many messages to another, in a predictable order. Sometimes, however, a process will send MESSAGE_2 before MESSAGE_1. It might send a request to write something to a disk file before sending a message that names the file and requests that it be opened. Messages can get out of order for hundreds of reasons; not all of them are bugs. The receiving program should be able to cope with this, perhaps by saving MESSAGE_2 until it gets MESSAGE_1, or by telling the other process that it discarded MESSAGE_2 because it came out of order.

Mismatching state information is a common symptom of a failure to cope with badly ordered messages. In the state table of one process, the disk file is open, the printer is initialized, the phone is offhook, etc. According to the other process, the disk file has not been opened, the printer is not ready, and the phone is on hook. It doesn't matter which process is right. The mismatch causes all sorts of confusion.

Message sent to only N of N+1 processes

Suppose that many (N+1) processes keep private copies of the same data, and update their local databases when they get a message instructing them to do so. Or suppose that many people are at their terminals, a separate control and communications process is assigned to each terminal, and an urgent message is sent out, to be printed on each screen, saying that the system will go down in three minutes. Sometimes, one of the processes doesn't get the message. This is usually the most recently activated process or the one most recently coded.

DATA STORAGE CORRUPTION

The data are stored on disk, tape, punch cards, whatever. The process corrupts stored data by putting bad values into these files.

Overwritten changes

Imagine two processes working with the same data. Both read the data from disk at about the same time. One saves some changes. The second doesn't know anything about changes made by the first. When it saves its changes, it overwrites the data saved by the first process. Some programs use field, record, or file locking to prevent processes from changing fields, records, or files that another process is changing. These locks are not always present, and they don't always work.

Data entry not saved

The program asks for data, which you enter. For some reason, maybe because the file is locked, it doesn't succeed in storing your entries on disk.

Too much data for receiving process to handle

The receiving process might not be able to cope with messages beyond a certain length, or with more than so many messages per minute. It might discard the excess, crash, or print error messages. What it won't do is process the excess messages successfully.

Overwriting a file after an error exit or user abort

You enter data but try to stop the program before it saves them. It saves the new (bad) data first, then stops.


-      
newsletter | RSS | site map |
-

1 (800) 322-0333   © 2008 LogiGear Corporation. All rights reserved.   Legal Notice.   Privacy Policy.