Re-add proper support for mremap(). Also, fix a bug in munmap().
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2142 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index 0ce9b13..5878826 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -1580,6 +1580,7 @@
extern Segment *VG_(find_segment)(Addr a);
extern Segment *VG_(next_segment)(Segment *);
+extern Segment *VG_(split_segment)(Addr a);
extern Bool VG_(seg_contains)(const Segment *s, Addr ptr, UInt size);
extern Bool VG_(seg_overlaps)(const Segment *s, Addr ptr, UInt size);
diff --git a/coregrind/vg_memory.c b/coregrind/vg_memory.c
index ff6a040..3a50975 100644
--- a/coregrind/vg_memory.c
+++ b/coregrind/vg_memory.c
@@ -114,8 +114,8 @@
VG_(SkipNode_Free)(&sk_segments, s);
}
-/* Split a segment at address a */
-static Segment *split_segment(Addr a)
+/* Split a segment at address a, returning the new segment */
+Segment *VG_(split_segment)(Addr a)
{
Segment *s = VG_(SkipList_Find)(&sk_segments, &a);
Segment *ns;
@@ -157,6 +157,7 @@
Segment *s;
Segment *next;
static const Bool debug = False || mem_debug;
+ Addr end = addr+len;
if (len == 0)
return;
@@ -174,19 +175,25 @@
for(s = VG_(SkipList_Find)(&sk_segments, &addr);
s != NULL && s->addr < (addr+len);
s = next) {
+ Addr seg_end = s->addr + s->len;
/* fetch next now in case we end up deleting this segment */
next = VG_(SkipNode_Next)(&sk_segments, s);
if (debug)
- VG_(printf)("unmap: addr=%p s=%p ->addr=%p len=%d end=%p\n",
- addr, s, s->addr, s->len, s->addr+s->len);
+ VG_(printf)("unmap: addr=%p-%p s=%p ->addr=%p-%p len=%d\n",
+ addr, addr+len, s, s->addr, s->addr+s->len, s->len);
- if (!VG_(seg_overlaps)(s, addr, len))
+ if (!VG_(seg_overlaps)(s, addr, len)) {
+ if (debug)
+ VG_(printf)(" (no overlap)\n");
continue;
+ }
/* 4 cases: */
- if (addr > s->addr && addr < (s->addr + s->len)) {
+ if (addr > s->addr &&
+ addr < seg_end &&
+ end >= seg_end) {
/* this segment's tail is truncated by [addr, addr+len)
-> truncate tail
*/
@@ -194,7 +201,7 @@
if (debug)
VG_(printf)(" case 1: s->len=%d\n", s->len);
- } else if (addr <= s->addr && (addr+len) >= (s->addr + s->len)) {
+ } else if (addr <= s->addr && end >= seg_end) {
/* this segment is completely contained within [addr, addr+len)
-> delete segment
*/
@@ -204,7 +211,7 @@
if (debug)
VG_(printf)(" case 2: s==%p deleted\n", s);
- } else if ((addr+len) > s->addr && (addr+len) < (s->addr+s->len)) {
+ } else if (addr <= s->addr && end > s->addr && end < seg_end) {
/* this segment's head is truncated by [addr, addr+len)
-> truncate head
*/
@@ -216,14 +223,14 @@
if (debug)
VG_(printf)(" case 3: s->addr=%p s->len=%d delta=%d\n", s->addr, s->len, delta);
- } else if (addr > s->addr && (addr+len) < (s->addr + s->len)) {
+ } else if (addr > s->addr && end < seg_end) {
/* [addr, addr+len) is contained within a single segment
-> split segment into 3, delete middle portion
*/
Segment *middle, *rs;
- middle = split_segment(addr);
- split_segment(addr+len);
+ middle = VG_(split_segment)(addr);
+ VG_(split_segment)(addr+len);
vg_assert(middle->addr == addr);
rs = VG_(SkipList_Remove)(&sk_segments, &addr);
@@ -455,8 +462,8 @@
vg_assert((a & (VKI_BYTES_PER_PAGE-1)) == 0);
vg_assert((len & (VKI_BYTES_PER_PAGE-1)) == 0);
- split_segment(a);
- split_segment(a+len);
+ VG_(split_segment)(a);
+ VG_(split_segment)(a+len);
for(s = VG_(SkipList_Find)(&sk_segments, &a);
s != NULL && s->addr < a+len;
diff --git a/coregrind/vg_syscalls.c b/coregrind/vg_syscalls.c
index cc829d5..30dac61 100644
--- a/coregrind/vg_syscalls.c
+++ b/coregrind/vg_syscalls.c
@@ -117,6 +117,42 @@
(*atforks[i].child)(tid);
}
+/* return true if address range entirely contained within client
+ address space */
+static Bool valid_client_addr(Addr start, UInt size, ThreadId tid, const Char *syscall)
+{
+ Addr end = start+size;
+ Addr cl_base = VG_(client_base);
+ Bool ret;
+
+ if (size == 0)
+ return True;
+
+ if (cl_base < 0x10000)
+ cl_base = 0x10000;
+
+ ret =
+ (end >= start) &&
+ start >= cl_base && start < VG_(client_end) &&
+ (end <= VG_(client_end));
+
+ if (0)
+ VG_(printf)("%s: test=%p-%p client=%p-%p ret=%d\n",
+ syscall, start, end, cl_base, VG_(client_end), ret);
+
+ if (!ret && syscall != NULL) {
+ VG_(message)(Vg_UserMsg, "Warning: client syscall %s tried to modify addresses %p-%p",
+ syscall, start, end);
+
+ if (VG_(clo_verbosity) > 1) {
+ ExeContext *ec = VG_(get_ExeContext)(tid);
+ VG_(pp_ExeContext)(ec);
+ }
+ }
+
+ return ret;
+}
+
/* ---------------------------------------------------------------------
Doing mmap, munmap, mremap, mprotect
------------------------------------------------------------------ */
@@ -162,9 +198,9 @@
VG_(map_fd_segment)(a, len, prot, flags, fd, offset, NULL);
- rr = prot & PROT_READ;
- ww = prot & PROT_WRITE;
- xx = prot & PROT_EXEC;
+ rr = prot & VKI_PROT_READ;
+ ww = prot & VKI_PROT_WRITE;
+ xx = prot & VKI_PROT_EXEC;
VG_TRACK( new_mem_mmap, a, len, rr, ww, xx );
}
@@ -195,9 +231,9 @@
VG_(mprotect_range)(a, len, prot);
- rr = prot & PROT_READ;
- ww = prot & PROT_WRITE;
- xx = prot & PROT_EXEC;
+ rr = prot & VKI_PROT_READ;
+ ww = prot & VKI_PROT_WRITE;
+ xx = prot & VKI_PROT_EXEC;
// if removing exe permission, should check and remove from exe_seg list
// if adding, should check and add to exe_seg list
@@ -207,34 +243,139 @@
}
static
-void mremap_segment ( Addr old_addr, UInt old_size, Addr new_addr,
- UInt new_size )
+Addr mremap_segment ( Addr old_addr, UInt old_size,
+ Addr new_addr, UInt new_size,
+ UInt flags, ThreadId tid)
{
- /* If the block moves, assume new and old blocks can't overlap; seems to
- * be valid judging from Linux kernel code in mm/mremap.c */
- vg_assert(old_addr == new_addr ||
- old_addr+old_size < new_addr ||
- new_addr+new_size < old_addr);
+ Addr ret;
+ Segment *seg, *next;
- if (new_size < old_size) {
- // if exe_seg
- // unmap old symbols from old_addr+new_size..old_addr+new_size
- // update exe_seg size = new_size
- // update exe_seg addr = new_addr...
- VG_TRACK( copy_mem_remap, old_addr, new_addr, new_size );
- VG_TRACK( die_mem_munmap, old_addr+new_size, old_size-new_size );
+ old_size = PGROUNDUP(old_size);
+ new_size = PGROUNDUP(new_size);
- } else {
- // if exe_seg
- // map new symbols from new_addr+old_size..new_addr+new_size
- // update exe_seg size = new_size
- // update exe_seg addr = new_addr...
- VG_TRACK( copy_mem_remap, old_addr, new_addr, old_size );
- // what should the permissions on the new extended part be??
- // using 'rwx'
- VG_TRACK( new_mem_mmap, new_addr+old_size, new_size-old_size,
- True, True, True );
+ if (PGROUNDDN(old_addr) != old_addr)
+ return -VKI_EINVAL;
+
+ if (!valid_client_addr(old_addr, old_size, tid, "mremap(old_addr)"))
+ return -VKI_EFAULT;
+
+ /* fixed at the current address means we don't move it */
+ if ((flags & VKI_MREMAP_FIXED) && (old_addr == new_addr))
+ flags &= ~(VKI_MREMAP_FIXED|VKI_MREMAP_MAYMOVE);
+
+ if (flags & VKI_MREMAP_FIXED) {
+ if (PGROUNDDN(new_addr) != new_addr)
+ return -VKI_EINVAL;
+
+ if (!valid_client_addr(new_addr, new_size, tid, "mremap(new_addr)"))
+ return -VKI_ENOMEM;
+
+ /* check for overlaps */
+ if ((old_addr < (new_addr+new_size) &&
+ (old_addr+old_size) > new_addr) ||
+ (new_addr < (old_addr+new_size) &&
+ (new_addr+new_size) > old_addr))
+ return -VKI_EINVAL;
}
+
+ /* Do nothing */
+ if (!(flags & VKI_MREMAP_FIXED) && new_size == old_size)
+ return old_addr;
+
+ seg = VG_(find_segment)(old_addr);
+
+ /* range must be contained within segment */
+ if (seg == NULL || !VG_(seg_contains)(seg, old_addr, old_size))
+ return -VKI_EINVAL;
+
+ next = VG_(next_segment)(seg);
+
+ if (0)
+ VG_(printf)("mremap: old_addr+new_size=%p next->addr=%p flags=%d\n",
+ old_addr+new_size, next->addr, flags);
+
+ if ((flags & VKI_MREMAP_FIXED) ||
+ (next != NULL && (old_addr+new_size) > next->addr)) {
+ /* we're moving the block */
+ Addr a;
+
+ if ((flags & (VKI_MREMAP_FIXED|VKI_MREMAP_MAYMOVE)) == 0)
+ return -VKI_ENOMEM; /* not allowed to move */
+
+ if ((flags & VKI_MREMAP_FIXED) == 0)
+ new_addr = 0;
+
+ a = VG_(find_map_space)(new_addr, new_size, True);
+
+ if ((flags & VKI_MREMAP_FIXED) && a != new_addr)
+ return -VKI_ENOMEM; /* didn't find the place we wanted */
+
+ new_addr = a;
+ ret = a;
+
+ /* we've nailed down the location */
+ flags |= VKI_MREMAP_FIXED|VKI_MREMAP_MAYMOVE;
+
+ ret = VG_(do_syscall)(__NR_mremap, old_addr, old_size, new_size,
+ flags, new_addr);
+
+ if (ret != new_addr) {
+ vg_assert(VG_(is_kerror)(ret));
+ return ret;
+ }
+
+ VG_TRACK(copy_mem_remap, old_addr, new_addr,
+ (old_size < new_size) ? old_size : new_size);
+
+ if (new_size > old_size)
+ VG_TRACK(new_mem_mmap, new_addr+old_size, new_size-old_size,
+ seg->prot & VKI_PROT_READ,
+ seg->prot & VKI_PROT_WRITE,
+ seg->prot & VKI_PROT_EXEC);
+ VG_TRACK(die_mem_munmap, old_addr, old_size);
+
+ VG_(map_file_segment)(new_addr, new_size,
+ seg->prot,
+ seg->flags,
+ seg->dev, seg->ino,
+ seg->offset, seg->filename);
+
+ VG_(munmap)((void *)old_addr, old_size);
+ } else {
+ /* staying in place */
+ ret = old_addr;
+
+ if (new_size < old_size) {
+ VG_TRACK(die_mem_munmap, old_addr+new_size, old_size-new_size);
+ VG_(munmap)((void *)(old_addr+new_size), old_size-new_size);
+ } else {
+ /* we've nailed down the location */
+ flags &= ~VKI_MREMAP_MAYMOVE;
+
+ if (0)
+ VG_(printf)("mremap: old_addr=%p old_size=%d new_size=%d flags=%d\n",
+ old_addr, old_size, new_size, flags);
+
+ ret = VG_(do_syscall)(__NR_mremap, old_addr, old_size, new_size,
+ flags, 0);
+
+ if (ret != old_addr)
+ return ret;
+
+ VG_TRACK(new_mem_mmap, old_addr+old_size, new_size-old_size,
+ seg->prot & VKI_PROT_READ,
+ seg->prot & VKI_PROT_WRITE,
+ seg->prot & VKI_PROT_EXEC);
+
+ VG_(map_file_segment)(old_addr+old_size, new_size-old_size,
+ seg->prot,
+ seg->flags,
+ seg->dev, seg->ino,
+ seg->offset, seg->filename);
+ }
+ }
+
+ return ret;
}
@@ -846,42 +987,6 @@
}
-/* return true if address range entirely contained within client
- address space */
-static Bool valid_client_addr(Addr start, UInt size, ThreadId tid, const Char *syscall)
-{
- Addr end = start+size;
- Addr cl_base = VG_(client_base);
- Bool ret;
-
- if (size == 0)
- return True;
-
- if (cl_base < 0x10000)
- cl_base = 0x10000;
-
- ret =
- (end >= start) &&
- start >= cl_base && start < VG_(client_end) &&
- (end <= VG_(client_end));
-
- if (0)
- VG_(printf)("%s: test=%p-%p client=%p-%p ret=%d\n",
- syscall, start, end, cl_base, VG_(client_end), ret);
-
- if (!ret && syscall != NULL) {
- VG_(message)(Vg_UserMsg, "Warning: client syscall %s tried to modify addresses %p-%p",
- syscall, start, end);
-
- if (VG_(clo_verbosity) > 1) {
- ExeContext *ec = VG_(get_ExeContext)(tid);
- VG_(pp_ExeContext)(ec);
- }
- }
-
- return ret;
-}
-
/* ---------------------------------------------------------------------
Vet file descriptors for sanity
------------------------------------------------------------------ */
@@ -1364,15 +1469,11 @@
PRE(mremap)
{
/* void* mremap(void * old_address, size_t old_size,
- size_t new_size, unsigned long flags); */
- MAYBE_PRINTF("mremap ( %p, %d, %d, 0x%x )\n",
- arg1, arg2, arg3, arg4);
- SYSCALL_TRACK( pre_mem_write, tid, "mremap(old_address)", arg1, arg2 );
-}
-
-POST(mremap)
-{
- mremap_segment( arg1, arg2, (Addr)res, arg3 );
+ size_t new_size, unsigned long flags, void * new_address); */
+ MAYBE_PRINTF("mremap ( %p, %d, %d, 0x%x, %p )\n",
+ arg1, arg2, arg3, arg4, arg5);
+
+ res = mremap_segment((Addr)arg1, arg2, (Addr)arg5, arg3, arg4, tid);
}
PRE(nice)
@@ -2380,6 +2481,8 @@
break;
}
case 22: /* IPCOP_shmdt */
+ if (!valid_client_addr(arg1, 1, tid, "shmdt"))
+ res = -VKI_EINVAL;
break;
case 23: /* IPCOP_shmget */
break;
@@ -2517,14 +2620,9 @@
}
case 22: /* IPCOP_shmdt */
{
- /* ### FIXME: this should call make_noaccess on the
- * area passed to shmdt. But there's no way to
- * figure out the size of the shared memory segment
- * just from the address... Maybe we want to keep a
- * copy of the exiting mappings inside valgrind? */
Segment *s = VG_(find_segment)(arg1);
- if (s->addr == arg1 && (s->flags & SF_SHM)) {
+ if (s != NULL && (s->flags & SF_SHM) && VG_(seg_contains)(s, arg1, 1)) {
VG_TRACK( die_mem_munmap, s->addr, s->len );
VG_(unmap_range)(s->addr, s->len);
}
@@ -4781,6 +4879,7 @@
SYSB_(execve, False),
SYSB_(brk, False),
SYSB_(mmap, False),
+ SYSB_(mremap, False),
#if SIGNAL_SIMULATION
SYSBA(sigaltstack, False),
@@ -4828,7 +4927,6 @@
SYSB_(personality, False),
SYSB_(chroot, False),
SYSB_(madvise, True),
- SYSBA(mremap, False),
SYSB_(nice, False),
SYSB_(setresgid32, False),
SYSB_(setfsuid32, False),