// smash.c: un buffer overflow en la pila, de forma segura y local.
// vulnerable() copia con strcpy una entrada de tamaño arbitrario en un buffer
// de 16 bytes. Si la entrada es más larga, pisa lo que hay sobre buf en la
// pila, incluida una dirección de retorno guardada.
//
// Un manejador de señal informa a qué dirección saltó la CPU al retornar, para
// VER la dirección de retorno corrompida sin montar ningún ataque real:
//   - Sin canario, una entrada larga llena esa dirección con 0x41 ('A') y, al
//     retornar, la CPU salta a 0x4141… y falla.
//   - Con -fstack-protector, el canario lo detecta: "stack smashing detected".
//
// Detalle de arquitectura: en ARM64 (el contenedor del curso) el compilador
// guarda la dirección de retorno al fondo del marco de cada función, así que el
// overflow de buf pisa la dirección de retorno guardada en el marco de
// QUIEN llamó; el salto ocurre cuando esa función retorna. En x86-64 se pisa la
// de la propia función. El mecanismo y la lección son los mismos.
//
//   uso: ./smash ARCHIVO     (ARCHIVO tiene los bytes a copiar)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ucontext.h>

static void onfault(int sig, siginfo_t *info, void *uc) {
    (void)info;
    ucontext_t *c = uc;
#if defined(__aarch64__)
    unsigned long pc = c->uc_mcontext.pc;
#elif defined(__x86_64__)
    unsigned long pc = c->uc_mcontext.gregs[REG_RIP];
#elif defined(__riscv)
    unsigned long pc = c->uc_mcontext.__gregs[0];
#else
    unsigned long pc = 0;
#endif
    fprintf(stderr, "[%s] la CPU intento ejecutar en 0x%lx\n",
            sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", pc);
    _exit(139);
}

static void vulnerable(const char *src) {
    char buf[16];
    strcpy(buf, src);                 // sin medir: si src > 15 bytes, pisa mas alla de buf
    printf("copie %zu bytes en un buffer de 16\n", strlen(buf));
}

int main(int argc, char **argv) {
    setvbuf(stdout, NULL, _IONBF, 0); // sin buffer: lo impreso se ve aunque luego falle
    struct sigaction sa = {0};
    sa.sa_sigaction = onfault;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGSEGV, &sa, NULL);
    sigaction(SIGBUS, &sa, NULL);

    if (argc != 2) {
        fprintf(stderr, "uso: %s ARCHIVO\n", argv[0]);
        return 1;
    }
    FILE *f = fopen(argv[1], "rb");
    if (!f) {
        perror("fopen");
        return 1;
    }
    char data[4096];
    size_t n = fread(data, 1, sizeof data - 1, f);
    data[n] = 0;
    fclose(f);

    vulnerable(data);
    // En x86-64 se corrompe la dirección de retorno de vulnerable, así que la
    // CPU ni llega aquí. En ARM64 se corrompe la de main: la ejecución SÍ llega
    // a este return, y el salto a 0x4141… ocurre al retornar de main.
    return 0;
}
