Regla 0x300Ch: Verificá siempre los límites de los arreglos antes de acceder a sus elementos
Memoria, punteros y tipos (0x30XX)
0x300Ch: Verificá siempre los límites de los arreglos antes de acceder a sus elementos¶
Enunciado normativo¶
Todo acceso a un elemento de arreglo DEBE estar precedido por la verificación de que el índice está dentro de
[0, cantidad). La verificación DEBE usar el mismo tipo sin signo que la cantidad (size_t) y NO DEBE confiar en que el llamador envió un índice válido.
¿Por qué existe esta regla?¶
El problema¶
En C, arreglo[i] es azúcar para *(arreglo + i): el lenguaje no comprueba el
índice ni en compilación ni en ejecución. Un índice fuera de rango lee o escribe
memoria vecina y el resultado va desde un dato incorrecto hasta un SIGSEGV o
una vulnerabilidad explotable. La validación convierte el acceso inválido en una
decisión controlada y es responsabilidad de la función, no del llamador.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Comportamiento indefinido | Acceso fuera de límites (C11 §6.5.6 no da garantías). |
| Corrupción | Escritura en memoria vecina que altera otras variables o el heap. |
| Seguridad | Desbordamiento de buffer explotable. |
Fundamento en el estándar y en la cátedra¶
C11 §6.5.6 regula la aritmética de punteros: sólo está definida dentro del mismo
objeto arreglo, y un punto más allá del final. Cualquier índice mayor es
comportamiento indefinido. La cátedra exige la validación explícita junto con
0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t: si la cantidad es size_t, comparar contra un índice int
puede fallar por conversiones de signo.
Alcance y excepciones¶
Aplica a accesos indexados v[i] y a aritmética de punteros p + i. No
aplica a arreglos cuyo índice está garantizado por construcción, como el
recorrido canónico for (size_t i = 0; i < cantidad; i++), donde la propia
condición del lazo es la verificación. Tampoco exime de validar entradas que
provienen del usuario, de archivos o de parámetros.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Índice del llamador sin validar¶
void guardar(int valores[], size_t cantidad, int indice, int nuevo)
{
valores[indice] = nuevo;
}Por qué falla: indice puede ser cualquier cosa, incluido un valor mayor o igual
a cantidad. La función escribe fuera del arreglo. El contrato debe validar
indice < cantidad o documentar la precondición y confiar en el llamador, pero
la cátedra prefiere la validación explícita.
❌ Contraejemplo 2 — Off-by-one en la condición del lazo¶
for (int i = 0; i <= cantidad; i++)
{
suma += valores[i];
}Por qué falla: la condición i <= cantidad permite i == cantidad, un punto
más allá del último elemento válido. La iteración final lee memoria ajena. El
error es particularmente peligroso porque el lazo parece correcto a simple
vista.
✅ Ejemplo conforme 1 — Validación explícita con size_t¶
bool guardar(int valores[], size_t cantidad, size_t indice, int nuevo)
{
if (indice >= cantidad)
{
return false;
}
valores[indice] = nuevo;
return true;
}El índice es size_t y se compara contra la cantidad sin conversiones de signo.
La función devuelve false ante un índice inválido en lugar de corromper
memoria. El contrato queda claro para el llamador.
✅ Ejemplo conforme 2 — Recorrido canónico con cota simple¶
size_t contar_positivos(const int valores[], size_t cantidad)
{
size_t positivos = 0;
for (size_t i = 0; i < cantidad; i++)
{
if (valores[i] > 0)
{
positivos++;
}
}
return positivos;
}La condición i < cantidad es la verificación de límites para cada acceso. El
tipo size_t evita el desbordamiento de signo y la comparación es directa. Es
el patrón que también recomienda 0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for.
⚠️ Casos límite¶
Arreglo vacío:
cantidad == 0; cualquier índice debe rechazarse, incluido el0.Índices con signo: si el índice es
inty puede ser negativo, validáindice >= 0antes de convertirlo asize_t; si no, la conversión lo vuelve enorme.Punteros más allá del final:
p + cantidades válido para comparar, pero no para desreferenciar.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Acceso indexado sin validación previa del índice. |
gcc / clang | gcc -Wall -Wextra -std=c11 -Warray-bounds -fsanitize=bounds ... | array subscript is above array bounds. |
valgrind | valgrind ./programa | Invalid read/write of size N fuera del bloque. |
Checklist de autocontrol¶
¿Validé
indice < cantidadantes de cada acceso?¿Usé
size_tpara el índice y la cantidad?¿Evité
<=en las condiciones de los lazos?¿Comprobé el caso
cantidad == 0?¿Traje los índices externos a
size_tsólo después de validarlos?
Reglas relacionadas¶
0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t — el tipo
size_tes la base de comparaciones correctas.0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for — condición de lazo simple como cota.
0x5001h: Los arreglos estáticos deben ser creados con un tamaño fijo en tiempo de compilación — los arreglos de tamaño fijo se definen en compilación.
0x300Bh: Usá siempre sizeof en las asignaciones de memoria dinámica, prefiriendo sizeof(*ptr) — reservar el tamaño correcto no evita un índice inválido.