CWE-783 is the bug where an expression does not mean what the author thinks it means, because operators bind in a different order than intended. Often it is just a bug. In security code, such as an authentication decision, it becomes a vulnerability.
It hits C and C++ code most often, but the rule applies in any language with a precedence table.
How it happens
Programmers read an expression left to right, as prose. The compiler follows a fixed order of operations. When the two disagree, the code still compiles and often passes casual testing.
The MITRE record has telling examples:
- CVE-2008-2516: an authentication module let attackers bypass authentication because it used
(x = call(args) == SUCCESS)instead of((x = call(args)) == SUCCESS). - CVE-2008-0599: a language interpreter computed a wrong buffer size by writing
size = ptr ? X : Ywhere the author needed the whole conditional in parentheses. - CVE-2001-1155: a product did not properly check the result of a reverse DNS lookup because of operator precedence, allowing bypass of DNS-based access restrictions.
The first one is worth studying. In C, the comparison operator binds tighter than assignment. So x = call(args) == SUCCESS compares first, then assigns the true or false result to x. Now x holds 0 or 1, not the return code, and later checks on x behave oddly.
int rc;
if (rc = check_password(user, pw) == OK) { /* wrong: rc is 0 or 1 */
grant_access();
}
A related classic is mixing bitwise and comparison operators, where a mask test ends up compared before it is masked.
What an attacker gains
The record says consequences vary with the code around the mistake. It lists confidentiality, integrity and availability, and an unexpected state. With an authentication check, that can mean getting in without valid credentials. With a size calculation, it can mean a buffer that is too small, which opens the way to memory corruption.
How to find it
MITRE notes static analysis can find some cases. Practical steps:
- Turn on compiler warnings that flag suspicious parentheses and assignments inside conditions, and treat them as errors.
- Run a linter or static analyzer in CI.
- In code review, read every condition in authentication and authorization code twice.
- Write unit tests for both outcomes of each security check, including failure.
How to fix it
Add parentheses and split complex expressions. Do not rely on memory of the precedence table.
int rc = check_password(user, pw);
if (rc == OK) {
grant_access();
}
Keep security decisions in small, named steps. Separate the call, the stored result and the comparison. Prefer languages and styles where assignment inside a condition is not allowed.
Quick check
Why did the CVE-2008-2516 expression give the wrong result?
The comparison ran before the assignment, so the variable held the true or false result and not the return code.
What is the cheapest defense?
Explicit parentheses, split statements, compiler warnings as errors, and tests that cover the failure path.