Module
Off-by-one Comparison Against Length
Summary
A product calculates or uses an incorrect maximum or minimum value that is 1 more, or 1 less, than the correct value. Off-by-n errors where n is an integer value greater than 1 is also possible.
Description
Reading an array or a list element from an index that is greater than the array length always returns undefined or leads to an error/exception.
For some programming languages, such as, C, C++, and Go, If the index is compared to the array length using the less-than-or-equal operator <= instead of the less-than operator <, the index could be out of bounds, which may not be intentional and may adversely affect performance, lead to unexpected results.
-
Availability. This weakness will generally lead to undefined behavior and therefore crashes. In the case of overflows involving loop index variables, the likelihood of infinite loops is also high.
-
Integrity. If the value in question is important to data (as opposed to flow), simple data corruption has occurred. Also, if the wrap around results in other conditions such as buffer overflows, further memory corruption may occur.
-
Confidentiality, Availability, Access Control. This weakness can sometimes trigger buffer overflows which can be used to execute arbitrary code. This is usually outside the scope of a program’s implicit security policy.
Although this error can be more severe in some languages than in others, it is a common error that can lead to unexpected results. Software develoers should watch out for this error when writing code that accesses arrays or lists.
Risk
How Can It Happen? Unintentional use of the less-than-or-equal operator <= instead of the less-than operator < can lead to an out-of-bounds error when accessing an array or list.
Example
Linux sudo
command before 1.6.6 contains an off-by-one error that can result in a heap-based buffer overflow that may allow local users to gain root privileges via special characters in the -p
(prompt) argument, which are not properly expanded.
Example Code (Bad Code)
The following example shows a function that intends to check whether an
array a contains an element elt
by iterating over its elements and comparing
them to elt. However, the terminating condition of the loop is incorrectly
specified as i <= a.length
, not i < a.length
, so elt
will additionally be
compared against the value undefined read from index a.length
:
boolean contains(String[] a, String elt) {
for (int i = 0; i <= a.length; ++i)
if (a[i].equal(elt)) {
return true;
}
return false;
}
Addressing Off-by-One Errors
How would we address potential Off-by-One Errors in our code? Below are a few tips:
-
Use appropriate loop statement. For instance, in JavaScript, the forEach() method of Array instances executes a provided function once for each array element. Similarly, in Java, we can also use the List’s forEach() method or to use the enhanced for loop (for-each loop) to iterate over the elements of a list or an array.
The following example check if a List contains a specific element using the enhanced for loop:
boolean contains(List<String> a, String elt) { for (String s : a) { if (s.equals(elt)) { return true; } } return false; }
-
Use iterators. In Java, we can use the Iterator interface to iterate over collections. The Iterator interface provides methods to check if there are more elements in the collection and to retrieve the next element. This way, we can avoid off-by-one errors when iterating over collections.
The following example shows how to use an iterator to check if a List contains a specific element:
boolean contains(List<String> a, String elt) { Iterator<String> it = a.iterator(); while (it.hasNext()) { if (it.next().equals(elt)) { return true; } } return false; }
-
Use appropriate comparison operator. Use less-than (<) rather than less-than-or-equal (<=) when comparing a potential index against the array length. For loops that iterate over every element in an array, use a for…of loop or the forEach method instead of explicitly iterating over all indices.
The following example shows a function that intends to check whether an array a contains an element elt by iterating over its elements and comparing them to elt. To have correct behavior, we use less-than instead of less-than-or-equals:
boolean contains(String[] a, String elt) { for (int i = 0; i < a.length; ++i) if (a[i].equal(elt)) { return true; } return false; }
Acknowledgement
This page is derived from the CodeQL project.