Expression is Always False
Description
Expression is Always False occurs when a conditional expression evaluates to false under all possible circumstances due to logical contradictions, type constraints, or constant values. Common patterns include comparisons of unsigned values to negative numbers (always false), contradictory compound conditions, comparing a variable to a value outside its possible range, and redundant checks after earlier validating conditions. The code protected by the condition never executes, making it effectively dead code.
Risk
Always-false expressions indicate logic errors that often have security implications. Security checks that never trigger provide no protection. Error handling code that never executes leaves errors unhandled. Bounds checking that never fails allows buffer overflows. The programmer's intent is not realized, and the resulting behavior differs from the expected safe operation. Attackers can exploit the gap between assumed and actual program behavior.
Solution
Enable compiler warnings for tautological comparisons and impossible conditions. Use static analysis tools that detect always-false expressions. Understand type constraints—unsigned values cannot be negative. Review compound conditions for contradictions. Track value ranges through code paths. Verify that defensive checks can actually trigger. Test boundary conditions to ensure checks activate when they should.
Common Consequences
| Impact | Details |
|---|---|
| Security | Scope: Bypassed Checks Security validations that never trigger provide no protection. |
| Reliability | Scope: Unhandled Errors Error handling that never executes leaves failures unaddressed. |
| Logic | Scope: Dead Code Code paths that can never be taken serve no purpose. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Unsigned comparison to negative (always false)
void check_size_vulnerable(size_t size) {
if (size < 0) { // ALWAYS FALSE! size_t is unsigned!
handle_error("Invalid size");
return;
}
// Proceeds even with "invalid" sizes
process(size);
}
// VULNERABLE: Unsigned underflow not caught
void process_buffer_vulnerable(unsigned int length) {
if (length < 0) { // ALWAYS FALSE!
return;
}
// length could be UINT_MAX (from underflow)
char* buffer = malloc(length);
// Potential issues with huge allocation
}
// VULNERABLE: Contradictory conditions
void validate_vulnerable(int value) {
if (value > 100 && value < 50) { // ALWAYS FALSE!
// Impossible condition
handle_special_case(); // Never executes!
}
}
// VULNERABLE: Range already checked
void check_range_vulnerable(int value) {
if (value < 0 || value > 100) {
return; // Invalid range rejected
}
// Here value is in [0, 100]
if (value < 0) { // ALWAYS FALSE! Already checked above!
handle_negative(); // Dead code!
}
process(value);
}
// VULNERABLE: Enum value comparison
typedef enum { NONE = 0, LOW = 1, HIGH = 2 } Priority;
void handle_priority_vulnerable(Priority p) {
if (p == NONE) {
return;
}
// p is now LOW or HIGH
if (p == NONE) { // ALWAYS FALSE!
// Dead code
handle_none();
}
}
// VULNERABLE: Pointer after NULL check
void use_pointer_vulnerable(int* ptr) {
if (ptr == NULL) {
return;
}
// ptr is not NULL here
if (ptr == NULL) { // ALWAYS FALSE!
log_error("NULL pointer"); // Never logs!
}
*ptr = 42;
}
// VULNERABLE: Boolean logic error
void check_flags_vulnerable(int flag1, int flag2) {
if (!flag1) {
return; // flag1 is false, return
}
// flag1 is true here
if (!flag1 && flag2) { // ALWAYS FALSE! flag1 is true!
// Dead code
handle_case();
}
}
// VULNERABLE: Char comparison on some platforms
void check_char_vulnerable(char c) {
// On platforms where char is unsigned:
if (c < 0) { // ALWAYS FALSE on unsigned char!
handle_negative_char();
}
}
// VULNERABLE: C++ with always-false conditions
void checkValue_vulnerable(unsigned int value) {
if (value < 0) { // ALWAYS FALSE!
throw std::invalid_argument("Negative value");
}
// Exception never thrown
}
// VULNERABLE: String comparison
void checkString_vulnerable(const std::string& str) {
if (str.length() < 0) { // ALWAYS FALSE! length() returns size_t
handleError();
}
}
// VULNERABLE: Vector size check
void processVector_vulnerable(const std::vector<int>& vec) {
if (vec.size() < 0) { // ALWAYS FALSE!
return;
}
// Always proceeds
for (int i : vec) {
process(i);
}
}
// VULNERABLE: Smart pointer after check
void usePointer_vulnerable(std::shared_ptr<Resource> ptr) {
if (!ptr) {
return;
}
// ptr is valid
if (!ptr) { // ALWAYS FALSE!
log("Null pointer"); // Dead code
}
ptr->use();
}
// VULNERABLE: Optional after check
void useOptional_vulnerable(std::optional<int> opt) {
if (!opt) {
return;
}
// opt has value
if (!opt.has_value()) { // ALWAYS FALSE!
handleEmpty(); // Dead code
}
process(*opt);
}
// VULNERABLE: JavaScript always-false conditions
function checkVulnerable(length) {
if (length < 0 && length > 100) { // ALWAYS FALSE!
throw new Error('Invalid length');
}
}
// VULNERABLE: Type coercion issues
function compareVulnerable(value) {
if (value === 1 && value === 2) { // ALWAYS FALSE!
// Dead code
handleBoth();
}
}
// VULNERABLE: After null check
function useObjectVulnerable(obj) {
if (obj === null) {
return;
}
// obj is not null
if (obj === null) { // ALWAYS FALSE!
console.log('Null!');
}
obj.doSomething();
}
// VULNERABLE: Array length check
function processArrayVulnerable(arr) {
if (arr.length < 0) { // ALWAYS FALSE! length >= 0
return;
}
// Unreachable return
}
Fixed Code
// SAFE: Use signed type for values that can be negative
void check_size_safe(ssize_t size) { // ssize_t is signed
if (size < 0) { // Now this can be true
handle_error("Invalid size");
return;
}
process((size_t)size); // Convert after validation
}
// SAFE: Check for zero or overflow-indicative values
void process_buffer_safe(unsigned int length) {
if (length == 0 || length > MAX_REASONABLE_SIZE) {
return;
}
char* buffer = malloc(length);
if (buffer) {
process(buffer, length);
free(buffer);
}
}
// SAFE: Fix contradictory condition
void validate_safe(int value) {
if (value > 50 && value < 100) { // Valid range
handle_special_case();
}
}
// SAFE: Remove redundant check
void check_range_safe(int value) {
if (value < 0 || value > 100) {
return;
}
// No redundant check needed
process(value);
}
// SAFE: Proper enum handling
void handle_priority_safe(Priority p) {
switch (p) {
case NONE:
handle_none();
break;
case LOW:
handle_low();
break;
case HIGH:
handle_high();
break;
}
}
// SAFE: No redundant NULL check
void use_pointer_safe(int* ptr) {
if (ptr == NULL) {
log_error("NULL pointer");
return;
}
// No need to check again
*ptr = 42;
}
// SAFE: Correct boolean logic
void check_flags_safe(int flag1, int flag2) {
if (!flag1) {
return;
}
// flag1 is true
if (flag2) { // Check flag2 alone
handle_case();
}
}
// SAFE: Use explicit signed char if needed
void check_char_safe(signed char c) {
if (c < 0) { // Now can be true
handle_negative_char();
}
}
// SAFE: Clear intent with assertions
void process_with_assertions(size_t size) {
// Document assumption rather than fake check
assert(size > 0 && "Size must be positive");
// Or explicit early return for zero
if (size == 0) {
return;
}
process(size);
}
// SAFE: Use appropriate signed type
void checkValue_safe(int value) { // Signed type
if (value < 0) { // Can now be true
throw std::invalid_argument("Negative value");
}
}
// SAFE: Check for empty instead of negative length
void checkString_safe(const std::string& str) {
if (str.empty()) { // Meaningful check
handleEmptyString();
}
}
// SAFE: Proper vector handling
void processVector_safe(const std::vector<int>& vec) {
if (vec.empty()) { // Check empty, not negative size
return;
}
for (int i : vec) {
process(i);
}
}
// SAFE: No redundant smart pointer check
void usePointer_safe(std::shared_ptr<Resource> ptr) {
if (!ptr) {
log("Null pointer");
return;
}
ptr->use(); // Known valid after check
}
// SAFE: Proper optional handling
void useOptional_safe(std::optional<int> opt) {
if (!opt) {
handleEmpty();
return;
}
process(*opt); // Known to have value
}
// SAFE: Use static_assert for compile-time guarantees
template<typename T>
void processContainer(const T& container) {
// size_type is always unsigned, document this
static_assert(std::is_unsigned_v<typename T::size_type>,
"Container size type must be unsigned");
if (container.empty()) {
return;
}
// Process non-empty container
}
// SAFE: Correct range check
function checkSafe(length) {
if (length < 0 || length > 100) { // Proper OR condition
throw new Error('Invalid length');
}
}
// SAFE: Meaningful checks
function processSafe(value) {
if (typeof value !== 'number' || isNaN(value)) {
throw new Error('Invalid number');
}
if (value < 0) {
throw new Error('Negative not allowed');
}
}
// SAFE: No redundant null check
function useObjectSafe(obj) {
if (obj === null || obj === undefined) {
console.log('Invalid object');
return;
}
obj.doSomething(); // Known valid
}
// SAFE: Check empty array
function processArraySafe(arr) {
if (!arr || arr.length === 0) { // Check empty, not negative
return;
}
for (let item of arr) {
process(item);
}
}
// ESLint can catch some of these with:
// - "no-constant-condition"
// - "@typescript-eslint/no-unnecessary-condition" (TypeScript)
Exploited in the Wild
Bounds Check Bypasses
Security bounds checks that always evaluated to false have been exploited to trigger buffer overflows.
Authentication Bypasses
Authentication checks that could never fail due to type issues have allowed unauthorized access.
Input Validation Failures
Input validation that never rejected bad input due to always-false conditions has allowed injection attacks.
Tools to test/exploit
-
GCC/Clang — -Wtype-limits, -Wtautological-compare.
-
Coverity — detects always-false conditions.
-
PVS-Studio — catches impossible comparisons.
-
ESLint — no-constant-condition rule.
CVE Examples
-
Multiple CVEs involving unsigned comparison bugs in security checks.
-
Buffer overflow CVEs where bounds checks never triggered.
-
Input validation bypasses from type mismatch in comparisons.
References
-
MITRE. "CWE-570: Expression is Always False." https://cwe.mitre.org/data/definitions/570.html
-
CERT C. "INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data." https://wiki.sei.cmu.edu/confluence/display/c/