/* Educational lifetime model, NOT PHP code or a PHP exploit.
 * clang -g -O0 -fsanitize=address -fno-omit-frame-pointer \
 *   request-lifetime.c -o request-lifetime
 * ./request-lifetime          # AddressSanitizer: heap-use-after-free
 * ./request-lifetime --fixed  # exits successfully
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Worker {
    char *last_error;
};

int main(int argc, char **argv) {
    struct Worker worker = {0};
    int fixed = argc > 1 && strcmp(argv[1], "--fixed") == 0;

    /* Request A publishes a pointer into request-owned memory. */
    char *request_memory = malloc(32);
    if (!request_memory) return 1;
    strcpy(request_memory, "error from request A");
    worker.last_error = request_memory;

    /* Request A ends. The same worker will serve request B. */
    if (fixed) worker.last_error = NULL;
    free(request_memory);

    /* Request B consumes the worker state before replacing it. */
    if (worker.last_error) {
        puts(worker.last_error); /* ASan reports the invalid read here. */
    } else {
        puts("No stale state: request B has no error to consume.");
    }
    return 0;
}
