Assigning instead of Comparing
Description
Assigning instead of Comparing occurs when a programmer uses the assignment operator (=) where a comparison operator (==) was intended. This mistake commonly happens in conditional statements (if, while, for) where the intent is to compare values, but the single equals sign causes assignment instead. The expression evaluates to the assigned value, not a boolean comparison result, leading to logic that behaves differently than intended.
Risk
This error creates serious security vulnerabilities and logic bugs. In authentication checks, assigning instead of comparing can cause the condition to always be true (or false), bypassing security controls. The assigned value replaces the original, causing data corruption. In loops, it can cause infinite loops or premature termination. Because the code compiles without errors and often produces subtly wrong behavior, these bugs can persist undetected through testing and into production.
Solution
Enable compiler warnings that flag assignment in conditional contexts (-Wparentheses in GCC/Clang). Use static analysis tools that detect assignment in conditions. Apply Yoda conditions (constant on left side) so assignment causes a compile error. Establish coding standards requiring explicit comparison (== or !=) in all conditionals. Use languages or linters that disallow or warn about assignment in conditions. In code review, specifically verify operators in security-critical conditions.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Authentication Bypass Security checks may always pass or always fail. |
| Integrity | Scope: Data Modification Variables are unexpectedly modified by assignment. |
| Availability | Scope: Logic Errors Program flow deviates from intended behavior. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Authentication bypass
int login_vulnerable(User* user, const char* password) {
if (user->authenticated = check_password(user, password)) {
// BUG! This ASSIGNS the result to authenticated
// If check_password returns any non-zero value, condition is true
// Even error codes might be non-zero!
return SUCCESS;
}
return FAILURE;
}
// VULNERABLE: Variable modification in condition
void search_vulnerable(int* data, int size, int target) {
for (int i = 0; i < size; i++) {
if (data[i] = target) { // BUG! Assigns target to data[i]
// Every element becomes equal to target!
// Condition true unless target is 0
printf("Found at index %d\n", i);
break;
}
}
}
// VULNERABLE: Infinite loop potential
void process_vulnerable(FILE* file) {
char buffer[256];
int done = 0;
while (done = 0) { // BUG! Always assigns 0, loop never executes
if (fgets(buffer, sizeof(buffer), file) == NULL) {
done = 1;
}
process_line(buffer);
}
}
// VULNERABLE: Pointer check bypassed
void use_pointer_vulnerable(int* ptr) {
if (ptr = NULL) { // BUG! Assigns NULL to ptr!
printf("Pointer is NULL\n");
return;
}
// ptr is now NULL, will crash!
*ptr = 42;
}
// VULNERABLE: String comparison mistake
int verify_token_vulnerable(const char* token) {
char* valid_token = get_valid_token();
if (token = valid_token) { // BUG! Assigns valid_token to token
// Always true (unless valid_token is NULL)
return 1; // Security bypass!
}
return 0;
}
// VULNERABLE: Nested condition assignment
int complex_check_vulnerable(int a, int b, int c) {
if ((a = b) && (b == c)) { // BUG! First part assigns
// a is now equal to b
// Condition based on whether b is non-zero AND b equals c
return 1;
}
return 0;
}
// VULNERABLE: Loop counter modification
void iterate_vulnerable(int* arr, int count) {
for (int i = 0; i < count; i++) {
if (i = arr[i]) { // BUG! Assigns arr[i] to i
// Loop counter corrupted!
printf("Value: %d\n", i);
}
}
}
// VULNERABLE: Enum comparison
typedef enum { STATE_INIT, STATE_RUN, STATE_DONE } State;
int check_state_vulnerable(State* state) {
if (*state = STATE_DONE) { // BUG! Assigns STATE_DONE
// state is now STATE_DONE regardless of previous value
// Condition always true (STATE_DONE != 0)
return 1;
}
return 0;
}
// VULNERABLE: C++ class member assignment
class VulnerableAccount {
bool locked;
int balance;
public:
bool canWithdraw(int amount) {
if (locked = false) { // BUG! Assigns false to locked
// Account is now unlocked!
// Condition always false
return false; // But logic is still wrong
}
return balance >= amount;
}
bool isOverdrawn() {
if (balance = 0) { // BUG! Sets balance to 0
// Balance zeroed!
// Condition always false (0 is falsy)
return false;
}
return balance < 0; // Never reached, and balance is now 0
}
};
// VULNERABLE: Smart pointer assignment
void process_vulnerable(std::shared_ptr<Object> obj) {
std::shared_ptr<Object> other = getOther();
if (obj = other) { // BUG! Assigns other to obj
// obj now points to other
// obj's original object may be destroyed!
obj->process();
}
}
// VULNERABLE: Boolean expression
bool validate_vulnerable(int value, bool flag) {
if (flag = (value > 0)) { // BUG! Assigns result to flag
// flag is modified
// Original flag value lost
return true;
}
return false;
}
// VULNERABLE: Exception handling
void handle_error_vulnerable(int* error_code) {
try {
doRiskyOperation();
} catch (const std::exception& e) {
if (*error_code = ERROR_EXCEPTION) { // BUG! Assigns error code
// error_code is set, but condition always true
// (unless ERROR_EXCEPTION is 0)
log_error(e.what());
}
}
}
// JavaScript allows assignment in conditions
// VULNERABLE: Authentication check
function loginVulnerable(user, password) {
if (user.authenticated = checkPassword(password)) {
// BUG! Assigns result to user.authenticated
// Truthy return value = authenticated
return { success: true };
}
return { success: false };
}
// VULNERABLE: Array search
function findVulnerable(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] = target) { // BUG! Modifies array!
return i;
}
}
return -1;
}
// VULNERABLE: Object property check
function checkPermission(user) {
if (user.role = 'admin') { // BUG! Makes everyone admin!
return true;
}
return false;
}
Fixed Code
// SAFE: Correct comparison operator
int login_safe(User* user, const char* password) {
int result = check_password(user, password);
if (result == SUCCESS) { // Correct comparison
user->authenticated = 1; // Explicit assignment when intended
return SUCCESS;
}
return FAILURE;
}
// SAFE: Yoda condition prevents assignment mistake
void search_safe(int* data, int size, int target) {
for (int i = 0; i < size; i++) {
if (target == data[i]) { // Yoda: constant on left
// If you wrote = by mistake: "target = data[i]" causes error
// because you can't assign to a literal
printf("Found at index %d\n", i);
break;
}
}
}
// SAFE: Correct comparison in while
void process_safe(FILE* file) {
char buffer[256];
int done = 0;
while (done == 0) { // Correct comparison
if (fgets(buffer, sizeof(buffer), file) == NULL) {
done = 1;
continue;
}
process_line(buffer);
}
}
// Better: use condition directly
void process_safe_v2(FILE* file) {
char buffer[256];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
process_line(buffer);
}
}
// SAFE: Correct NULL check
void use_pointer_safe(int* ptr) {
if (ptr == NULL) { // Correct comparison
printf("Pointer is NULL\n");
return;
}
*ptr = 42; // Safe, ptr is valid
}
// Yoda style NULL check
void use_pointer_yoda(int* ptr) {
if (NULL == ptr) { // Can't assign to NULL
printf("Pointer is NULL\n");
return;
}
*ptr = 42;
}
// SAFE: String comparison with strcmp
int verify_token_safe(const char* token) {
char* valid_token = get_valid_token();
if (token != NULL && valid_token != NULL &&
strcmp(token, valid_token) == 0) { // Correct string comparison
return 1;
}
return 0;
}
// SAFE: Explicit comparison in all conditions
int complex_check_safe(int a, int b, int c) {
if ((a == b) && (b == c)) { // Both are comparisons
return 1;
}
return 0;
}
// SAFE: Loop counter preserved
void iterate_safe(int* arr, int count) {
for (int i = 0; i < count; i++) {
if (i == arr[i]) { // Comparison, not assignment
printf("Value at index %d equals index\n", i);
}
}
}
// SAFE: Enum comparison
int check_state_safe(State* state) {
if (*state == STATE_DONE) { // Correct comparison
return 1;
}
return 0;
}
// Yoda style for enums
int check_state_yoda(State* state) {
if (STATE_DONE == *state) { // Can't assign to enum constant
return 1;
}
return 0;
}
// SAFE: C++ with correct operators
class SafeAccount {
bool locked;
int balance;
public:
bool canWithdraw(int amount) const { // const prevents modification
if (locked == false) { // Correct comparison
// Or better: if (!locked)
return balance >= amount;
}
return false;
}
bool isOverdrawn() const {
if (balance == 0) { // Correct comparison
return false;
}
return balance < 0;
}
// Better style: direct boolean
bool isLocked() const {
return locked; // No condition needed
}
};
// SAFE: Smart pointer comparison
void process_safe(std::shared_ptr<Object> obj) {
std::shared_ptr<Object> other = getOther();
if (obj == other) { // Correct comparison
obj->process();
}
}
// SAFE: Boolean without assignment
bool validate_safe(int value, bool flag) {
bool isPositive = value > 0; // Separate assignment
if (flag && isPositive) { // Logical comparison
return true;
}
return false;
}
// Or directly
bool validate_safe_v2(int value, bool flag) {
return flag && (value > 0);
}
// SAFE: Exception handling
void handle_error_safe(int* error_code) {
try {
doRiskyOperation();
} catch (const std::exception& e) {
*error_code = ERROR_EXCEPTION; // Explicit assignment
log_error(e.what());
}
}
// SAFE: Using [[nodiscard]] and const
class SafeResult {
public:
[[nodiscard]] bool operator==(const SafeResult& other) const;
// Compiler warns if result is ignored
// const prevents accidental assignment in comparisons
};
// SAFE: JavaScript with correct operators
// Use strict equality
function loginSafe(user, password) {
const result = checkPassword(password);
if (result === true) { // Strict comparison
user.authenticated = true; // Explicit assignment
return { success: true };
}
return { success: false };
}
// SAFE: Don't modify array in search
function findSafe(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) { // Strict equality
return i;
}
}
return -1;
}
// Or use Array methods
function findSafeModern(arr, target) {
return arr.indexOf(target);
// Or: arr.findIndex(x => x === target);
}
// SAFE: Proper permission check
function checkPermission(user) {
if (user.role === 'admin') { // Strict comparison
return true;
}
return false;
}
// Even better: direct return
function checkPermissionConcise(user) {
return user.role === 'admin';
}
// ESLint rule: no-cond-assign
// Catches assignment in conditions
Exploited in the Wild
SSL/TLS Implementation Bugs
Multiple SSL/TLS implementations have had vulnerabilities where assignment instead of comparison led to improper certificate validation.
Authentication System Bypasses
Login systems have been bypassed due to assignment bugs that caused authentication checks to always succeed.
Access Control Failures
Access control systems have failed when permission checks used assignment instead of comparison.
Tools to test/exploit
-
GCC/Clang Warnings — -Wparentheses warns about assignment in conditions.
-
ESLint — no-cond-assign rule for JavaScript.
-
Coverity — detects assignment/comparison confusion.
-
SonarQube — rule for assignment in conditions.
CVE Examples
-
CVE-2003-0161 — Sendmail prescan() assignment bug.
-
Various authentication bypass CVEs from assignment mistakes.
-
Multiple CVEs in embedded systems from comparison errors.
References
-
MITRE. "CWE-481: Assigning instead of Comparing." https://cwe.mitre.org/data/definitions/481.html
-
CERT C. "EXP45-C: Do not perform assignments in selection statements." https://wiki.sei.cmu.edu/confluence/display/c/