2

I have looked at the various topics relating to this, but couldn't find this specific issue I am having.

Things I looked at: Injecting code into executable at runtime C SIGSEGV Handler & Mprotect Can I write-protect every page in the address space of a Linux process? How to write a signal handler to catch SIGSEGV?

I am able to handle SIGSEGV gracefully when the protection needs to be set to either PROT_READ or PROT_WRITE in the handler. However, when I try to inject instructions with mmap, and then use mprotect to set it to PROT_READ only, and then I execute the instructions via inline assembly, it causes a SIGSEGV as intended, but the handler is unable to get the originating address causing the signal, so I am unable to mprotect it to PROT_READ | PROT_EXEC.

Example:

void sigHandler(int signum, siginfo_t *info, void *ptr) {

    printf("Received signal number: %d\n", signum);
    printf("Signal originates from process %lu\n",
        (unsigned long)info->si_pid);

    printf("SIGSEGV caused by this address: ? %p\n", info->si_addr);

    char * alignedbaseAddr = (((unsigned int)(info->si_addr)) >> 12) * getPageSize(); 
    printf("Aligning to %p\n", alignedbaseAddr);
    //flip this page to be r+x
    mprotect(alignedbaseAddr, getPageSize(), PROT_READ | PROT_EXEC);
}
void setupSignalHandler() {
    action.sa_sigaction = sigHandler;
    action.sa_flags = SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(SIGSEGV, &action, NULL);
}

int main(int argc, char *argv[]) {
    char * baseAddr = (char*)mmap(NULL, getDiskSize(), PROT_READ | PROT_WRITE,    MAP_SHARED, fd, 0);
    if(baseAddr == MAP_FAILED) {
        perror("Unable to mmap.");
    }
    printf("Process address space is %d\n", getDiskSize());
    //no-op filler
    for(int i = 0; i < (getDiskSize()) - 1; i++) {
        baseAddr[i] = 0x90;
    }
    //ret instruction
    baseAddr[i] = 0xc3;

    if( mprotect(baseAddr, getDiskSize(), PROT_READ) == -1) {
        perror("mprotect");
        exit(1);
    }

    printf("Protecting addresses: %p to %p for READ_ONLY\n", baseAddr, baseAddr + getDiskSize() - 1);
    setupSignalHandler();


    __asm__
    (
     "call %%eax;"
     : "=a" (output)
     : "a" (baseAddr)
    );

    printf("Will this ever print?");
    //close fd, and unmap memory
    cleanUp();
    return EXIT_SUCCESS;
}

Here is the resulting output:

Received signal number: 11
Signal originates from process 0
SIGSEGV caused by this address: ? (nil)

//the above output repeatedly loops, since it fails to "re mprotect" that page.

Architecture: x86 32 bit OS: Ubuntu 11.04 - Linux version 2.6.38-12-generic (buildd@vernadsky) (gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) )

Any ideas? The above logic works fine for simply read and writing into memory. Is there a better way to execute instructions at runtime as opposed to inline assembly?

Thanks in advance!

Community
  • 1
  • 1
Setzer
  • 739
  • 4
  • 10
  • 18

1 Answers1

2

In that case, the faulting address is the instruction pointer. Cast your third argument ptr (of your signal handler installed with SA_SIGINFO) to a ucontext_t, and retrieve the appropriate register, perhaps as (untested code!)

ucontext_t *uc = ptr;
void* faultyip = uc->uc_mcontext.gregs[REG_IP];

Read carefully /usr/include/sys/ucontext.h for more.

I'm interested to know why you are asking!!

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Basile, I am trying to simulate virtual memory by triggering page faults when executing instructions like how a real OS would do. – Setzer Nov 22 '11 at 07:55
  • The idea is that I'll parse an executable, and feed it into this VM simulator. I tried using above the logic to re-mprotect to have execute permissions, but still the same issue. The EIP is pointing correctly at the instruction causing the fault (the context is correct), but the same issue occurs. Any other ideas? I can post the results if you like? – Setzer Nov 22 '11 at 08:01
  • the si->code under the siginfo * type yields a value of 128, which is either SEGV_MAPERR, or SEGV_ACCERR. Do you know which one it is? – Setzer Nov 22 '11 at 08:49
  • So just as a test, if I save off the "baseAddr" with a gbl variable, then use mprotect on that saved off value, then it works fine. The problem is that sigHandler does not know the originating address causing the fault, I have a feeling this has to do with the inline assembly. Plus, the EIP is pointing to the text section which is already "executable" so the EIP won't help here unless I'm missing something. – Setzer Nov 23 '11 at 00:49