Operator Precedence Logic Error

Description

Operator Precedence Logic Error is a programming vulnerability where an expression uses operator precedence in a way that produces incorrect logic. Different operators have different precedence levels (order of evaluation), and when developers don't account for this, expressions may evaluate differently than intended. This is particularly dangerous in security-critical code such as authentication decisions, access control checks, or financial calculations, where incorrect logic can lead to security bypasses or data corruption.

Risk

Operator precedence errors can have severe security implications depending on context. In authentication code, incorrect precedence can cause authentication to always succeed or always fail. In access control logic, it can grant unauthorized access or deny legitimate users. In financial calculations, incorrect precedence can result in wrong amounts. The bugs are subtle and may not be caught in basic testing because they produce correct results for some inputs while failing for others. Code reviewers may also miss these issues if they don't carefully trace the evaluation order.

Solution

Use parentheses liberally to make expression evaluation order explicit, especially in security-critical code. Don't rely on memory of operator precedence rules—make intent clear through parentheses. During code review, pay special attention to expressions combining assignment, comparison, and logical operators. Use compiler warnings (like -Wparentheses in GCC) to catch suspicious expressions. Write unit tests that specifically target edge cases in boolean expressions. Consider breaking complex expressions into multiple statements with intermediate variables to improve clarity.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Unexpected State - Expression evaluates to unintended value, producing incorrect program behavior.
Access ControlScope: Access Control

Bypass Protection Mechanism - Precedence errors in authentication or authorization logic can bypass security checks.
AvailabilityScope: Availability

DoS: Crash - Unexpected values may cause crashes or undefined behavior.

Example Code

Vulnerable Code

// Vulnerable: Assignment vs comparison precedence
#include <stdio.h>

#define FAIL 0
#define SUCCESS 1

int AuthenticateUser(const char* username, const char* password) {
    // Returns SUCCESS or FAIL
    return checkCredentials(username, password);
}

int vulnerable_login(const char* username, const char* password) {
    int isUser;

    // Vulnerable: == has higher precedence than =
    // Evaluates as: isUser = (AuthenticateUser(...) == FAIL)
    // Instead of: (isUser = AuthenticateUser(...)) == FAIL
    if (isUser = AuthenticateUser(username, password) == FAIL) {
        // This branch executes when auth SUCCEEDS (== FAIL is false, so isUser = 0)
        return FAIL;  // Wrong! Fails authenticated users
    }

    // Reaches here when auth FAILS (== FAIL is true, so isUser = 1)
    return SUCCESS;  // Wrong! Succeeds for failed auth
}
// Vulnerable: Financial calculation with wrong precedence
public class VulnerableFinance {

    public double vulnerableROI(double currentValue, double initialInvestment) {
        // Vulnerable: Division before subtraction
        // Evaluates as: currentValue - (initialInvestment / initialInvestment)
        // Which is: currentValue - 1.0
        double returnROI = currentValue - initialInvestment / initialInvestment;

        // If currentValue=150, initialInvestment=100
        // Expected: (150-100)/100 = 0.5 (50% ROI)
        // Actual: 150 - (100/100) = 150 - 1 = 149 (completely wrong!)
        return returnROI;
    }

    public boolean vulnerableThreshold(int value) {
        // Vulnerable: Bitwise AND vs comparison precedence
        // & has lower precedence than ==
        // Evaluates as: (value & 0xFF) == (0 != 0)
        // Which is: (value & 0xFF) == false
        if (value & 0xFF == 0 != 0) {  // Completely wrong logic
            return true;
        }
        return false;
    }
}
// Vulnerable: Pointer and comparison precedence
void vulnerable_pointer(char* str) {
    // Vulnerable: != has lower precedence than *
    // Evaluates as: *str != ('\0')  -- which is actually correct here
    // But consider:
    char* ptr = str;

    // Vulnerable: ++ has higher precedence than *
    // Evaluates as: *(ptr++) instead of (*ptr)++
    while (*ptr++ != '\0') {
        // ptr already incremented, may skip first char in processing
    }
}

