Incorrect Block Delimitation
Description
Incorrect Block Delimitation occurs when control flow statements (if, for, while, else) don't properly delimit their intended scope using braces or similar constructs. In C-family languages, these statements only control the immediately following statement if braces are omitted. Programmers may intend multiple statements to be controlled but only the first is actually affected. This leads to code that visually appears correct but behaves differently—often called the "dangling else" or "indentation-based bug" problem.
Risk
Incorrect block delimitation creates serious security vulnerabilities and logic errors. Security checks may appear to protect code that actually executes unconditionally. The Apple "goto fail" vulnerability is a famous example. Authentication bypasses, authorization failures, and data corruption can result. These bugs are particularly dangerous because code review often fails to catch them—the code visually looks correct due to indentation, but the compiler ignores indentation. Automated tools may also miss these issues.
Solution
Always use braces for control structures, even for single statements. Adopt and enforce coding standards that mandate braces. Use static analysis tools that detect misleading indentation. Configure IDE formatting to expose block delimitation issues. Enable compiler warnings like -Wmisleading-indentation (GCC 6+). In code review, verify that indentation matches actual control flow. Consider languages that use significant indentation (Python) or enforce braces.
Common Consequences
| Impact | Details |
|---|---|
| Security | Scope: Authentication/Authorization Bypass Security checks may not protect intended code. |
| Integrity | Scope: Logic Errors Code executes outside intended control flow. |
| Availability | Scope: Unpredictable Behavior Program flow differs from programmer intent. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: The famous "goto fail" pattern
int verify_signature_vulnerable(Signature* sig) {
int err = 0;
// Check hash
if ((err = check_hash(sig)) != 0)
goto fail;
// Check certificate
if ((err = check_certificate(sig)) != 0)
goto fail;
goto fail; // BUG! This always executes!
// Check signature - NEVER REACHED due to bug above!
if ((err = check_signature(sig)) != 0)
goto fail;
err = 0; // Success - NEVER REACHED!
fail:
return err;
}
// VULNERABLE: If without braces
int authenticate_vulnerable(User* user, const char* password) {
if (check_password(user, password) == 0)
log_attempt(user, "success");
user->authenticated = 1; // BUG! Always executes!
grant_access(user); // BUG! Always executes!
return user->authenticated;
}
// VULNERABLE: Dangling else
void process_vulnerable(int value, int flag) {
if (flag)
if (value > 0)
handle_positive();
else // BUG! This else binds to inner if, not outer if!
handle_no_flag(); // Actually handles value <= 0 when flag is true!
}
// VULNERABLE: For loop without braces
void clear_array_vulnerable(int* arr, int size) {
for (int i = 0; i < size; i++)
arr[i] = 0;
validate_cleared(arr, i); // BUG! Outside loop, 'i' out of scope!
}
// VULNERABLE: While loop issue
void process_stream_vulnerable(Stream* stream) {
while (!stream->eof())
read_data(stream);
process_data(stream); // BUG! Only executes once after loop!
}
// VULNERABLE: Misleading indentation in security check
int check_access_vulnerable(User* user, Resource* resource) {
if (user->role != ADMIN)
if (resource->owner != user->id)
return ACCESS_DENIED;
log_denial(user, resource); // BUG! Always logs denial!
// Grant access
return ACCESS_GRANTED; // BUG! Reached even for non-owners!
}
// VULNERABLE: Nested conditionals
void complex_check_vulnerable(int a, int b, int c) {
if (a > 0)
if (b > 0)
if (c > 0)
do_all_positive();
else // BUG! Binds to innermost if (c > 0)
do_a_negative(); // Actually executes when c <= 0!
}
// VULNERABLE: Multiple statements intended for loop
void initialize_vulnerable(int* values, int* flags, int count) {
for (int i = 0; i < count; i++)
values[i] = 0;
flags[i] = 0; // BUG! Outside loop, i is out of scope!
}
// VULNERABLE: C++ with same issues
class VulnerableAuth {
public:
bool login(const std::string& password) {
if (checkPassword(password))
logSuccess();
authenticated = true; // BUG! Always sets true!
return authenticated; // Always returns true after call!
}
void process(bool condition) {
if (condition)
doFirst();
doSecond(); // BUG! Always executes!
doThird(); // BUG! Always executes!
}
private:
bool authenticated = false;
};
// VULNERABLE: Destructor cleanup issue
void cleanup_vulnerable(Resource* resources, int count) {
for (int i = 0; i < count; i++)
resources[i].release();
delete &resources[i]; // BUG! Only deletes last element!
}
// VULNERABLE: Exception handling
void handle_vulnerable(Request* req) {
if (req == nullptr)
log_error("Null request");
throw std::invalid_argument("req is null"); // BUG! Always throws!
}
// JavaScript has same issues
// VULNERABLE: Missing braces
function loginVulnerable(password) {
if (checkPassword(password))
console.log('Login successful');
authenticated = true; // BUG! Always executes!
return authenticated;
}
// VULNERABLE: For loop
function processVulnerable(items) {
for (let i = 0; i < items.length; i++)
validate(items[i]);
process(items[i]); // BUG! Only processes last item!
}
// VULNERABLE: Nested if
function checkAccessVulnerable(user, resource) {
if (user.isAdmin)
if (resource.isPrivate)
return 'admin-private';
else // Binds to inner if!
return 'not-admin'; // Wrong! Returns when private is false!
return 'regular';
}
Fixed Code
// SAFE: Always use braces
int verify_signature_safe(Signature* sig) {
int err = 0;
// Check hash
if ((err = check_hash(sig)) != 0) {
goto fail;
}
// Check certificate
if ((err = check_certificate(sig)) != 0) {
goto fail;
}
// Check signature
if ((err = check_signature(sig)) != 0) {
goto fail;
}
err = 0; // Success
fail:
return err;
}
// SAFE: Braces make intent clear
int authenticate_safe(User* user, const char* password) {
if (check_password(user, password) == 0) {
log_attempt(user, "success");
user->authenticated = 1;
grant_access(user);
} else {
log_attempt(user, "failure");
user->authenticated = 0;
}
return user->authenticated;
}
// SAFE: Explicit braces for dangling else
void process_safe(int value, int flag) {
if (flag) {
if (value > 0) {
handle_positive();
}
} else {
handle_no_flag();
}
}
// Alternative with else-if chain
void process_safe_v2(int value, int flag) {
if (flag && value > 0) {
handle_positive();
} else if (flag && value <= 0) {
handle_non_positive();
} else {
handle_no_flag();
}
}
// SAFE: For loop with braces
void clear_array_safe(int* arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] = 0;
validate_index(arr, i);
}
// Separate validation of full array
validate_cleared(arr, size);
}
// SAFE: While loop with braces
void process_stream_safe(Stream* stream) {
while (!stream->eof()) {
read_data(stream);
process_data(stream);
}
}
// SAFE: Security check with proper blocks
int check_access_safe(User* user, Resource* resource) {
if (user->role != ADMIN) {
if (resource->owner != user->id) {
log_denial(user, resource);
return ACCESS_DENIED;
}
}
// Grant access only when conditions allow
return ACCESS_GRANTED;
}
// SAFE: Nested conditionals with braces
void complex_check_safe(int a, int b, int c) {
if (a > 0) {
if (b > 0) {
if (c > 0) {
do_all_positive();
}
}
} else {
do_a_negative();
}
}
// Or flatten the logic
void complex_check_safe_v2(int a, int b, int c) {
if (a > 0 && b > 0 && c > 0) {
do_all_positive();
} else if (a <= 0) {
do_a_negative();
}
}
// SAFE: Multiple statements in loop
void initialize_safe(int* values, int* flags, int count) {
for (int i = 0; i < count; i++) {
values[i] = 0;
flags[i] = 0;
}
}
// SAFE: Single-line if still gets braces
void set_flag_safe(int* flag, int condition) {
if (condition) {
*flag = 1;
}
}
// Or put on same line for truly simple cases
void set_flag_oneline(int* flag, int condition) {
if (condition) { *flag = 1; }
}
// SAFE: C++ with proper braces
class SafeAuth {
public:
bool login(const std::string& password) {
if (checkPassword(password)) {
logSuccess();
authenticated = true;
} else {
logFailure();
authenticated = false;
}
return authenticated;
}
void process(bool condition) {
if (condition) {
doFirst();
doSecond();
doThird();
}
}
private:
bool authenticated = false;
};
// SAFE: Cleanup with proper loop
void cleanup_safe(std::vector<std::unique_ptr<Resource>>& resources) {
for (auto& resource : resources) {
resource->release();
// unique_ptr handles deletion automatically
}
resources.clear();
}
// SAFE: Exception handling
void handle_safe(Request* req) {
if (req == nullptr) {
log_error("Null request");
throw std::invalid_argument("req is null");
}
// Process valid request
processRequest(req);
}
// SAFE: Modern C++ style with initialization
bool validateAndProcess(const Data& data) {
if (auto result = validate(data); result.success) {
return process(data);
}
return false;
}
// SAFE: JavaScript with proper braces
function loginSafe(password) {
if (checkPassword(password)) {
console.log('Login successful');
authenticated = true;
}
return authenticated;
}
// SAFE: For loop with braces
function processSafe(items) {
for (let i = 0; i < items.length; i++) {
validate(items[i]);
process(items[i]);
}
}
// Or modern approach
function processSafeModern(items) {
items.forEach(item => {
validate(item);
process(item);
});
}
// SAFE: Nested if with explicit braces
function checkAccessSafe(user, resource) {
if (user.isAdmin) {
if (resource.isPrivate) {
return 'admin-private';
}
return 'admin-public';
} else {
return 'not-admin';
}
}
// Or flattened logic
function checkAccessFlat(user, resource) {
if (user.isAdmin && resource.isPrivate) {
return 'admin-private';
}
if (user.isAdmin) {
return 'admin-public';
}
return 'not-admin';
}
// ESLint: curly rule enforces braces
// "curly": ["error", "all"]
Exploited in the Wild
Apple "goto fail" Bug (CVE-2014-1266)
The most famous example—a duplicate goto statement that bypassed SSL/TLS certificate validation, allowing man-in-the-middle attacks.
Authentication Bypasses
Multiple authentication systems have been bypassed due to incorrect block delimitation where security checks didn't protect the code they appeared to protect.
Access Control Failures
Authorization code has granted unintended access when conditions didn't properly scope the protected operations.
Tools to test/exploit
-
GCC -Wmisleading-indentation — warns about misleading indentation (GCC 6+).
-
Clang — similar warning for misleading indentation.
-
Coverity — detects control flow issues.
-
ESLint curly — enforces brace style.
CVE Examples
-
CVE-2014-1266 — Apple SSL "goto fail" bug.
-
CVE-2018-16712 — ImageMagick block delimitation issue.
-
Multiple authentication bypass CVEs from similar patterns.
References
-
MITRE. "CWE-483: Incorrect Block Delimitation." https://cwe.mitre.org/data/definitions/483.html
-
CERT C. "EXP19-C: Use braces for the body of an if, for, or while statement." https://wiki.sei.cmu.edu/confluence/display/c/