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
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Unexpected State - Expression evaluates to unintended value, producing incorrect program behavior. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Precedence errors in authentication or authorization logic can bypass security checks. |
| Availability | Scope: 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
- MITRE Corporation. "CWE-783: Operator Precedence Logic Error." https://cwe.mitre.org/data/definitions/783.html
- CERT C Coding Standard. "EXP00-C. Use parentheses for precedence of operation."
- C++ Core Guidelines. "ES.41: If in doubt about operator precedence, parenthesize."