When running valgrind on the following program the assertion fails:
#include <unistd.h>
#include <sys/mman.h>
#in开发者_JAVA百科clude <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <ucontext.h>
static size_t pageSize = 4096;
uint8_t *bs;
static void sig(int num,
siginfo_t *info, void *unused) {
ucontext *p = (ucontext *)unused;
uint8_t *addr = (uint8_t *)info->si_addr;
wprintf(L"rax=%lx\n", p->uc_mcontext.gregs[REG_RAX]);
wprintf(L"addr=%lx\n", addr);
assert(mprotect(bs, pageSize*4,
PROT_READ | PROT_WRITE) == 0);
}
bool setsig() {
sigset_t mask;
struct sigaction sa;
if (sigemptyset(&mask))
return false;
sa.sa_sigaction = sig;
sa.sa_mask = mask;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV,&sa, NULL) != 0)
return false;
return true;
}
int main() {
assert(setsig());
bs = (uint8_t *)mmap(NULL, pageSize*4,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(bs != MAP_FAILED);
bs[pageSize] = 3; // !!
assert(bs[pageSize] == 3);
return 0;
}
RAX holds (bs + pageSize) at the faulting instruction, corresponding to (!!) in the code. However, si_addr does not match RAX in the ucontext of the signal handler (the value of RAX in the ucontext is equal to 'bs'). When (!!) is reexecuted after enabling writes RAX contains (bs). Executing outside valgrind works as expected.
Have I done something to cause undefined behaviour, or is it possible that this is a bug in GCC or valgrind?
It will work if you use precise exceptions.
valgrind --vex-iropt-precise-memory-exns=yes ./your_program
This page precisely describes what you are trying to do :-))
If you're using signals in clever ways (for example, catching SIGSEGV, modifying page state and restarting the instruction), you're probably relying on precise exceptions. In this case, you will need to use
--vex-iropt-precise-memory-exns=yes
.
精彩评论