Changeset View
Standalone View
tests/sys/kern/kern_copyin.c
Show All 28 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/exec.h> | #include <sys/exec.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/user.h> | #include <sys/user.h> | ||||
#include <sys/mman.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <atf-c.h> | #include <atf-c.h> | ||||
Show All 22 Lines | |||||
{ | { | ||||
int ret; | int ret; | ||||
ret = fcntl(scratch_file, F_GETLK, (const void *)uaddr); | ret = fcntl(scratch_file, F_GETLK, (const void *)uaddr); | ||||
return (ret == -1 ? errno : 0); | return (ret == -1 ? errno : 0); | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef __amd64__ | static int | ||||
static uintptr_t | get_vm_layout(struct kinfo_vm_layout *kvm) | ||||
get_maxuser_address(void) | |||||
{ | { | ||||
struct kinfo_vm_layout kvm; | |||||
size_t len; | size_t len; | ||||
int error, mib[4]; | int mib[4]; | ||||
mib[0] = CTL_KERN; | mib[0] = CTL_KERN; | ||||
mib[1] = KERN_PROC; | mib[1] = KERN_PROC; | ||||
mib[2] = KERN_PROC_VM_LAYOUT; | mib[2] = KERN_PROC_VM_LAYOUT; | ||||
mib[3] = getpid(); | mib[3] = getpid(); | ||||
len = sizeof(kvm); | len = sizeof(*kvm); | ||||
error = sysctl(mib, nitems(mib), &kvm, &len, NULL, 0); | |||||
if (error != 0) | |||||
return (0); | |||||
return (kvm.kvm_max_user_addr); | return (sysctl(mib, nitems(mib), kvm, &len, NULL, 0)); | ||||
} | } | ||||
#endif | |||||
#define FMAX ULONG_MAX | #define FMAX ULONG_MAX | ||||
#if __SIZEOF_POINTER__ == 8 | #if __SIZEOF_POINTER__ == 8 | ||||
/* PR 257193 */ | /* PR 257193 */ | ||||
#define ADDR_SIGNED 0x800000c000000000 | #define ADDR_SIGNED 0x800000c000000000 | ||||
#endif | #endif | ||||
ATF_TC_WITHOUT_HEAD(kern_copyin); | ATF_TC_WITHOUT_HEAD(kern_copyin); | ||||
ATF_TC_BODY(kern_copyin, tc) | ATF_TC_BODY(kern_copyin, tc) | ||||
{ | { | ||||
char template[] = "copyin.XXXXXX"; | char template[] = "copyin.XXXXXX"; | ||||
struct kinfo_vm_layout kvm; | |||||
uintptr_t maxuser; | uintptr_t maxuser; | ||||
long page_size; | |||||
void *addr; | |||||
int error; | |||||
#if defined(__mips__) | addr = MAP_FAILED; | ||||
/* | |||||
* MIPS has different VM layout: the UVA map on mips ends the | |||||
* highest mapped entry at the VM_MAXUSER_ADDRESS - PAGE_SIZE, | |||||
* while all other arches map either stack or shared page up | |||||
* to the VM_MAXUSER_ADDRESS. | |||||
*/ | |||||
maxuser = VM_MAXUSER_ADDRESS - PAGE_SIZE; | |||||
#elif defined(__amd64__) | |||||
maxuser = get_maxuser_address(); | |||||
ATF_REQUIRE(maxuser != 0); | |||||
#else | |||||
maxuser = VM_MAXUSER_ADDRESS; | |||||
#endif | |||||
error = get_vm_layout(&kvm); | |||||
ATF_REQUIRE(error == 0); | |||||
emaste: I would suggest rewriting this (drop the "now") to avoid implying changed behaviour ("can now")… | |||||
page_size = sysconf(_SC_PAGESIZE); | |||||
ATF_REQUIRE(page_size != (long)-1); | |||||
maxuser = kvm.kvm_max_user_addr; | |||||
Not Done Inline Actions@andrew should we use sysconf(_SC_PAGESIZE)? emaste: @andrew should we use `sysconf(_SC_PAGESIZE)`? | |||||
Not Done Inline ActionsI've been using getpagesize() as it can normally get the page size without a syscall. andrew: I've been using `getpagesize()` as it can normally get the page size without a syscall. | |||||
Not Done Inline ActionsOh right I forgot about that... indeed getpagesize() is the way to go. emaste: Oh right I forgot about that... indeed `getpagesize()` is the way to go. | |||||
Not Done Inline ActionsOh, but _SC_PAGESIZE will also avoid the syscall: case _SC_PAGESIZE: return (getpagesize()); and it seems POSIX deprecated getpagesize(). So sysconf(_SC_PAGESIZE) is preferred I guess. emaste: Oh, but `_SC_PAGESIZE` will also avoid the syscall:
```
case _SC_PAGESIZE… | |||||
Not Done Inline ActionsD35352 for a .Xr and POSIX deprecation mention in getpagesize(3). emaste: D35352 for a .Xr and POSIX deprecation mention in getpagesize(3). | |||||
scratch_file = mkstemp(template); | scratch_file = mkstemp(template); | ||||
ATF_REQUIRE(scratch_file != -1); | ATF_REQUIRE(scratch_file != -1); | ||||
Not Done Inline ActionsIf shared page mapping is not randomized, wouldn't this mmap destroy it? kib: If shared page mapping is not randomized, wouldn't this mmap destroy it? | |||||
Done Inline ActionsThat's what MAP_EXCL is for. According to mmap man page: If MAP_EXCL is not specified, a successful MAP_FIXED request replaces any previous mappings for the process' pages in the range from addr to addr + len. In contrast, if MAP_EXCL is specified, the request will fail if a mapping already exists within the range. kd: That's what MAP_EXCL is for. According to mmap man page:
```
If… | |||||
unlink(template); | unlink(template); | ||||
/* | |||||
* Since the shared page address can be randomized we need to make | |||||
* sure that something is mapped at the top of the user address space. | |||||
* Otherwise reading bytes from maxuser-X will fail rendering this test | |||||
* useless. | |||||
*/ | |||||
if (kvm.kvm_shp_addr + kvm.kvm_shp_size < maxuser) { | |||||
Not Done Inline ActionsI quite dislike it. I think a much better approach is to provide explicit shared page address in the uva layout sysctl, as I proposed earlier. Then you check if shp address intersects with maxuser - pagesize. Slightly more elaborate but also working approach is to flag shared page in the kinfo_vmentry output of sysctl KERN_PROC_VMMAP. kib: I quite dislike it. I think a much better approach is to provide explicit shared page address… | |||||
Not Done Inline ActionsThis assumes that the 'shared page mapping' size is exactly one page. One day it have to be grown in size, we are already very low on free space there after vdso's consume most of it. kib: This assumes that the 'shared page mapping' size is exactly one page. One day it have to be… | |||||
kibUnsubmitted Not Done Inline ActionsI think the most correct approach is to check the vm map and see if anything is mapped at the maxuser-1, and if it is, whether the mapping is guard. But for now let it go. kib: I think the most correct approach is to check the vm map and see if anything is mapped at the… | |||||
addr = mmap((void *)(maxuser - page_size), page_size, PROT_READ, | |||||
MAP_ANON | MAP_FIXED, -1, 0); | |||||
ATF_REQUIRE(addr != MAP_FAILED); | |||||
} | |||||
ATF_CHECK(copyin_checker(0, 0) == 0); | ATF_CHECK(copyin_checker(0, 0) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser - 10, 9) == 0); | ATF_CHECK(copyin_checker(maxuser - 10, 9) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser - 10, 10) == 0); | ATF_CHECK(copyin_checker(maxuser - 10, 10) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser - 10, 11) == EFAULT); | ATF_CHECK(copyin_checker(maxuser - 10, 11) == EFAULT); | ||||
ATF_CHECK(copyin_checker(maxuser - 1, 1) == 0); | ATF_CHECK(copyin_checker(maxuser - 1, 1) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser, 0) == 0); | ATF_CHECK(copyin_checker(maxuser, 0) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser, 1) == EFAULT); | ATF_CHECK(copyin_checker(maxuser, 1) == EFAULT); | ||||
ATF_CHECK(copyin_checker(maxuser, 2) == EFAULT); | ATF_CHECK(copyin_checker(maxuser, 2) == EFAULT); | ||||
ATF_CHECK(copyin_checker(maxuser + 1, 0) == 0); | ATF_CHECK(copyin_checker(maxuser + 1, 0) == 0); | ||||
ATF_CHECK(copyin_checker(maxuser + 1, 2) == EFAULT); | ATF_CHECK(copyin_checker(maxuser + 1, 2) == EFAULT); | ||||
ATF_CHECK(copyin_checker(FMAX - 10, 9) == EFAULT); | ATF_CHECK(copyin_checker(FMAX - 10, 9) == EFAULT); | ||||
ATF_CHECK(copyin_checker(FMAX - 10, 10) == EFAULT); | ATF_CHECK(copyin_checker(FMAX - 10, 10) == EFAULT); | ||||
ATF_CHECK(copyin_checker(FMAX - 10, 11) == EFAULT); | ATF_CHECK(copyin_checker(FMAX - 10, 11) == EFAULT); | ||||
#if __SIZEOF_POINTER__ == 8 | #if __SIZEOF_POINTER__ == 8 | ||||
ATF_CHECK(copyin_checker(ADDR_SIGNED, 1) == EFAULT); | ATF_CHECK(copyin_checker(ADDR_SIGNED, 1) == EFAULT); | ||||
ATF_CHECK(copyin_checker2(ADDR_SIGNED) == EFAULT); | ATF_CHECK(copyin_checker2(ADDR_SIGNED) == EFAULT); | ||||
#endif | #endif | ||||
if (addr != MAP_FAILED) | |||||
munmap(addr, PAGE_SIZE); | |||||
} | } | ||||
ATF_TP_ADD_TCS(tp) | ATF_TP_ADD_TCS(tp) | ||||
{ | { | ||||
ATF_TP_ADD_TC(tp, kern_copyin); | ATF_TP_ADD_TC(tp, kern_copyin); | ||||
return (atf_no_error()); | return (atf_no_error()); | ||||
} | } |
I would suggest rewriting this (drop the "now") to avoid implying changed behaviour ("can now"); in several years it won't be "new" behaviour.