Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x5015h: No invoques el interprete de comandos con system() o popen()

Compilacion, preprocesador y seguridad (0x50XX)

Universidad Nacional de Río Negro

0x5015h: No invoques el interprete de comandos con system() o popen()

Enunciado normativo

NO DEBE invocarse system() ni popen(); usá las APIs especificas de la biblioteca estandar.

Síntoma en el código del estudiante

El programa delega trabajo al intérprete de comandos del sistema en lugar de resolverlo con la biblioteca de C:

system("pause");
system("clear");
popen("ls -l", "r");

El caso grave es cuando el argumento se arma con una entrada externa.

Diagnóstico

Mecanismo del defecto

system no ejecuta el comando directamente: le pasa la cadena al procesador de comandos del sistema operativo mediante sh -c (C11 §7.22.4.8). Ese paso por el shell es el problema. El shell vuelve a interpretar la cadena y trata a sus metacaracteres (;, |, &, `, $(), salto de línea) como separadores y operadores. Si el argumento contiene datos que el usuario controla, un atacante puede cerrar el comando previsto y encadenar el suyo: pasar archivo; rm -rf datos convierte una simple lectura en una ejecución arbitraria. Es la vulnerabilidad de inyección de comandos.

popen abre una tubería hacia el shell y arrastra el mismo mecanismo; si no se consume el flujo, deja el proceso hijo y el descriptor abiertos.

Consecuencia observable

Fundamento en el estándar C11

system está declarada en <stdlib.h> (C11 §7.22.4.8), pero con un argumento distinto de NULL el comportamiento depende de que exista un procesador de comandos, algo ajeno al lenguaje y no portable. La biblioteca estándar cubre casi todo lo que el estudiante suele pedirle al shell: pausar (getchar), limpiar pantalla (secuencias de control o system nunca es necesario), listar y leer archivos (fopen, fgets, readdir), copiar o mover datos (stdio.h). Cuando de verdad haga falta lanzar un programa, la alternativa segura evita el shell y pasa argumentos ya separados con la familia exec*, de modo que ningún metacaracter sea reinterpretado.

Corrección idiomática

❌ Código con el antipatrón

#include <stdlib.h>

void pausar_consola(void)
{
    system("pause");
}

✅ Código refactorizado

#include <stdio.h>

void pausar_consola(void)
{
    getchar();
}

✅ Código refactorizado — sin concatenar entrada en un comando

#include <stdio.h>

void mostrar_archivo(const char *ruta)
{
    FILE *archivo = fopen(ruta, "r");

    if (archivo == NULL) {
        perror("fopen");
        return;
    }

    int caracter;
    while ((caracter = fgetc(archivo)) != EOF) {
        putchar(caracter);
    }

    fclose(archivo);
}

La ruta se usa como dato, nunca se reensambla en una cadena que el shell vaya a interpretar: así los metacaracteres no tienen poder alguno.

Errores típicos al compilar o ejecutar

sh: 1: pause: not found

Y en un caso de inyección, el shell ejecuta lo que no debía:

$ ./programa "datos; rm -rf ./salida"
rm: cannot remove './salida': No such file or directory

Checklist de verificación

Reglas relacionadas

Síntoma en el código del estudiante

El ejercicio usa llamadas a sistema de control de procesos, señales o red que la consigna no autoriza:

fork();
execve("/bin/ls", argv, NULL);
kill(pid, SIGKILL);
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
socket(AF_INET, SOCK_STREAM, 0);

También entra en esta familia pipe, mmap, clone y shmget cuando aparecen como atajo para resolver un problema de memoria o de concurrencia.

Diagnóstico

Mecanismo del defecto

Estas funciones no pertenecen a ISO C11: son parte de POSIX y viven en <unistd.h>, <signal.h>, <sys/ptrace.h>, <sys/socket.h>, etc. Con -std=c11 estricto, el estándar oculta esas declaraciones salvo que se pida explícitamente la interfaz POSIX con una macro de prueba de características (_POSIX_C_SOURCE/_XOPEN_SOURCE). El compilador entonces las trata como funciones desconocidas y, según la versión, emite una declaración implícita o un error. Aun cuando el enlace contra la libc encuentre los símbolos, el código deja de ser portable y queda fuera del modelo de la materia.

En un entorno de corrección automática, fork, exec, socket o ptrace permiten crear procesos, abrir red o inspeccionar memoria: por eso los sandboxes pedagógicos las bloquean y abortan la ejecución.

Consecuencia observable

Fundamento en el estándar C11

ISO C11 no define estas llamadas; solo incluye la biblioteca estándar (<stdio.h>, <stdlib.h>, <string.h>, <math.h>, etc.). Para usarlas habría que declarar la macro de característica POSIX antes de cualquier #include y aceptar que deja de ser C estándar. La consigna de la cátedra resuelve cada ejercicio con las primitivas de E/S y los algoritmos vistos en clase, de modo que no hacen falta. Si el TP las autoriza, se declaran formalmente en la cabecera correspondiente y se documenta la dependencia.

Corrección idiomática

❌ Código con el antipatrón

#include <unistd.h>

void crear_tarea(void)
{
    fork();
}

✅ Código refactorizado

#include <stdio.h>

void ejecutar_algoritmo(const int datos[], size_t cantidad)
{
    size_t suma = 0;

    for (size_t i = 0; i < cantidad; i++) {
        suma += (size_t)datos[i];
    }

    printf("Resultado: %zu\n", suma);
}

El problema se resuelve con arreglos, lazos y funciones del alcance de la materia, sin delegar en el sistema operativo. Si de verdad se necesita la interfaz POSIX, se documenta la macro de características al tope del fuente:

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>

Errores típicos al compilar o ejecutar

aviso.c:6:5: warning: implicit declaration of function 'fork' [-Wimplicit-function-declaration]

Con -Werror o una versión estricta del compilador:

aviso.c:6:5: error: implicit declaration of function 'fork'; did you mean 'fread'? [-Wimplicit-function-declaration]

En el evaluador automático:

[nostromo] operación no permitida: se bloqueó fork()

Checklist de verificación

Reglas relacionadas