_kvm_uread maps a userspace page to a slot in the amap, but
only checks whether the slot is indeed within the amap.
It must also use the slot to extract the correct vm_anon in order
to find the physical address to read from.

The attached program shows that kvm_uread otherwise attempts reads
from an incorrect userspace address.

ok?

Index: lib/libkvm/kvm_proc.c
===================================================================
RCS file: /cvs/src/lib/libkvm/kvm_proc.c,v
retrieving revision 1.52
diff -u -p -r1.52 kvm_proc.c
--- lib/libkvm/kvm_proc.c       22 Oct 2014 04:13:35 -0000      1.52
+++ lib/libkvm/kvm_proc.c       16 Apr 2016 18:23:32 -0000
@@ -163,7 +163,7 @@ _kvm_ureadm(kvm_t *kd, const struct kinf
        if (slot > amap.am_nslot)
                return (NULL);
 
-       addr = (u_long)amap.am_anon + (offset / kd->nbpg) * sizeof(anonp);
+       addr = (u_long)(amap.am_anon + slot);
        if (KREAD(kd, addr, &anonp))
                return (NULL);
 
/*
 * Compile with: gcc -O2 libkvmtest.c -o libkvmtest -lkvm
 * Run as root.
 */

#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/mman.h>

#include <err.h>
#include <fcntl.h>
#include <kvm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define ARGSIZE (2 * 4096)

#define DO_MUNMAP       1

int
main(int argc, char **argv)
{
        int cnt, i;
        char *p, **pargv;
        kvm_t *kd;
        struct kinfo_proc *kp;

        p = mmap(NULL, ARGSIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
            -1, 0);
        if (p == MAP_FAILED)
                err(1, "mmap");

        /*
         * Make sure that the kernel creates an amap the mapped range
         * by populating it with pages.
         */
        memset(p, 0, ARGSIZE);

        /*
         * Now unmap the first page. This causes the mapped
         * range to be split into two vm_map_entries. The
         * first entry is freed. The second vm_amp_entry represents
         * the second page in the range. The second map entry points
         * to the previously created amap. Since the amap still
         * contains two slots to represent the original range,
         * the second vm_map_entry must point into the amap with
         * an offset of 1 to refer to the slot representing the second
         * page.
         */
#if DO_MUNMAP
        if (munmap(p, 4096) == -1)
                err(1, "munmap");
#endif

        p[4096] = 'A';
        argv[0] = &p[4096];

        kd = kvm_openfiles("./libkvmtest", "/dev/mem", NULL, O_RDONLY,
            NULL);
        if (kd == NULL)
                errx(1, "kvm_open");
        kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), sizeof *kp, &cnt);
        if (kp == NULL || cnt != 1)
                errx(1, "kvm_getprocs");

        pargv = kvm_getargv(kd, kp, 8192);
        if (pargv == NULL)
                errx(1, "pargv");
        for (i = 0; pargv[i] != NULL; i++)
                printf("pargv[%d]: %s\n", i, pargv[i]);

        return 0;

}

Reply via email to