Regla 0x4005h: Prescindí del uso de offsets y posiciones fijas codificadas a mano en archivos binarios sin validar sus dimensiones
Archivos y E/S (0x40XX)
0x4005h: Prescindí del uso de offsets y posiciones fijas codificadas a mano en archivos binarios sin validar sus dimensiones¶
Enunciado normativo¶
NO DEBE accederse a posiciones arbitrarias de un archivo binario con offsets literales sin comprobar antes que la posición pertenece al rango válido del archivo. El tamaño DEBE calcularse con
fseek/ftello conocerse por metadatos, nunca suponerse.
Un offset es un índice: antes de saltar hay que verificar que cae dentro del archivo y que la lectura posterior puede completarse.
¿Por qué existe esta regla?¶
El problema¶
fseek permite posicionar el cursor más allá del fin de archivo: el
estándar lo autoriza y la operación puede retornar éxito. El error no aparece en
el fseek sino en la lectura siguiente, que devuelve cero elementos. Si el
programa no verifica el offset ni el retorno de fread, procesa un búfer sin
inicializar como si contuviera datos válidos.
Hay un segundo problema, típico de la E/S binaria: el tamaño serializado de un
registro no tiene por qué coincidir con sizeof de la struct en memoria. El
compilador inserta relleno (padding) para alinear los campos, y ese relleno no
se escribe necesariamente en el archivo. Además, el orden de bytes
(endianness) puede diferir entre plataformas. Un offset calculado como
indice * sizeof(struct registro) es, por lo tanto, una suposición frágil.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Una lectura fuera de rango devuelve cero elementos y el búfer queda con basura. |
| Datos incorrectos | Un registro se interpreta corrido porque el padding cambió el tamaño real. |
| No portabilidad | El formato binario asumido deja de funcionar al cambiar de compilador o arquitectura. |
| Ruptura por cambio de formato | Agregar un campo a la struct invalida todos los offsets literales. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §7.21.9.2 describe fseek, que retorna cero si tiene éxito y
un valor distinto de cero si falla; no garantiza que la posición exista. §7.21.9.4
define ftell, que combinado con fseek(..., SEEK_END) permite obtener el
tamaño. La cátedra adopta la regla para prevenir accesos fuera de rango
(0x300Ch: Verificá siempre los límites de los arreglos antes de acceder a sus elementos) y para desaconsejar formatos binarios que dependan del layout
en memoria.
Alcance y excepciones¶
Aplica a todo acceso posicional con fseek, fsetpos, ftell o fgetpos
sobre archivos binarios, en lectura o escritura. No prohíbe los offsets: exige
que se deriven de constantes con nombre, dimensiones calculadas o metadatos del
formato, y que se validen antes de usar.
Excepción razonable: un archivo cuyo formato fija posiciones constantes documentadas (por ejemplo, una cabecera en el byte 0) puede usar constantes simbólicas con nombre (ver 0x010Fh: Prohibición de constantes numéricas mágicas en índices de arreglos), siempre que valide el tamaño.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Offset literal sin comprobar el tamaño¶
fseek(f, 1000, SEEK_SET);
fread(buf, 1, 10, f);Por qué falla: si el archivo tiene menos de 1000 bytes, fseek posiciona más
allá del fin (o falla, y su retorno se ignora), y fread devuelve cero. El
búfer buf queda con datos indeterminados que el programa tratará como válidos.
❌ Contraejemplo 2 — Asumir que el layout en memoria es el layout en disco¶
struct registro r;
fseek(f, indice * sizeof(struct registro), SEEK_SET);
fread(&r, sizeof(r), 1, f);Por qué falla: sizeof(struct registro) incluye padding que puede no estar
serializado, y los campos pueden tener otro orden de bytes. El offset calculado
apunta a un lugar incorrecto aunque el índice sea válido.
✅ Ejemplo conforme 1 — Calcular y validar dimensiones antes de saltar¶
long tamano_archivo(FILE *f)
{
if (fseek(f, 0, SEEK_END) != 0) {
return -1;
}
long fin = ftell(f);
fseek(f, 0, SEEK_SET);
return fin;
}El tamaño se obtiene de la fuente autoritativa y queda disponible para validar
cualquier offset antes de usarlo; el retorno de cada fseek se controla.
✅ Ejemplo conforme 2 — Offset validado y lectura verificada¶
long tam = tamano_archivo(f);
long pos = (long)indice * (long)TAM_REGISTRO;
if (tam < 0 || pos < 0 || pos + TAM_REGISTRO > tam) {
return -1;
}
if (fseek(f, pos, SEEK_SET) != 0) {
return -1;
}
size_t leidos = fread(&r, 1, TAM_REGISTRO, f);
if (leidos != TAM_REGISTRO) {
return -1;
}La constante TAM_REGISTRO documenta el tamaño serializado (no el de la
struct), la cota se comprueba con < y > antes del salto, y tanto fseek
como fread se verifican (0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos).
⚠️ Casos límite¶
Archivo vacío:
ftelldevuelve 0; cualquier offset positivo debe rechazarse.ftellretornalong: en archivos grandes puede acercarse a su límite; en POSIX se prefierefseeko/ftello, fuera del alcance de C11 puro.fseekretorna distinto de cero ante fallo: no asumir que siempre tuvo éxito solo porque el offset era válido.SEEK_CURySEEK_END: la validación del offset resultante exige conocer la posición actual o el tamaño, no solo el desplazamiento.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x4005h: fseek con literal sin validación previa. |
gcc | gcc -Wall -Wextra -std=c11 archivo.c | Sin señal directa; requiere revisión. |
| Revisión manual | — | Todo fseek debe estar precedido por un cálculo de tamaño y una cota. |
Checklist de autocontrol¶
¿Calculé el tamaño del archivo con
fseek/ftellantes de saltar?¿Comprobé que
offset + tamaño_del_dato <= tamaño_archivo?¿Usé el tamaño serializado del registro y no
sizeof(struct)?¿Verifiqué el retorno de
fseeky el conteo defread?
Reglas relacionadas¶
0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos — los retornos de
fread/fseekdeben verificarse.0x010Fh: Prohibición de constantes numéricas mágicas en índices de arreglos — prohibición de índices numéricos mágicos; usá constantes.
0x300Ch: Verificá siempre los límites de los arreglos antes de acceder a sus elementos — verificar límites de arreglos antes de acceder.
0x4004h: Mantené la simetría de recursos al abrir y cerrar archivos en el mismo nivel de abstracción — la validación de dimensiones no reemplaza el cierre simétrico.