CWE-1095, Loop Condition Value Update within the Loop, describes a loop whose control flow condition is based on a value that is updated within the body of the loop.
MITRE marks it as a quality issue with no direct security implications and prohibits it for mapping real vulnerabilities. It earns a place in a secure coding guide because it is a classic home for logic errors.
How it happens
A standard counted loop updates its counter in the header, so you read the whole rule in one line. Here the update is hidden in the body, sometimes in only one branch.
/* Smell: the bound changes while the loop runs */
size_t n = count_items(list);
for (size_t i = 0; i < n; i++) {
if (should_drop(list[i])) {
remove_item(list, i);
n--; /* condition value changes mid loop */
}
}
This version also skips the element that slides into position i after a removal. Readers have to simulate the loop to see that.
/* Clearer: build a new result and leave the bound alone */
size_t out = 0;
for (size_t i = 0; i < n; i++) {
if (!should_drop(list[i])) {
list[out++] = list[i];
}
}
n = out;
What an attacker gains
Nothing directly. MITRE lists reduced maintainability and increased analytical complexity. The indirect risk is that loops that are hard to read are where off by one errors, skipped checks and unbounded iteration live, and some of those become real vulnerabilities in other categories.
How to find it
- Search for loops where the variable in the condition is assigned inside the body.
- Static analysis tools and linters often have a rule for modifying loop bounds.
- Test with empty input, one item, and inputs where every item and no item matches.
How to fix it
- Prefer loops whose bound is fixed before the loop starts.
- Use filter and map style operations, or build a new collection instead of editing one in place.
- If the bound must change, comment why, and test the edge cases.
- Iterate backward when removing items by index.
Quick check
What bug does the in loop removal example contain?
After removing the item at index i, the next item shifts into that position, but the loop moves on and never examines it.