Use of Incorrect Operator

Description

Use of Incorrect Operator occurs when a programmer accidentally uses the wrong operator in an expression, leading to unintended behavior. Common examples include using assignment (=) instead of comparison (==), bitwise AND (&) instead of logical AND (&&), or bitwise OR (|) instead of logical OR (||). These mistakes often compile without error because the expressions are syntactically valid, but they produce incorrect results or have dangerous side effects.

Risk

Incorrect operator usage leads to logic errors, security bypasses, and data corruption. Using assignment instead of comparison in conditionals always evaluates to the assigned value, bypassing intended checks. Bitwise operators on boolean values produce different results than logical operators, especially with short-circuit evaluation. In security-critical code, these errors can disable authentication checks, bypass authorization, or corrupt data. The bugs are subtle and often pass code review because they look similar to correct code.

Solution

Enable compiler warnings for suspicious operator usage (-Wall, -Wparentheses). Use static analysis tools that detect operator misuse. Place constants on the left side of comparisons (Yoda conditions) so assignment causes a compile error. In conditionals, always explicitly compare against expected values rather than relying on implicit boolean conversion. Code review should specifically check operators in security-critical conditions. Use consistent coding standards that make correct operator usage clear.

Common Consequences

ImpactDetails
Access ControlScope: Security Bypass

Wrong operators in authentication checks can bypass security.
IntegrityScope: Data Corruption

Assignment instead of comparison modifies data unexpectedly.
LogicScope: Incorrect Behavior

Wrong operators produce incorrect program logic.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Assignment instead of comparison
int authenticate_vulnerable(const char* password, const char* correct) {
    int authenticated = 0;

    if (authenticated = check_password(password, correct)) {
        // Bug! This assigns the return value, always "succeeds" if non-zero
        // Even if check_password returns an error code!
        grant_access();
    }

    return authenticated;  // Returns the assigned value!
}

// VULNERABLE: Assignment in conditional
void process_vulnerable(int* data, int expected) {
    if (*data = expected) {  // Bug! Assigns expected to *data
        // Always true unless expected is 0
        printf("Match found!\n");
    }
}

// VULNERABLE: Bitwise AND instead of logical AND
int check_permissions_vulnerable(User* user) {
    if (user->role == ADMIN & user->authenticated) {
        // Bug! Bitwise AND, no short-circuit evaluation
        // Also evaluates user->authenticated even if role != ADMIN
        return 1;
    }
    return 0;
}

// VULNERABLE: Bitwise OR instead of logical OR
int is_valid_vulnerable(int* ptr, int flag) {
    if (ptr == NULL | flag == 0) {  // Bug! Bitwise OR
        // No short-circuit: evaluates flag == 0 even if ptr is NULL
        // Also produces wrong result for non-boolean values
        return 0;
    }
    return 1;
}

// VULNERABLE: Single ampersand in condition
int validate_input_vulnerable(char* input, int len) {
    if (input != NULL & len > 0 & len < MAX_LEN) {
        // Bug! Should be && for short-circuit evaluation
        // len > 0 evaluated even if input is NULL
        process(input, len);
        return 1;
    }
    return 0;
}

// VULNERABLE: Wrong comparison operator
int in_range_vulnerable(int value, int min, int max) {
    if (value > min && value > max) {  // Bug! Second should be <
        return 1;  // Never true for valid ranges!
    }
    return 0;
}

// VULNERABLE: Negation of wrong part
int not_equal_vulnerable(int a, int b) {
    if (!a == b) {  // Bug! Negates a, then compares to b
        // Actually means: ((!a) == b)
        // Should be: !(a == b) or (a != b)
        return 1;
    }
    return 0;
}

// VULNERABLE: Increment instead of addition
int calculate_vulnerable(int base, int offset) {
    return base ++ offset;  // Bug! Syntax error or unexpected behavior
    // Probably meant: base + offset
}

// VULNERABLE: Division instead of modulo
int is_even_vulnerable(int n) {
    if (n / 2 == 0) {  // Bug! Should be n % 2
        return 1;  // Only true when n is 0 or 1!
    }
    return 0;
}
// VULNERABLE: C++ specific issues
class VulnerableClass {
    int value;
    bool initialized;

public:
    // VULNERABLE: Assignment in constructor initializer
    VulnerableClass(int v) : value(v), initialized(true) {
        if (value = 0) {  // Bug! Assigns 0, condition always false
            throw std::invalid_argument("Zero not allowed");
        }
    }

