
In C89 (and historical K&R implementations), the meaning of the remainder operator for negative operands was implementation defined. This was changed in the C99 standard [[ISO/IEC 9899-1999]].
Because not all C compilers are strictly C99 conforming, you cannot rely on the behavior of the %
operator if you need to run on a wide range of platforms with many different compilers.
According to C99:
The result of the
/
operator is the quotient from the division of the first operand by the second; the result of the%
operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined.
and:
When integers are divided, the result of the
/
operator is the algebraic quotient with any fractional part discarded. If the quotienta/b
is representable, the expression(a/b)*b + a%b
shall equala
.
Discarding the fractional part of the remainder is often called truncation toward zero.
The C99 definition of the %
operator implies the following behavior:
17 % 3 -> 2 17 % -3 -> 2 -17 % 3 -> -2 -17 % -3 -> -2
The result has the same sign as the dividend (the first operand in the expression).
Non-Compliant Code Example
In this noncompliant example, the insert()
function adds values to a buffer in a modulo fashion, that is, by inserting values at the beginning of the buffer once the end is reached. However, both size
and index
are declared as int
and consequently not guaranteed to be positive. Depending on the implementation and on the sign of size
and index
, the result of (index + 1) % size
may be negative, resulting in a write outside the bounds of the list
array.
int insert(int index, int *list, int size, int value) { if (size != 0) { index = (index + 1) % size; list[index] = value; return index; } else { return -1; } }
This noncompliant code example also violates INT01-C. Use rsize_t or size_t for all integer values representing the size of an object.
Compliant Solution
To provide a nonnegative modulo operation, use the imod()
("integer modulo") inline function:
/* modulo function giving non-negative result */ inline int imod(int i, int j) { return (i % j) < 0 ? (i % j) + (j < 0 ? -j : j) : i % j; }
However, the most appropriate solution in this case is to use unsigned types as in this compliant solution:
int insert(size_t index, int *list, size_t size, int value) { if (size != 0) { index = (index + 1) % size; list[index] = value; return index; } else { return -1; } }
Risk Assessment
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
INT10-A |
low |
unlikely |
high |
P1 |
L3 |
Automated Detection
Fortify SCA Version 5.0 with the CERT C Rule Pack can detect violations of this recommendation.
Compass/ROSE could detect the specific NCCE. It could identify when the result of a % operation might be negative, and flag usage of that result in an array index. It could, conceivably flag usage of any such result without first checking that the result is positive, but this will most likely introduce many false positives.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Other Languages
This rule appears in the C++ Secure Coding Standard as INT10-CPP. Do not assume a positive remainder when using the % operator.
References
[[Beebe 05]]
[[ISO/IEC 9899-1999]] Section 6.5.5, "Multiplicative operators"
[[Microsoft 07]] C Multiplicative Operators
[[MITRE 07]] CWE ID 682, "Incorrect Calculation," and CWE ID 129
, "Unchecked Array Indexing"
[[Sun 05]] Appendix E, "Implementation-Defined ISO/IEC C90 Behavior"
04. Integers (INT) INT11-C. Take care when converting from pointer to integer or integer to pointer