Integer overflow y bugs de enteros
Los enteros en C tienen un tamaño fijo (32 o 64 bits) y un rango limitado. Cuando una operación produce un valor fuera de ese rango, “da la vuelta” (overflow/underflow), y si un número con signo se trata como sin signo (o al revés), su valor cambia drásticamente (signedness). Estos bugs son peligrosos porque esos enteros suelen usarse como tamaños de buffer o índices: un cálculo erróneo lleva a reservar poca memoria, leer/escribir fuera de límites, o saltarse una comprobación — y de ahí a un overflow clásico.
Tipos de bug
Sección titulada «Tipos de bug»Integer overflow x + y supera el máximo -> da la vuelta a un valor pequeño/negativo # en 32 bits: 0xFFFFFFFF + 1 = 0 (vuelve a 0)Integer underflow 0 - 1 en unsigned -> 0xFFFFFFFF (enorme)Signedness un int negativo tratado como unsigned -> número gigante # -1 (int) interpretado como unsigned = 4294967295Truncation asignar un int (32) a un short (16) -> se pierden bits altosMultiplication len * sizeof(x) desborda -> reserva menos de lo esperadoPor qué son explotables
Sección titulada «Por qué son explotables»El peligro no es el entero en sí, sino para qué se usa:
# 1) tamaño de reserva controlado -> heap/stack overflowint n = user_input; // atacante mete un valor que desbordachar *buf = malloc(n * 8); // n*8 desborda -> malloc reserva poco// luego se escriben n elementos -> overflow del heap
# 2) saltarse una comprobación de límitesif (index < MAX) { array[index] = x; } // si index es negativo (signed) pasa el check// pero array[index] escribe FUERA del array (índice negativo)
# 3) underflow en un tamaño -> copia gigantescasize_t len = header_len - 20; // si header_len < 20, len = enorme -> overflow en memcpyEl patrón clave: un entero controlado por el atacante que alimenta un malloc, un memcpy, un índice de array o una comprobación if (x < limite).
Cómo encontrarlos
Sección titulada «Cómo encontrarlos»# en reversing/código: buscar operaciones aritméticas sobre tamaños/índices# antes de malloc, memcpy, read, bucles# comprobaciones de límites que usan signed donde debería ser unsigned# fuzzing con valores extremos: 0, -1, 0xFFFFFFFF, INT_MAX, INT_MIN, tamaños grandesDe integer bug a explotación
Sección titulada «De integer bug a explotación»1. Encontrar el entero controlado que alimenta un tamaño/índice/check2. Elegir un valor que provoque el efecto deseado: - overflow -> reserva pequeña -> heap/stack overflow (ver pwn-stack/pwn-heap) - signedness/negativo -> saltarse el límite -> escritura fuera de rango - underflow -> tamaño gigante -> copia masiva3. Convertir la corrupción de memoria resultante en control del flujo (como un overflow clásico)Es frecuente que el integer bug sea el primer eslabón que habilita un overflow clásico de stack o heap.
Para la defensa
Sección titulada «Para la defensa»- Comprobaciones de rango correctas: validar antes de operar; usar tipos sin signo para tamaños (
size_t) y verificar límites superiores e inferiores. - Detección de overflow: funciones seguras (
__builtin_add_overflow,ckd_addde C23),-ftrapv, UBSan (UndefinedBehaviorSanitizer) en desarrollo. - No calcular tamaños con aritmética sin verificar desbordamiento (
if (n > MAX/8) error;antes den*8). - Fuzzing con valores límite; revisión de código centrada en aritmética de tamaños.
Checklist de prueba
Sección titulada «Checklist de prueba»- Identificar enteros controlados que alimentan tamaños/índices/checks
- Probar valores límite (0, -1, 0xFFFFFFFF, INT_MAX/MIN)
- Overflow en cálculo de tamaño → reserva pequeña → overflow
- Signedness/negativo → saltarse comprobación de límite
- Underflow → tamaño gigante → copia masiva
- Encadenar con stack/heap overflow (Stack buffer overflow/Explotación de heap)
- Convertir en control del flujo
- Blue: ¿validación de rango, size_t, UBSan?