    // VULNERABLE: Overloaded operator confusion
    bool operator==(const VulnerableClass& other) {
        return value = other.value;  // Bug! Assignment, not comparison!
        // Modifies this->value!
    }

    // VULNERABLE: Pointer vs address-of confusion
    void process(int* ptr) {
        if (*ptr && ptr) {  // Bug! Order wrong, should check ptr first
            // Dereferences ptr before checking if it's valid!
            doWork(*ptr);
        }
    }
};

// VULNERABLE: Stream operator confusion
void output_vulnerable(std::ostream& os, int value) {
    if (os < value) {  // Bug! Should be os << value for output
        // Compares os to value (probably compilation error or weird behavior)
    }
}

// VULNERABLE: Smart pointer comparison
void compare_pointers_vulnerable(std::shared_ptr<Object> a,
                                  std::shared_ptr<Object> b) {
    if (a = b) {  // Bug! Assignment, not comparison
        // a now points to same object as b!
        process(*a);
    }
}
// Java prevents some issues, but others remain
public class VulnerableJava {

    // VULNERABLE: Bitwise instead of logical (legal in Java)
    public boolean checkVulnerable(Object obj, int value) {
        // Bug! Bitwise AND, no short-circuit
        if (obj != null & obj.hashCode() == value) {
            // Evaluates obj.hashCode() even if obj is null!
            return true;
        }
        return false;
    }

    // VULNERABLE: Confusing == with equals()
    public boolean compareStrings_vulnerable(String a, String b) {
        if (a == b) {  // Bug! Compares references, not content
            return true;
        }
        return false;
    }

    // VULNERABLE: Negation scope
    public boolean notEqual_vulnerable(int a, int b) {
        if (!a == b) {  // Compilation error in Java, but shows intent
            return true;
        }
        return false;
    }

    // VULNERABLE: Operator precedence mistake
    public int calculate_vulnerable(int a, int b, int c) {
        return a + b * c;  // Not a bug, but might be unintended
        // Did they mean (a + b) * c?
    }
}

Fixed Code

// SAFE: Comparison with explicit operator
int authenticate_safe(const char* password, const char* correct) {
    int authenticated = 0;

    int result = check_password(password, correct);
    if (result == SUCCESS) {  // Explicit comparison
        authenticated = 1;
        grant_access();
    }

    return authenticated;
}

// SAFE: Yoda conditions (constant on left)
void process_safe(int* data, int expected) {
    if (expected == *data) {  // If you write = by mistake, compiler error!
        printf("Match found!\n");
    }
}

// SAFE: Logical AND for boolean conditions
int check_permissions_safe(User* user) {
    if (user->role == ADMIN && user->authenticated) {
        // Correct! Logical AND with short-circuit evaluation
        return 1;
    }
    return 0;
}

// SAFE: Logical OR for boolean conditions
int is_valid_safe(int* ptr, int flag) {
    if (ptr == NULL || flag == 0) {  // Correct! Logical OR
        return 0;
    }
    return 1;
}

// SAFE: Proper short-circuit evaluation
int validate_input_safe(char* input, int len) {
    if (input != NULL && len > 0 && len < MAX_LEN) {
        // Correct! Short-circuits if input is NULL
        process(input, len);
        return 1;
    }
    return 0;
}

// SAFE: Correct comparison operators
int in_range_safe(int value, int min, int max) {
    if (value > min && value < max) {  // Correct comparison
        return 1;
    }
    return 0;
}

// Or with inclusive bounds
int in_range_inclusive_safe(int value, int min, int max) {
    if (value >= min && value <= max) {
        return 1;
    }
    return 0;
}

// SAFE: Correct negation
int not_equal_safe(int a, int b) {
    if (a != b) {  // Direct not-equal operator
        return 1;
    }
    return 0;

    // Or with explicit parentheses
    if (!(a == b)) {
        return 1;
    }
}

// SAFE: Correct arithmetic operator
int calculate_safe(int base, int offset) {
    return base + offset;  // Clear addition
}

