_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;
}