Do not make assumptions about the sign of the resulting value from the remainder % operator.
The In C89 (and historical K&R implementations), the meaning of the remainder operator for negative arguments operands was defined to be implementation defined in 1989 ISO C (and was so in historical K&R implementations), but that was fixed in the 1999 Standard-defined. This behavior was changed in C99, and the change remains in C11.
Because not all C - compilers are strictly C99C-conforming, you can not programmers cannot rely on the behavior of the % operator if you they need to run on a wide range of platforms with many different compilers.
The div() function provides the same result as the remainder % operator.According to C99C Standard, subclause 6.5.5 [ISO/IEC 9899:2011], states:
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/bis representable, the expression(a/b)*b + a%bshall equala.
Discarding the fractional part of the remainder is often called "truncation toward zero".
The C99 C definition of the % operator implies the following behavior:
| Code Block |
|---|
17 11 % 3 5 -> 21 17 11 % -35 -> 21 -1711 % 3 5 -> -21 -1711 % -35 -> -21 |
The result has the same sign as the dividend (the first operand in the expression).
...
Noncompliant Code Example
In this non-compliant example a noncompliant code example, the insert() function is used to add adds values to a buffer in a modulo fashion, that is, by inserting values into at the beginning of the buffer once the end is reached. However, the variable used to index the array, index, is not both size and index are declared as int and consequently are not guaranteed to be positive. If index is negative then Depending on the implementation and on the sign of size and index, the result of (index + 1) %size will also be negative. This causes a new value to be inserted in to a negative offset within list% size may be negative, resulting in a write outside the bounds of the list array.
| Code Block | ||||
|---|---|---|---|---|
| ||||
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; } } |
Implementation Details
Microsoft Visual Studio
In division where either operand is negative, the direction of truncation is toward 0.
If either operation is negative in division with the remainder operator, the result has the same sign as the dividend (the first operand in the expression). For example:
| Code Block |
|---|
50 % -6 = 2
-50 % 6 = -2
|
In each case, 50 and 2 have the same sign.
Sun Studio 10 C 5.7 Compiler
The result is the same sign as the dividend; thus, the remainder of -23/4 is -3.
gcc
GCC always follows the C99 requirement that the result of division is truncated towards zero.
Compliant Solution
To provide a true (never negative) modulo operation, use the IMOD ("integer modulo") macro:
| Code Block | ||
|---|---|---|
| ||
/* modulo macro giving non-negative result */
#define IMOD(i, j) (((i) % (j)) < 0 ? ((i) % (j)) + (j) : ((i) % (j)))
/* if i % j is never negative, replace with the following line: */
/* #define IMOD(i, j) ((i) % (j)) */
|
Risk Assessment
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
STR07-A | 1 (low) | 1 (unlikely) | 2 (medium) | P2 | L3 |
This code also violates ERR02-C. Avoid in-band error indicators.
Noncompliant Code Example
Taking the absolute value of the modulo operation returns a positive value:
| Code Block | ||||
|---|---|---|---|---|
| ||||
int insert(int index, int *list, int size, int value) {
if (size != 0) {
index = abs((index + 1) % size);
list[index] = value;
return index;
}
else {
return -1;
}
}
|
However, this noncompliant code example violates INT01-C. Use size_t or rsize_t for all integer values representing the size of an object. There is also a possibility that (index + 1) could result in a signed integer overflow in violation of INT32-C. Ensure that operations on signed integers do not result in overflow.
Compliant Solution (Unsigned Types)
The most appropriate solution in this case is to use unsigned types to eliminate any possible implementation-defined behavior, as in this compliant solution. For compliance with ERR02-C. Avoid in-band error indicators, this solution fills a result argument with the mathematical result and returns nonzero only if the operation succeeds.
| Code Block | ||||
|---|---|---|---|---|
| ||||
int insert(size_t* result, size_t index, int *list, size_t size, int value) {
if (size != 0 && size != SIZE_MAX) {
index = (index + 1) % size;
list[index] = value;
*result = index;
return 1;
}
else {
return 0;
}
}
|
Risk Assessment
Incorrectly assuming that the result of the remainder operator for signed operands will always be positive can lead to an out-of-bounds memory accessor other flawed logic.
Recommendation | Severity | Likelihood | Detectable | Repairable | Priority | Level |
|---|---|---|---|---|---|---|
INT10-C | High | Unlikely | No | No | P3 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Compass/ROSE | Could detect the specific noncompliant code example. 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 it would likely introduce many false positives | ||||||||
| Helix QAC |
| C3103 | |||||||
| LDRA tool suite |
| 584 S | Fully implemented | ||||||
| Parasoft C/C++test |
| CERT_C-INT10-a | The operands of the remainder operator '%' should be of unsigned integer types | ||||||
| Polyspace Bug Finder |
| Checks for tainted modulo operand (rec. fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
...
Related Guidelines
Bibliography
| [Beebe 2005] | |
| [ISO/IEC 9899:2011] | Subclause 6.5.5, |
...
| "Multiplicative Operators" | |
| [Microsoft 2007] | C Multiplicative Operators |
| [Sun 2005] | Appendix E, "Implementation-Defined ISO/IEC C90 Behavior" |
...
operators" \[[Microsoft 07|AA. C References#Microsoft 07]\] [C Multiplicative Operators|http://msdn2.microsoft.com/en-us/library/efa0csed(VS.80).aspx] \[[Sun 05|AA. C References#Sun 05]\] C User's Guide Sun Studio 11 819-3688-10 [http://docs.sun.com/source/819-3688/]. 2005. [Appendix E, "Implementation-Defined ISO/IEC C90 Behavior"|http://docs.sun.com/source/819-3688/c90.implementation.app.html]