// SAFE: Correct modulo operation
int is_even_safe(int n) {
    if (n % 2 == 0) {  // Correct modulo
        return 1;
    }
    return 0;
}

// SAFE: Explicit parentheses for clarity
int complex_condition_safe(int a, int b, int c) {
    // Use parentheses to make intent clear
    if ((a > 0) && ((b < 10) || (c == 0))) {
        return 1;
    }
    return 0;
}

// SAFE: Separate assignment from condition
int process_with_assignment_safe(char* buffer, int* error) {
    // Assign first
    int result = read_data(buffer);

    // Then check
    if (result < 0) {
        *error = result;
        return 0;
    }

    return 1;
}
// SAFE: C++ with correct operators
class SafeClass {
    int value;
    bool initialized;

public:
    // SAFE: Proper comparison in constructor
    SafeClass(int v) : value(v), initialized(true) {
        if (value == 0) {  // Correct comparison
            throw std::invalid_argument("Zero not allowed");
        }
    }

    // SAFE: Comparison operator doesn't modify state
    bool operator==(const SafeClass& other) const {  // Note: const!
        return value == other.value;  // Correct comparison
    }

    // SAFE: Correct null check order
    void process(int* ptr) {
        if (ptr && *ptr) {  // Check ptr first, then dereference
            doWork(*ptr);
        }
    }

    // SAFE: Explicit comparison
    bool isValid() const {
        return initialized == true && value > 0;
    }
};

// SAFE: Stream operations
void output_safe(std::ostream& os, int value) {
    os << value;  // Correct output operator
}

// SAFE: Smart pointer comparison
void compare_pointers_safe(std::shared_ptr<Object> a,
                           std::shared_ptr<Object> b) {
    if (a == b) {  // Correct comparison
        process(*a);
    }

    // Or compare pointed objects
    if (a && b && *a == *b) {
        processEqual(*a, *b);
    }
}

// SAFE: Using [[nodiscard]] to catch ignored results
class SafeResult {
public:
    [[nodiscard]] bool operator==(const SafeResult& other) const;
};
// SAFE: Java with correct operators
public class SafeJava {

    // SAFE: Logical AND with short-circuit
    public boolean checkSafe(Object obj, int value) {
        if (obj != null && obj.hashCode() == value) {
            // Short-circuits if obj is null
            return true;
        }
        return false;
    }

    // SAFE: Use equals() for string comparison
    public boolean compareStrings_safe(String a, String b) {
        if (a == null || b == null) {
            return a == b;  // Both null or one null
        }
        return a.equals(b);  // Content comparison
    }

    // Or use Objects.equals() for null-safe comparison
    public boolean compareStrings_safe_v2(String a, String b) {
        return Objects.equals(a, b);
    }

    // SAFE: Explicit not-equal
    public boolean notEqual_safe(int a, int b) {
        return a != b;
    }

    // SAFE: Parentheses for clarity
    public int calculate_safe(int a, int b, int c) {
        return (a + b) * c;  // Clear intent with parentheses
    }

    // SAFE: Using Optional to avoid null checks
    public boolean processOptional(Optional<Object> obj, int value) {
        return obj.filter(o -> o.hashCode() == value).isPresent();
    }
}

Exploited in the Wild

SSL/TLS Bypass via Assignment Bug

The infamous "goto fail" bug in Apple's SSL implementation involved a logic error that could be seen as related to incorrect operator/control flow issues.

Authentication Bypass in Web Applications

Web applications have had authentication bypasses where assignment operators were used instead of comparison in login checks.

Privilege Escalation via Logic Errors

Incorrect operators in privilege checking code have led to privilege escalation vulnerabilities.


Tools to test/exploit


CVE Examples

  • CVE-2014-1266 — Apple SSL "goto fail" (control flow related).

  • Various authentication bypass CVEs due to operator misuse.

  • Logic error CVEs from incorrect comparison operators.


References

  1. MITRE. "CWE-480: Use of Incorrect Operator." https://cwe.mitre.org/data/definitions/480.html

  2. CERT C. "EXP45-C: Do not perform assignments in selection statements." https://wiki.sei.cmu.edu/confluence/display/c/