Schleifenbedingungswert-Aktualisierung innerhalb der Schleife
Beschreibung
Schleifenbedingungswert-Aktualisierung innerhalb der Schleife tritt auf, wenn ein Produkt eine Schleife mit einer Kontrollfluss-Bedingung verwendet, die auf einem Wert basiert, der innerhalb des Schleifenkörpers aktualisiert wird. Dies erzeugt Code, der schwerer zu verstehen und zu analysieren ist, weil die Schleifenabbruchbedingung nicht sofort aus dem Schleifenkopf ersichtlich ist. Die dynamische Modifikation von Schleifenkontrollvariablen innerhalb des Schleifenkörpers erschwert es, zu verstehen, wann und wie die Schleife beendet wird.
Risiko
Das Modifizieren von Schleifenkontrollvariablen innerhalb von Schleifenkörpern hat indirekte Sicherheitsimplikationen. Der Code wird schwerer zu analysieren, was möglicherweise Fehler einschließlich Sicherheitsschwachstellen verbirgt. Schleifenverhalten wird unvorhersehbar, was die Verifizierung korrekter Beendigung erschwert. Endlosschleifen werden wahrscheinlicher, wenn Aktualisierungen falsch oder bedingt sind. Code-Reviewer können Sicherheitsprobleme aufgrund der Komplexität übersehen. Statische Analysetools können Schwierigkeiten haben, solche Schleifen zu analysieren. Off-by-one-Fehler sind bei komplexer Schleifensteuerung wahrscheinlicher. Das unvorhersagbare Schleifenverhalten kann für Denial of Service ausgenutzt werden.
Lösung
Definieren Sie Schleifengrenzen klar im Schleifenkopf, wo möglich. Verwenden Sie Standard-Schleifenmuster (for-each, Iterator), die keine manuelle Verwaltung von Kontrollvariablen erfordern. Vermeiden Sie das Modifizieren von Schleifenkontrollvariablen innerhalb des Schleifenkörpers. Wenn Modifikation notwendig ist, dokumentieren Sie klar warum und stellen Sie korrektes Verhalten sicher. Verwenden Sie break-Anweisungen für vorzeitigen Abbruch anstatt Kontrollvariablen zu modifizieren. Erwägen Sie Umstrukturierung als while-Schleife mit klaren Abbruchbedingungen. Verwenden Sie statische Analysetools, um problematische Schleifenmuster zu identifizieren. Wenden Sie Code-Review-Richtlinien an, die solche Muster kennzeichnen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Schleifenverhalten ist schwerer zu verstehen und sicher zu ändern. |
| Andere | Bereich: Ändere Erhöhte analytische Komplexität - Sicherheitsanalyse wird durch dynamische Schleifensteuerung kompliziert. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Endlosschleife - Falsche Aktualisierungen können Endlosschleifen verursachen. |
Beispielcode
Anfälliger Code
// Anfällig: Schleifenbedingungsvariable wird im Schleifenkörper modifiziert
public class VulnerableLoopProcessor {
public void processItems(List<String> items) {
int i = 0;
// Anfällig: 'i' wird im Schleifenkörper modifiziert
while (i < items.size()) {
String item = items.get(i);
if (item.isEmpty()) {
// Leere Elemente überspringen - aber dies ändert die Schleifensteuerung!
i += 2; // Anfällig: Schleifenvariable modifiziert
continue;
}
if (item.startsWith("SKIP_")) {
// Ein weiterer Modifikationspfad
i = findNextNonSkip(items, i); // Anfällig: komplexe Aktualisierung
continue;
}
processItem(item);
i++; // Normales Inkrement im Körper versteckt
}
// Schwer zu wissen: Wird diese Schleife beendet? Wie viele Iterationen?
}
public int countValidItems(List<Item> items) {
int count = 0;
int index = 0;
// Anfällig: Mehrfache Modifikationen der Schleifensteuerung
while (index < items.size()) {
Item item = items.get(index);
if (item.isDeleted()) {
index++;
continue;
}
if (item.isBatch()) {
// Alle Elemente im Batch verarbeiten
count += item.getBatchSize();
index += item.getBatchSize(); // Vorwärts springen
} else {
count++;
index++;
}
// Bug: Wenn getBatchSize() 0 zurückgibt, Endlosschleife!
}
return count;
}
}
# Anfällig: Schleifensteuerung an mehreren Stellen modifiziert
class VulnerableDataParser:
def parse_records(self, data):
i = 0
records = []
# Anfällig: 'i' wird im gesamten Schleifenkörper modifiziert
while i < len(data):
if data[i] == '\n':
i += 1 # Zeilenumbrüche überspringen
continue
if data[i] == '#':
# Kommentare überspringen - Zeilenende finden
while i < len(data) and data[i] != '\n':
i += 1 # Verschachtelte Modifikation!
continue
if data[i] == '"':
# Zitierter String - komplexe Vorwärtsbewegung
i += 1
start = i
while i < len(data) and data[i] != '"':
if data[i] == '\\':
i += 2 # Escape-Zeichen überspringen
else:
i += 1
records.append(data[start:i])
i += 1 # Schließendes Anführungszeichen überspringen
else:
# Normales Token
start = i
while i < len(data) and data[i] not in ' \t\n':
i += 1
records.append(data[start:i])
# Extrem schwer zu verifizieren:
# - Wird dies für alle Eingaben beendet?
# - Gibt es Off-by-one-Fehler?
# - Was wenn Escape-Sequenz am Ende der Daten?
return records
def find_patterns(self, text, patterns):
position = 0
matches = []
# Anfällig: Position wird auf komplexe Weise aktualisiert
while position < len(text):
matched = False
for pattern in patterns:
if text[position:].startswith(pattern):
matches.append((position, pattern))
position += len(pattern) # Über Treffer hinaus vorrücken
matched = True
break
if not matched:
position += 1
# Bug-Potenzial: Wenn Pattern leerer String, Endlosschleife!
return matches
// Anfällig: C-Schleife mit modifizierter Kontrollvariable
void vulnerable_process_buffer(char* buffer, size_t size) {
size_t i = 0;
// Anfällig: 'i' wird im Schleifenkörper modifiziert
while (i < size) {
if (buffer[i] == '\0') {
i++; // Null-Bytes überspringen
continue;
}
if (buffer[i] == ESCAPE_CHAR) {
// Escape-Sequenz verarbeiten
i++; // Über Escape-Zeichen hinaus
if (i >= size) break; // Grenzen prüfen
switch (buffer[i]) {
case 'n':
handle_newline();
i++; // Modifikation hier
break;
case 'x':
// Hex-Escape - 2 weitere Zeichen lesen
i++; // Modifikation hier
if (i + 2 <= size) {
handle_hex(buffer[i], buffer[i+1]);
i += 2; // Und hier
}
break;
default:
i++; // Und hier auch
}
} else {
handle_char(buffer[i]);
i++; // Normaler Fall
}
}
// Sicherheitsbedenken: Schwer zu verifizieren, ob Grenzen in allen Pfaden respektiert werden
}
Korrigierter Code
// Korrigiert: Klare Schleifenmuster mit vorhersagbarem Kontrollfluss
public class FixedLoopProcessor {
public void processItems(List<String> items) {
// Korrigiert: for-each verwenden, wenn Modifikation nicht nötig
for (String item : items) {
if (item.isEmpty()) {
continue; // Nur überspringen, keine Indizes modifizieren
}
if (item.startsWith("SKIP_")) {
continue;
}
processItem(item);
}
}
// Korrigiert: Wenn Index-Manipulation nötig, klare Struktur verwenden
public void processWithSkips(List<String> items) {
// Korrigiert: Expliziten Iterator mit klarer Steuerung verwenden
ListIterator<String> iterator = items.listIterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.isEmpty()) {
continue; // Iterator handhabt Vorrücken
}
if (item.startsWith("BATCH_")) {
int batchSize = getBatchSize(item);
processBatchItem(item);
// Korrigiert: Klare, begrenzte Überspringoperation
skipItems(iterator, batchSize - 1); // -1 weil wir schon eins verbraucht haben
continue;
}
processItem(item);
}
}
private void skipItems(ListIterator<?> iterator, int count) {
for (int i = 0; i < count && iterator.hasNext(); i++) {
iterator.next();
}
}
public int countValidItems(List<Item> items) {
// Korrigiert: Stream mit klarer Logik verwenden
return (int) items.stream()
.filter(item -> !item.isDeleted())
.mapToInt(item -> item.isBatch() ? item.getBatchSize() : 1)
.filter(size -> size > 0) // Schutz gegen Batches mit Größe Null
.sum();
}
}
# Korrigiert: Klare Schleifenmuster mit vorhersagbarer Steuerung
class FixedDataParser:
def parse_records(self, data: str) -> list:
# Korrigiert: Generator/Iterator-Pattern verwenden
return list(self._tokenize(data))
def _tokenize(self, data: str):
"""Generator, der Tokens mit klarem Zustandsmanagement liefert."""
pos = 0
length = len(data)
while pos < length:
char = data[pos]
if char in ' \t\n':
pos += 1
continue
if char == '#':
pos = self._skip_to_newline(data, pos, length)
continue
if char == '"':
token, pos = self._parse_quoted_string(data, pos, length)
yield token
else:
token, pos = self._parse_token(data, pos, length)
yield token
def _skip_to_newline(self, data: str, pos: int, length: int) -> int:
"""Position nach Zeilenumbruch zurückgeben. Klare, einzweckige Funktion."""
while pos < length and data[pos] != '\n':
pos += 1
return pos + 1 if pos < length else pos
def _parse_quoted_string(self, data: str, pos: int, length: int) -> tuple:
"""Zitierten String parsen. Gibt (token, neue_position) zurück."""
assert data[pos] == '"'
pos += 1 # Öffnendes Anführungszeichen überspringen
chars = []
while pos < length and data[pos] != '"':
if data[pos] == '\\' and pos + 1 < length:
chars.append(self._unescape(data[pos + 1]))
pos += 2
else:
chars.append(data[pos])
pos += 1
end_pos = pos + 1 if pos < length else pos # Schließendes Anführungszeichen überspringen
return ''.join(chars), end_pos
def _parse_token(self, data: str, pos: int, length: int) -> tuple:
"""Unzitierten Token parsen. Gibt (token, neue_position) zurück."""
start = pos
while pos < length and data[pos] not in ' \t\n':
pos += 1
return data[start:pos], pos
def find_patterns(self, text: str, patterns: list) -> list:
# Korrigiert: Schutz gegen leere Patterns und klare Logik
valid_patterns = [p for p in patterns if p] # Leere entfernen
if not valid_patterns:
return []
matches = []
position = 0
while position < len(text):
# Korrigiert: Hilfsmethode mit klarer Semantik verwenden
match = self._find_pattern_at(text, position, valid_patterns)
if match:
pattern, length = match
matches.append((position, pattern))
position += length
else:
position += 1
return matches
def _find_pattern_at(self, text: str, pos: int, patterns: list):
"""Pattern an Position finden. Gibt (pattern, lange) oder None zurück."""
for pattern in patterns:
if text[pos:pos + len(pattern)] == pattern:
return pattern, len(pattern)
return None
// Korrigiert: Klare Schleifenstruktur mit explizitem Zustandsautomaten
typedef enum {
STATE_NORMAL,
STATE_ESCAPE,
STATE_HEX_FIRST,
STATE_HEX_SECOND
} ParseState;
void fixed_process_buffer(char* buffer, size_t size) {
ParseState state = STATE_NORMAL;
char hex_first = 0;
// Korrigiert: Einfache for-Schleife mit klarem Inkrement
for (size_t i = 0; i < size; i++) {
char c = buffer[i];
switch (state) {
case STATE_NORMAL:
if (c == '\0') {
continue;
} else if (c == ESCAPE_CHAR) {
state = STATE_ESCAPE;
} else {
handle_char(c);
}
break;
case STATE_ESCAPE:
if (c == 'n') {
handle_newline();
state = STATE_NORMAL;
} else if (c == 'x') {
state = STATE_HEX_FIRST;
} else {
// Unbekanntes Escape - als Literal behandeln
handle_char(c);
state = STATE_NORMAL;
}
break;
case STATE_HEX_FIRST:
hex_first = c;
state = STATE_HEX_SECOND;
break;
case STATE_HEX_SECOND:
handle_hex(hex_first, c);
state = STATE_NORMAL;
break;
}
}
// Korrigiert: Unvollständige Escape-Sequenzen am Ende behandeln
if (state != STATE_NORMAL) {
handle_incomplete_sequence(state);
}
}
// Korrigiert: Alternative mit explizitem Positionsmanagement
typedef struct {
const char* data;
size_t size;
size_t pos;
} BufferReader;
char reader_peek(BufferReader* r) {
return r->pos < r->size ? r->data[r->pos] : '\0';
}
char reader_advance(BufferReader* r) {
return r->pos < r->size ? r->data[r->pos++] : '\0';
}
bool reader_has_more(BufferReader* r) {
return r->pos < r->size;
}
void fixed_process_with_reader(char* buffer, size_t size) {
BufferReader reader = { buffer, size, 0 };
// Korrigiert: Klare Operationen mit Reader-Abstraktion
while (reader_has_more(&reader)) {
char c = reader_advance(&reader);
if (c == '\0') {
continue;
}
if (c == ESCAPE_CHAR) {
process_escape_sequence(&reader);
} else {
handle_char(c);
}
}
}
void process_escape_sequence(BufferReader* r) {
if (!reader_has_more(r)) {
handle_incomplete_sequence(STATE_ESCAPE);
return;
}
char c = reader_advance(r);
if (c == 'n') {
handle_newline();
} else if (c == 'x') {
process_hex_escape(r);
} else {
handle_char(c);
}
}
CVE-Beispiele
Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitäts-/Wartbarkeitsproblem und keine direkte Sicherheitsschwachstelle darstellt.
Verwandte CWEs
- CWE-1120: Excessive Code Complexity (Eltern)
- CWE-1226: Complexity Issues (Kategoriemitglied)
- CWE-835: Loop with Unreachable Exit Condition (verwandt)
Referenzen
- MITRE Corporation. "CWE-1095: Loop Condition Value Update within the Loop." https://cwe.mitre.org/data/definitions/1095.html
- CISQ Quality Measures - Maintainability.
- Martin, Robert C. "Clean Code" - Simple Loop Structures.