...
Code Block | ||||
---|---|---|---|---|
| ||||
LinkedList bannedUsers; int is_banned(User usr) { int x = 0; Node cur_node = (bannedUsers->head); while (cur_node != NULL) { if(!strcmp((char *)cur_node->data, usr->name)) { x++; } cur_node = cur_node->next; } return x; } void processRequest(User usr) { if(is_banned(usr) == 1) { return; } serveResults(); } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
LinkedList bannedUsers;
int is_banned(User usr) {
int x = 0;
Node cur_node = (bannedUsers->head);
while(cur_node != NULL) {
if (strcmp((char *)cur_node->data, usr->name)==0) {
x++;
}
cur_node = cur_node->next;
}
return x;
}
void processRequest(User usr) {
if (is_banned(usr) != 0) {
return;
}
serveResults();
}
|
...
Although failures are frequently indicated by a return value of 0, some common conventions may conflict in the future with code in which the test for nonzero is not explicit. In this case, defaulting the test for nonzero welcomes bugs if and when a developer modifies foovalidateUser()
to return an error code or −1 rather than 0 to indicate a failure (all of which are also common conventions).
...
Code Block | ||||
---|---|---|---|---|
| ||||
errno_t validateUser(User usr) { if(list_contains(allUsers, usr) == 0) { return 303; /* User not found error code */ } if(list_contains(validUsers, usr) == 0) { return 304; /* Invalid user error code */ } return 0; } void processRequest(User usr, Request request) { if(!validateUser(usr)) { return "invalid user"; } else { serveResults(); } } |
In this code, the programmer intended to add error code functionality to indicate the cause of a validation failure. The new code, however, validates any invalid or nonexisting user. Because there is no explicit test in processRequest()
, the logical error is not obvious and seems correct by certain conventions.
Compliant Solution
The following This compliant code is preferable for improved maintenance. By defining what constitutes a failure and explicitly testing for it, the behavior is clearly implied, and future modifications are more likely to preserve it. If a future modification is made, such as in the previous example, it is immediately obvious that the if
statement in processRequest()
does not correctly utilize the specification of validateUser()
.
...
Code Block | ||||
---|---|---|---|---|
| ||||
void login(char *usr, char *pw) { User user = find_user(usr); if (!strcmp((user->password),pw_given)) { grantAccess(); } else { denyAccess("Incorrect Password"); } } |
The preceding code works correctly. However, to simplify the login code or to facilitate checking a user's password more than once, a programmer can separate the password-checking code from the login function in the following way:
Code Block | ||||
---|---|---|---|---|
| ||||
int check_password(User *user, char *pw_given) { if (!strcmp((user->password),pw_given)) { return 1; } return 0; } void login(char *usr, char *pw) { User user = find_user(usr); if (!check_password(user, pw)) { grantAccess(); } else { denyAccess("Incorrect Password"); } } |
In an attempt to leave the previous logic intact, the developer just replaces strcmp()
with a call to the new function. However, doing so produces incorrect behavior. In this case, any user who inputs an incorrect password is granted access. Again, two conventions conflict and produce code that is easily corrupted when modified. To make code maintainable and to avoid these conflicts, such a result should never be defaulted.
Compliant Solution
The following approach to This compliant solution, using a comparison function for this purpose, is the preferred approach. By performing an explicit test, any programmer who wishes to modify the equality test can clearly see the implied behavior and convention that is being followed.
Code Block | ||||
---|---|---|---|---|
| ||||
void login(char *usr, char *pw) { User user = find_user(usr); if (strcmp((user->password),pw_given) == 0) { grantAccess(); } else { denyAccess("Incorrect Password"); } } |
Risk Assessment
Code that does not conform to the common practices presented is difficult to maintain. Bugs can easily arise when modifying helper functions that evaluate true/false or success/failure. Bugs can also easily arise when modifying code that tests for equality using a comparison function that obeys the same conventions as standard library functions such as strcmp
.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
EXP20-C |
Medium |
Probable |
Low | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description | ||||||
Astrée |
| Supported indirectly via MISRA C:2004 Rule 13.2. | |||||||
Axivion Bauhaus Suite |
| CertC-EXP20 | Fully implemented | ||||||
Helix QAC |
| C3344, C4116 | |||||||
LDRA tool suite |
| 114 S | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_C-EXP20-a | Avoid comparing values with TRUE macro/enum constant using equality operators ("==", "!=") | ||||||
PC-lint Plus |
| 697 | Partially supported: reports comparisons of Boolean values to constants other than 0 |
Bibliography
[StackOvflw 2009] | "Should I Return TRUE/FALSE Values from a C Function?" |
...
...