Question

If I mprotect a segment with PROT_NONE and if a SIGSEGV occurs due to a write which gets handled by sigaction with sa_sigaction, We will be able to find the address where the fault occurs using siginfo_t's si_addr. But is there a way to find the data and the length of the data that was tried to be written ?

I am trying to do this because I trying a copy-on-write mechanism for my project.

Was it helpful?

Solution

You can't find the data the process tried to write, nor does it make sense to ask about its size. If you could get the data it would mean the kernel had already copied it somewhere.

You get a SIGSEGV on an entire page. That is, regardless of the data the process is writing, you will get exactly one fault per page - the first time you try to write a byte. So all you need to do is:

  • Keep track of page state
  • Increase permissions as needed

OTHER TIPS

The kernel doesn't know, so it can't tell you. But you can try to find out if you want. You have the code address that faulted on the stack, so you can disassemble the code there to try to figure it out. There is no other way to know (think about it if it's not obvious to you why). The instruction faulted because it touched a protected page, that's all that's known unless you analyze the assembly code.

If you can't tell what object you're dealing with by knowing only the page that faulted, I'd strongly suggest you reconsider changing your design so that you do. (posix_memalign, perhaps?)

Update: Don't forget that you need to have a hook that's called on every context switch. You may need to copy pages around on each hook. For example:

  1. Context A is accessing page Q with CoW semantics. Context A gets read-only access to the page.

  2. Context B is accessing page Q with CoW semantics. Context B gets read-only access to the page.

  3. Context A goes to modify the page, we make a copy for context B. Context A now has write access to the page and modifies it.

  4. We switch from context A to context B. At this point, you must switch in the copy of the page you made for context B.

Note that the other way around this is to have contexts make specific calls to map and lock pages. That won't work if you allow a context to hold a mapping across a context switch -- at least, not without a lot of extra work.

When you use mprotect() you know the starting address and length of the memory segment you're protecting. You need to store that information somewhere so you can use it later.

Once you've protected your memory segment, if you access it in a way that violates the protection you'll get a SIGSEGV signal. In the signal handler you'll get a pointer to siginfo_t. This gives you the information you need. si_addr gives you the address of the instruction that caused the illegal access, and si_ptr gives you the address that was accessed illegally. You then compare si_ptr to the memory segments you've protected until you find the one it belongs to, and that's the one you need to copy for your "copy on write"... once you've done that you then need to call something like setcontext() or siglongjmp() to continue running from a known location.

I hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top