// Vulnerable: Conditional and assignment
int vulnerable_conditional(int a, int b, int c) {
    // Vulnerable: ?: has lower precedence than =
    // This doesn't do what it looks like
    int result;
    result = a > b ? a : b = c;  // Assigns c to b, returns c

    return result;
}
# Vulnerable: Python-specific precedence issues
def vulnerable_check(value, flag):
    # Vulnerable: 'not' applies only to first operand
    # Reads as: (not value) in [1, 2, 3]
    # Instead of: not (value in [1, 2, 3])
    if not value in [1, 2, 3] and flag:
        return True
    return False

def vulnerable_comparison(x, y, z):
    # Vulnerable: Chained comparison confusion
    # Python allows: 1 < x < 10 (works correctly)
    # But this is confusing:
    result = x < y == z  # This is (x < y) and (y == z), not (x < y) == z
    return result

Fixed Code

// Fixed: Explicit parentheses for authentication
#include <stdio.h>

#define FAIL 0
#define SUCCESS 1

int fixed_login(const char* username, const char* password) {
    int isUser;

    // Fixed: Parentheses make evaluation order explicit
    if ((isUser = AuthenticateUser(username, password)) == FAIL) {
        // Now correctly enters this branch when auth fails
        return FAIL;
    }

    // Correctly reaches here only when auth succeeds
    return SUCCESS;
}

// Even better: Separate assignment from comparison
int fixed_login_v2(const char* username, const char* password) {
    // Fixed: Clear, unambiguous code
    int authResult = AuthenticateUser(username, password);

    if (authResult == FAIL) {
        return FAIL;
    }

    return SUCCESS;
}
// Fixed: Financial calculation with correct precedence
public class FixedFinance {

    public double fixedROI(double currentValue, double initialInvestment) {
        // Fixed: Parentheses ensure correct order
        double returnROI = (currentValue - initialInvestment) / initialInvestment;

        // If currentValue=150, initialInvestment=100
        // Correctly: (150-100)/100 = 0.5 (50% ROI)
        return returnROI;
    }

    // Even better: Use intermediate variables
    public double fixedROI_v2(double currentValue, double initialInvestment) {
        double profit = currentValue - initialInvestment;
        double returnROI = profit / initialInvestment;
        return returnROI;
    }

    public boolean fixedThreshold(int value) {
        // Fixed: Parentheses clarify intent
        if ((value & 0xFF) != 0) {
            return true;
        }
        return false;
    }
}
// Fixed: Clear pointer operations
void fixed_pointer(char* str) {
    char* ptr = str;

    // Fixed: Explicit parentheses and clearer logic
    while (*ptr != '\0') {
        // Process current character
        processChar(*ptr);
        ptr++;  // Increment separately
    }
}

// Fixed: Clear conditional assignment
int fixed_conditional(int a, int b, int c) {
    int result;

    // Fixed: Use parentheses or separate statements
    result = (a > b) ? a : b;  // Clear ternary
    // Or if you need to conditionally assign:
    if (a > b) {
        result = a;
    } else {
        result = b;
        b = c;  // Separate side effect
    }

    return result;
}
# Fixed: Python with explicit parentheses
def fixed_check(value, flag):
    # Fixed: Parentheses clarify 'not' scope
    if (not (value in [1, 2, 3])) and flag:
        return True
    # Or even clearer:
    if value not in [1, 2, 3] and flag:  # Use 'not in' operator
        return True
    return False

def fixed_comparison(x, y, z):
    # Fixed: Explicit about what we're comparing
    result = (x < y) == z  # Compare boolean (x < y) with z
    # Or if we want chained comparison:
    result = (x < y) and (y == z)  # Chain explicitly
    return result

CVE Examples

  • CVE-2008-2516: Authentication bypass caused by operator precedence error in conditional.
  • CVE-2008-0599: Buffer size miscalculation due to incorrect operator precedence.
  • CVE-2001-1155: DNS verification bypass resulting from precedence error.

References

  1. MITRE Corporation. "CWE-783: Operator Precedence Logic Error." https://cwe.mitre.org/data/definitions/783.html
  2. CERT C Coding Standard. "EXP00-C. Use parentheses for precedence of operation."
  3. C++ Core Guidelines. "ES.41: If in doubt about operator precedence, parenthesize."