2

How can a program signal itself when it does a write access to a configurable memory region?

This would be something similar to the data-breakpoint feature found in some debuggers. POSIX compliance is desired but not required as long as it works on Linux.

Here there is an illustrative code of what I would like:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void configure_trap(void *FROM, void *TO){
  /*
  Set a trap on write access to any memory location from
    address FROM to address TO.
  When the trap is triggered, send SIGTRAP to the process.
  There is no need for an answer to have the full code, just
    an indication on how to proceed.
  */
}

char *ptr;

void trap_signal_handler(int signum){
  if(ptr[123] == 'x'){
    printf("Invalid value in ptr[123] !!!\n");
    /*
    Print a backtrace using libunwind. (Not part of this question.)
    */
  }
}

void some_function(){
  ptr[123] = 'x';
  /*
  This write access could be performed directly in this function or
    another function called directly or indirectly by this one and it
    could reside in this program or in an external library or could even
    be performed in a system call.
  trap_signal_handler should be called at this point.
  After the signal handler has been executed, program should resume
    normal operation.
  */
}

int main(){
  struct sigaction sa = { .sa_handler = trap_signal_handler };
  sigaction(SIGTRAP, &sa, NULL);
  ptr = malloc(1024);
  configure_trap(&ptr[123], &ptr[123]);
  some_function();
  return(0);
}

Thanks!

vicencb
  • 303
  • 1
  • 6

2 Answers2

2

First, use mprotect() to mark a page read-only. Then when it is written, SIGSEGV will be raised. You will have installed a signal handler for this, and if it is done using sigaction you can know what address was accessed by inspecting si_addr. For more on this, see: C SIGSEGV Handler & Mprotect

Note that mprotect() has a granularity of one page, meaning if you try to protect a single byte, you will actually have protected 4 KB (if that's your page size).

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • How can I use this approach to call `trap_signal_handler` AFTER the write has been performed? – vicencb Aug 06 '17 at 11:47
  • @vicencb: You can't. – John Zwinck Aug 06 '17 at 11:59
  • 1
    In practice, the pages are marked inaccessible, so that all accesses to the page cause a SIGSEGV; and the signal handler *emulates the instruction*. This is the only way that works reliably for more than a single (initial) access. The signal handler can then either raise another signal, or call the other signal handler directly (with a modified `siginfo_t` structure). All this is, of course, hardware-specific. – Nominal Animal Aug 06 '17 at 13:41
  • @JohnZwinck: Thank you for your answer, it helped me to find the solution.I have posted it as another answer. – vicencb Aug 09 '17 at 21:53
1

Use the https://github.com/vicencb/qdbp project.

  1. It first mprotects the memory page as read-only.
  2. When a SIGSEGV is raised it single steps your program one instruction at a time until the one that caused the write to the read-only memory.
  3. Then it calls your callback.
vicencb
  • 303
  • 1
  • 6