How to capture memory, read and write using sigsegv?

How do I fool Linux into thinking that memory read / write was successful? I am writing a C ++ library so that all reads / writes are redirected and processed transparently to the end user. At any time, when a variable is written or read, the library will need to catch this request and drop it to the hardware simulation, which will process the data from there.

Please note that my library is platform dependent:

Linux ubuntu 3.16.0-39-generi # 53 ~ 14.04.1-Ubuntu SMP x86_64 GNU / Linux

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

Current approach: catch SIGSEGV and increase REG_RIP

My current approach involves getting a memory area using mmap() and turning off access using mprotect() . I have a SIGSEGV handler to get information containing a memory address, export read / write to another location, and then increase the REG_RIP context.

 void handle_sigsegv(int code, siginfo_t *info, void *ctx) { void *addr = info->si_addr; ucontext_t *u = (ucontext_t *)ctx; int err = u->uc_mcontext.gregs[REG_ERR]; bool is_write = (err & 0x2); // send data read/write to simulation... // then continue execution of program by incrementing RIP u->uc_mcontext.gregs[REG_RIP] += 6; } 

This works for very simple cases, such as:

 int *num_ptr = (int *)nullptr; *num_ptr = 10; // write segfault 

But for something even a little more complicated, I get SIGABRT:

30729 Invalid instruction (flushing kernel) ./$ target

Using mprotect () in a SIGSEGV Handler

If I do not need to increase REG_RIP, handle_sigsegv() will be called again and again by the kernel until the memory area becomes readable or writable. I could run mprotect() for this particular address, but this has a few caveats:

  • Subsequent access to the memory will not call SIGSEGV due to the fact that the memory area now has the ability PROT_WRITE. I tried to create a stream that continuously marks the area as PROT_NONE, but this does not elude the following point:
  • mprotect() will read or write to memory at the end of the day, which will invalidate the use of my library.

Writing a device driver

I also tried to write a device module so that the library could call mmap() on the char device, where the driver handles reading and writing from there. This makes sense in theory, but I could not (or did not have the knowledge) catch every load / store processor problems on the device. I tried to overwrite the displayed vm_operations_struct and / or inode address_space_operations structure, but this will only read / write when the page crashes or the page is flushed to the backup storage.

Perhaps I could use mmap() and mprotect() as described above on a device that doesn't write data anywhere (similar to /dev/null ), then has a process that recognizes read / write and routes the data from there (?) .

Use syscall() and provide the restore function of the restore

The following is deduced from the segvcatch 1 project: converts segfaults to exceptions.

 #define RESTORE(name, syscall) RESTORE2(name, syscall) #define RESTORE2(name, syscall)\ asm(\ ".text\n"\ ".byte 0\n"\ ".align 16\n"\ "__" #name ":\n"\ " movq $" #syscall ", %rax\n"\ " syscall\n"\ ); RESTORE(restore_rt, __NR_rt_sigreturn) void restore_rt(void) asm("__restore_rt") __attribute__ ((visibility("hidden"))); extern "C" { struct kernel_sigaction { void (*k_sa_sigaction)(int, siginfo_t *, void *); unsigned long k_sa_flags; void (*k_sa_restorer)(void); sigset_t k_sa_mask; }; } // then within main ... struct kernel_sigaction act; act.k_sa_sigaction = handle_sigegv; sigemptyset(&act.k_sa_mask); act.k_sa_flags = SA_SIGINFO|0x4000000; act.k_sa_restorer = restore_rt; syscall(SYS_rt_sigaction, SIGSEGV, &act, NULL, _NSIG / 8); 

But this ends up being the same as the normal sigaction() configuration. If I do not set the restore function, the signal handler is not called more than once, even if the memory area is still inaccessible. Perhaps there is another trick that I could do with the kernel signal here.


Again, the whole purpose of the library is to transparently handle reads and writes to memory. There may be a much better way to do something, perhaps with ptrace() or even update the kernel code that generates the segfault signal, but the important part is that the end-user code does not need to be changed. I have seen examples using setjmp() and longjmp() to continue after segfault, but for this you will need to add these calls to each memory access. The same goes for converting segfault to try / catch.


1 segvcatch project
+7
c ++ linux memory ubuntu kernel
source share
1 answer

You can use mprotect and avoid the first problem you noticed, also having a SIGSEGV handler setting the T flag in the flag register. Then you add a SIGTRAP handler that restores protected memory and clears the T flag.

The T flag forces the processor one step, so when the SEGV handler returns, it will execute this single instruction and then immediately TRAP.

This still leaves you with a second problem - the read / write instruction will actually be. You may be able to get around this problem by carefully changing the memory before and / or after the instruction in two signal handlers ...

+3
source share

All Articles