Regla 0x200Bh: Prohibición de retornar la dirección de una variable local de stack
Funciones, contratos y modularizacion (0x20XX)
0x200Bh: Prohibición de retornar la dirección de una variable local de stack¶
Enunciado normativo¶
NO DEBE devolverse la dirección de una variable automática (
&local) ni de un arreglo local. Si la función debe entregar memoria, DEBE reservarla en el heap conmalloc/callocy documentar quién la libera.
¿Por qué existe esta regla?¶
El problema¶
Las variables locales viven en el marco de pila y mueren cuando la función retorna. Devolver su dirección produce un puntero colgante: el número sigue existiendo, pero el objeto apuntado ya no está vivo, y desreferenciarlo es comportamiento indefinido.
Lo engañoso es que a menudo “funciona”: el valor viejo sigue ahí hasta que otra llamada pisa el marco. El fallo aparece tarde, como bug intermitente.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Comportamiento indefinido | Desreferenciar el puntero es UB según §6.2.4 y §6.5.3.2. |
| Bug intermitente | El valor “correcto” sobrevive hasta que otra función pisa el marco. |
| Seguridad | Un atacante puede controlar el contenido de la pila reutilizada. |
Fundamento en el estándar y en la cátedra¶
C11 establece que la duración de un objeto automático termina al salir del bloque (§6.2.4). Acceder a él por puntero después es comportamiento indefinido, y la norma incluye el uso de una dirección cuya vida terminó (§6.5.3.2). La cátedra lo prohíbe: si el dato debe sobrevivir, se reserva en el heap (0x3001h: Siempre verificá la asignación exitosa de memoria dinámica) o se copia al llamador.
Alcance y excepciones¶
Cubre cualquier retorno por puntero hacia un objeto local, incluidos:
return &variable;return arreglo_local;(el arreglo decae a puntero al primer elemento)guardar esa dirección en un puntero de salida (
*out = &local;)
Excepción legítima: devolver un puntero a un objeto con duración estática
(static o global) es válido respecto de la vida útil, pero prohibido por
0x2004h: No se permite el uso de variables globales por el estado compartido. Devolver memoria de malloc es la
solución correcta.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Retorno de dirección local¶
int *crear_valor(void)
{
int x = 10;
return &x;
}Por qué falla: x vive en el marco de crear_valor; al retornar, el marco se
destruye y el llamador recibe una dirección que la próxima llamada reutilizará.
❌ Contraejemplo 2 — Retorno de arreglo local¶
char *nombre_por_defecto(void)
{
char buffer[32] = "sin nombre";
return buffer;
}Por qué falla: buffer decae a char * y se devuelve la dirección de la pila
local. Aunque la cadena sea válida un instante, ya no lo es al volver. Es un
error clásico que el compilador suele advertir con
-Wreturn-local-addr.
✅ Ejemplo conforme 1 — Reserva en el heap con propiedad documentada¶
/**
* @brief Crea un valor inicializado en 10.
* @return Puntero a un int en el heap, o NULL si falla malloc.
* @post El llamador es dueño del bloque y debe liberarlo con free().
*/
int *crear_valor(void)
{
int *p = malloc(sizeof(*p));
if (p == NULL) {
return NULL;
}
*p = 10;
return p;
}La memoria sobrevive a la función y el contrato dice quién la libera. La
reserva usa sizeof(*p), conforme a 0x300Bh: Usá siempre sizeof en las asignaciones de memoria dinámica, prefiriendo sizeof(*ptr).
✅ Ejemplo conforme 2 — Copia al búfer del llamador¶
int copiar_por_defecto(char *destino, size_t cap)
{
if (destino == NULL || cap == 0) {
return ERROR_PUNTERO_NULO;
}
const char *origen = "sin nombre";
if (strlen(origen) + 1 > cap) {
return ERROR_ESPACIO;
}
strcpy(destino, origen);
return OK;
}El llamador aporta el almacenamiento; la función solo escribe en él. No hay memoria que sobreviva a un marco destruido.
⚠️ Casos límite¶
staticlocal: compila y no es UB, pero viola 0x2004h: No se permite el uso de variables globales; no lo uses como parche.Estructuras con punteros internos: aunque el
structse copie por valor, si guarda punteros a locales, siguen colgando.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gcc / clang | gcc -Wall -Wextra -Wreturn-local-addr -std=c11 ... | “function returns address of local variable”. |
gaff | gaff check archivo.c | return &local; o retorno de arreglo local. |
Checklist de autocontrol¶
¿Algún
returndevuelve&de una variable local?¿La memoria que devuelvo viene del heap y está documentada?
¿El llamador sabe que debe llamar a
free?
Reglas relacionadas¶
0x3001h: Siempre verificá la asignación exitosa de memoria dinámica — verificar que
mallocno retorneNULL.0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes — liberar y anular el puntero para evitar colgantes.
0x300Bh: Usá siempre sizeof en las asignaciones de memoria dinámica, prefiriendo sizeof(*ptr) — reservar con
sizeof(*ptr).