Funktionsaufruf mit falschem Argumenttyp
Beschreibung
Funktionsaufruf mit falschem Argumenttyp ist ein Programmierfehler, bei dem Code eine Funktion, Prozedur oder Routine mit einem Argument aufruft, das einen anderen Datentyp hat als von der Funktion erwartet. Diese Schwachstelle tritt am häufigsten in locker typisierten Sprachen auf, in stark typisierten Sprachen, in denen variable Argumenttypen zur Kompilierzeit nicht erzwungen werden können, oder wenn implizite Typumwandlungen stattfinden. Wenn Funktionen Argumente unerwarteter Typen erhalten, können sie die Daten falsch interpretieren, fehlerhafte Operationen ausführen oder undefiniertes Verhalten zeigen, das zu Sicherheitslücken führen kann.
Risiko
Falsche Argumenttypen erzeugen je nach Kontext erhebliche Sicherheitsrisiken. Das Übergeben eines Pointers, wo ein Integer erwartet wird (oder umgekehrt), kann zu Speicherkorruption führen. Zeichenbehandlungsfunktionen, die Werte außerhalb des gültigen Bereichs erhalten, können Speicher falsch zugreifen. Typverwechslungen in sicherheitskritischen Funktionen können Authentifizierungs- oder Autorisierungsprüfungen umgehen. In Sprachen mit impliziter Typumwandlung können subtile Typunterschiede erfolgreich kompilieren, aber zur Laufzeit gefährlich falsche Ergebnisse produzieren. Format-String-Funktionen sind besonders anfällig - das Übergeben eines Integers, wo ein String-Pointer erwartet wird, kann Abstürze oder Informationslecks verursachen.
Lösung
Verwenden Sie wenn möglich stark typisierte Sprachen. Aktivieren Sie Compiler-Warnungen für Typunterschiede und behandeln Sie Warnungen als Fehler. In C verwenden Sie immer Funktionsprototypen und aktivieren Sie die Warnungen -Wformat und -Wconversion. Für Funktionen mit variablen Argumenten verwenden Sie Format-Attribute, um Compiler-Prüfungen zu ermöglichen. Vermeiden Sie implizite Umwandlungen - verwenden Sie explizite Casts nur wenn nötig und nach sorgfältiger Überprüfung. Befolgen Sie CERT-Richtlinien für Zeichenbehandlungsfunktionen. Verwenden Sie statische Analysetools, die Typunterschiede erkennen. In dynamisch typisierten Sprachen fügen Sie explizite Typüberprüfungen an Funktionseinstiegspunkten hinzu.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sonstiges | Bereich: Sonstiges Qualitätsverschlechterung - Funktionen produzieren falsches oder undefiniertes Verhalten, wenn sie mit falschen Argumenttypen aufgerufen werden. |
| Integrität | Bereich: Integrität Speicher ändern - Typverwechslung kann dazu führen, dass Funktionen an falsche Speicherstellen schreiben. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz, Beenden oder Neustart - Typunterschiede können Abstürze verursachen, besonders bei Pointer/Integer-Verwechslung. |
Beispielcode
Anfälliger Code
// Anfällig: Int übergeben wo Pointer in printf erwartet
#include <stdio.h>
void vulnerable_printf_type(int value) {
// Anfällig: %s erwartet char*, aber int wird übergeben
printf("Wert: %s\n", value); // FALSCHER TYP!
// Behandelt Integer als Speicheradresse - stürzt ab oder leckt Speicher
}
// Anfällig: Zeichenfunktion mit Wert außerhalb des Bereichs
#include <ctype.h>
int vulnerable_char_check(int input) {
// Anfällig: isalpha erwartet unsigned char Bereich (0-255) oder EOF
// Übergabe eines beliebigen int kann undefiniertes Verhalten verursachen
char c = (char)input; // Kann auf manchen Plattformen negativ sein
if (isalpha(c)) { // Undefiniert wenn c negativ und nicht EOF
return 1;
}
return 0;
}
// Anfällig: Signed/Unsigned Unterschied
void vulnerable_memset(char* buffer, int value, int size) {
// Wenn size negativ ist, interpretiert memset es als riesige positive Zahl
memset(buffer, value, size); // size_t erwartet, int übergeben
}
// Anfällig: Float wo int erwartet
void process_count(int count) {
for (int i = 0; i < count; i++) {
// Element verarbeiten
}
}
void vulnerable_float() {
float f = 10.7f;
// Anfällig: Float wird stillschweigend zu int abgeschnitten
process_count(f); // Implizite Konvertierung, erwartet evtl. 11 Iterationen
}
// Anfällig: Java Typverwechslung mit Generics
import java.util.*;
public class VulnerableTypes {
// Anfällig: Raw Type erlaubt falsche Typen
@SuppressWarnings("unchecked")
public void vulnerableGeneric() {
List list = new ArrayList(); // Raw Type
list.add("string");
list.add(123); // Typen mischen!
for (Object item : list) {
// Anfällig: ClassCastException zur Laufzeit
String s = (String) item; // Stürzt bei Integer ab
process(s);
}
}
// Anfällig: Varargs Typverwechslung
public void logMessage(String format, Object... args) {
System.out.printf(format, args);
}
public void vulnerableVarargs() {
int[] numbers = {1, 2, 3};
// Anfällig: int[] übergeben wo mehrere Objects erwartet
logMessage("Werte: %d, %d, %d", numbers); // Falsch!
// numbers wird als einzelnes Object behandelt, nicht 3 separate Argumente
}
}
# Anfällig: Python Typfehler
def process_items(items: list) -> int:
"""Verarbeitet eine Liste von Elementen und gibt Anzahl zurück."""
total = 0
for item in items:
total += len(item) # Erwartet Elemente mit len()
return total
# Anfällig: String übergeben wo Liste erwartet
result = process_items("hallo") # String ist iterierbar, aber len("h") etc.
# Funktioniert aber produziert unerwartetes Ergebnis (zählt Zeichen)
# Anfällig: Falschen Typ an Sicherheitsfunktion übergeben
def verify_hash(data: bytes, expected_hash: str) -> bool:
"""Verifiziert dass Daten-Hash mit erwartetem übereinstimmt."""
import hashlib
actual = hashlib.sha256(data).hexdigest()
return actual == expected_hash
# Anfällig: str statt bytes übergeben
user_data = request.form['data'] # str Typ
# verify_hash(user_data, expected) # TypeError oder Encoding-Probleme
// Anfällig: Variadische Funktion Typverwechslung
#include <stdarg.h>
#include <stdio.h>
void vulnerable_variadic(int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
// Nimmt an alle Argumente sind char*
char* str = va_arg(args, char*); // Typ muss übereinstimmen!
printf("%s\n", str);
}
va_end(args);
}
void call_vulnerable() {
// Anfällig: int übergeben wo char* erwartet
vulnerable_variadic(3, "hallo", 12345, "welt");
// 12345 als Pointer interpretiert - Absturz oder Müll
}
Lösung
// Korrigiert: Korrekte Typen für printf
#include <stdio.h>
void secure_printf_type(int value) {
// Korrigiert: %d für int, nicht %s
printf("Wert: %d\n", value);
}
void secure_printf_string(const char* str) {
// Korrigiert: %s mit tatsächlichem String-Pointer
printf("Wert: %s\n", str);
}
// Korrigiert: Sichere Zeichenbehandlung
#include <ctype.h>
int secure_char_check(int input) {
// Korrigiert: Cast zu unsigned char für Zeichenfunktionen
unsigned char c = (unsigned char)input;
// Oder zuerst Bereich validieren
if (input < 0 || input > 255) {
return 0; // Ungültiger Zeichenwert
}
if (isalpha(c)) {
return 1;
}
return 0;
}
// Korrigiert: Richtiger Typ für Größe
#include <string.h>
#include <stddef.h>
void secure_memset(char* buffer, int value, size_t size) {
// Korrigiert: size_t für Größen verwenden
memset(buffer, value, size);
}
int secure_memset_checked(char* buffer, int value, int size) {
// Korrigiert: Vor impliziter Konvertierung validieren
if (size < 0) {
return -1; // Fehler: ungültige Größe
}
memset(buffer, value, (size_t)size);
return 0;
}
// Korrigiert: Explizite Typkonvertierung mit Bewusstsein
void secure_float_handling() {
float f = 10.7f;
// Korrigiert: Explizite Konvertierung mit Rundungsentscheidung
int count_floor = (int)f; // 10 - abschneiden
int count_round = (int)(f + 0.5f); // 11 - runden
// Passenden wählen basierend auf Anforderungen
process_count(count_round);
}
// Korrigiert: Java mit korrekten Generics
import java.util.*;
public class SecureTypes {
// Korrigiert: Parametrisierte Typen verwenden
public void secureGeneric() {
List<String> list = new ArrayList<>();
list.add("string");
// list.add(123); // Kompilierfehler!
for (String s : list) {
// Typsichere Iteration
process(s);
}
}
// Korrigiert: Korrekte Varargs-Behandlung
public void logMessage(String format, Object... args) {
System.out.printf(format, args);
}
public void secureVarargs() {
int[] numbers = {1, 2, 3};
// Korrigiert: Zu Wrapper-Objekten konvertieren
logMessage("Werte: %d, %d, %d", numbers[0], numbers[1], numbers[2]);
// Oder Integer-Array verwenden
Integer[] boxedNumbers = {1, 2, 3};
logMessage("Werte: %d, %d, %d", (Object[]) boxedNumbers);
}
// Korrigiert: Typsicheres Logging
public void secureLog(String message, int... values) {
StringBuilder sb = new StringBuilder(message);
for (int v : values) {
sb.append(" ").append(v);
}
System.out.println(sb);
}
}
# Korrigiert: Python mit Typvalidierung
from typing import List, Union
def process_items_secure(items: List[str]) -> int:
"""Verarbeitet eine Liste von String-Elementen und gibt Gesamtlänge zurück."""
if not isinstance(items, list):
raise TypeError(f"Liste erwartet, {type(items).__name__} erhalten")
total = 0
for item in items:
if not isinstance(item, str):
raise TypeError(f"str-Elemente erwartet, {type(item).__name__} erhalten")
total += len(item)
return total
# Korrigiert: Typsichere Hash-Verifizierung
def verify_hash_secure(data: Union[bytes, str], expected_hash: str) -> bool:
"""Verifiziert dass Daten-Hash mit erwartetem übereinstimmt."""
import hashlib
# Korrigiert: Sowohl bytes als auch str behandeln
if isinstance(data, str):
data = data.encode('utf-8')
elif not isinstance(data, bytes):
raise TypeError(f"bytes oder str erwartet, {type(data).__name__} erhalten")
actual = hashlib.sha256(data).hexdigest()
return actual == expected_hash
# Laufzeit-Typüberprüfung verwenden
from typing import get_type_hints
import functools
def type_checked(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
hints = get_type_hints(func)
# Argumenttypen gegen Hints validieren
# (vereinfacht - Produktion wäre gründlicher)
return func(*args, **kwargs)
return wrapper
// Korrigiert: Typsichere Alternativen zu Variadic
#include <stdio.h>
// Korrigiert: Typisiertes Array statt Variadic verwenden
void secure_print_strings(const char** strings, int count) {
for (int i = 0; i < count; i++) {
if (strings[i] != NULL) {
printf("%s\n", strings[i]);
}
}
}
void call_secure() {
const char* strings[] = {"hallo", "welt", "test"};
secure_print_strings(strings, 3);
}
// Korrigiert: Typsichere Union für gemischte Typen
typedef enum { TYPE_INT, TYPE_STRING, TYPE_FLOAT } ArgType;
typedef struct {
ArgType type;
union {
int i;
const char* s;
float f;
} value;
} TypedArg;
void secure_mixed_print(TypedArg* args, int count) {
for (int i = 0; i < count; i++) {
switch (args[i].type) {
case TYPE_INT:
printf("%d\n", args[i].value.i);
break;
case TYPE_STRING:
printf("%s\n", args[i].value.s);
break;
case TYPE_FLOAT:
printf("%f\n", args[i].value.f);
break;
}
}
}
CVE-Beispiele
- CVE-2006-1174: Argumenttyp-Unterschied im Funktionsaufruf führte zur Sicherheitsumgehung.
- CVE-2007-1420: Typverwechslung verursachte Absturz in String-Behandlungsfunktion.
Referenzen
- MITRE Corporation. "CWE-686: Function Call With Incorrect Argument Type." https://cwe.mitre.org/data/definitions/686.html
- CERT C Coding Standard. "EXP37-C. Call functions with the correct number and type of arguments."
- CERT C Coding Standard. "STR37-C. Arguments to character handling functions must be representable as an unsigned char."