aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libxc/xc_linux_osdep.c
diff options
context:
space:
mode:
authorWangzhenguo <wangzhenguo@huawei.com>2012-08-17 14:46:48 +0100
committerWangzhenguo <wangzhenguo@huawei.com>2012-08-17 14:46:48 +0100
commitec5dbe86ae08ecdb2eb3d6b3c15708fd508ddbf5 (patch)
tree11884e6bd6ca16ff5b414f5ab2e0806aebe838a5 /tools/libxc/xc_linux_osdep.c
parentae10d8e67da181dce7a6fac33f0c49fd87806b98 (diff)
downloadxen-ec5dbe86ae08ecdb2eb3d6b3c15708fd508ddbf5.tar.gz
xen-ec5dbe86ae08ecdb2eb3d6b3c15708fd508ddbf5.tar.bz2
xen-ec5dbe86ae08ecdb2eb3d6b3c15708fd508ddbf5.zip
libxc/Linux: Add VM_DONTCOPY flag of the VMA of the hypercall buffer
This avoids the hypercall buffer becoming CoW on fork. In multi-threads and multi-processes environment, e.g. the process has two threads, thread A may call hypercall, thread B may call fork() to create child process. After forking, all pages of the process including hypercall buffers are cow. It will cause a write protection and return EFAULT error if hypervisor calls copy_to_user in hypercall in thread A context, Fix: 1. Before hypercall: use MADV_DONTFORK of madvise syscall to make the hypercall buffer not to be copied to child process after fork. 2. After hypercall: undo the effect of MADV_DONTFORK for the hypercall buffer by using MADV_DOFORK of madvise syscall. 3. Use mmap/nunmap for memory alloc/free instead of malloc/free to bypass libc. Note: Child processes must not use the opened xc_{interface,evtchn,gnttab,gntshr} handle that inherits from parents. They should reopen the handle if they want to interact with xc. Otherwise, it may cause segment fault to access hypercall buffer caches of the handle. Signed-off-by: Zhenguo Wang <wangzhenguo@huawei.com> Signed-off-by: Xiaowei Yang <xiaowei.yang@huawei.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> [ ijc -- s/ptr/p/ to fix build & tweaked the wording of the comments slightly. ] Committed-by: Ian Campbell <ian.campbell@citrix.com>
Diffstat (limited to 'tools/libxc/xc_linux_osdep.c')
-rw-r--r--tools/libxc/xc_linux_osdep.c19
1 files changed, 9 insertions, 10 deletions
diff --git a/tools/libxc/xc_linux_osdep.c b/tools/libxc/xc_linux_osdep.c
index 471cec5f3f..33fdba3605 100644
--- a/tools/libxc/xc_linux_osdep.c
+++ b/tools/libxc/xc_linux_osdep.c
@@ -93,22 +93,21 @@ static void *linux_privcmd_alloc_hypercall_buffer(xc_interface *xch, xc_osdep_ha
size_t size = npages * XC_PAGE_SIZE;
void *p;
- p = xc_memalign(xch, XC_PAGE_SIZE, size);
- if (!p)
- return NULL;
+ /* Address returned by mmap is page aligned. */
+ p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0);
- if ( mlock(p, size) < 0 )
- {
- free(p);
- return NULL;
- }
+ /* Do not copy the VMA to child process on fork. Avoid the page being COW
+ on hypercall. */
+ madvise(p, npages * XC_PAGE_SIZE, MADV_DONTFORK);
return p;
}
static void linux_privcmd_free_hypercall_buffer(xc_interface *xch, xc_osdep_handle h, void *ptr, int npages)
{
- munlock(ptr, npages * XC_PAGE_SIZE);
- free(ptr);
+ /* Recover the VMA flags. Maybe it's not necessary */
+ madvise(ptr, npages * XC_PAGE_SIZE, MADV_DOFORK);
+
+ munmap(ptr, npages * XC_PAGE_SIZE);
}
static int linux_privcmd_hypercall(xc_interface *xch, xc_osdep_handle h, privcmd_hypercall_t *hypercall)