Alon
Alon

Reputation: 1804

mprotect and file handles

I have this simple program where I am trying to protect a block of memory, and then read a file into that memory, releasing it when it segfaults.. first I thought there was only a problem if the file is a fifo.. but now it seems that even for a normal file it fails,

this is the code:

#include <errno.h>
#include <string.h>
#include <iostream>
#include <assert.h>
#include <malloc.h>
#include <sys/mman.h>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <unistd.h>
#include <signal.h>
using namespace std;

#define BUFFER_SIZE 8000
#define handle_error(msg) \
    do { cout << __LINE__ << endl ;perror(msg); exit(EXIT_FAILURE); } while (0)

volatile int fault_count = 0;
char* buffer = 0;
int size = 40960;

int my_fault_handler(void* addr, int serious) {
    if (mprotect(buffer, size,
                 PROT_READ | PROT_WRITE) == -1)
         handle_error("mprotect");
    ++fault_count;
    cout << "Segfaulting" << endl;
    return 1;
}


static void handler(int sig, siginfo_t *si, void *unused) {
    my_fault_handler(si ->si_addr, sig);
}
int main (int argc, char *argv[])
{
    long pagesize = sysconf(_SC_PAGESIZE);
    struct sigaction sa;

   sa.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = &handler;
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
        perror("sigaction");

    cerr << "pageSize: " << pagesize << endl;

    buffer = (char*)memalign(pagesize, size);
    if (buffer == NULL)
        handle_error("memalign");
    if (mprotect(buffer, size, PROT_READ) == -1)
        handle_error("mprotect");

    FILE* file = fopen("test", "r");
    cout << "File Open" << endl;
    if (!file) {
        cout << "Failed opening file " << strerror(errno) << endl;
        return 0;
    }

    //*buffer = 0;
    while(fread(buffer, pagesize*2, 1, file)) {
       if (mprotect(buffer, size,
                    PROT_READ) == -1)
            handle_error("mprotect");
    }
    cout << ' ' << strerror(errno) << endl;

    return(0);
}

note the //*buffer = 0;, if I unmark this line the program segfaults and works correctly.. anyone has any idea? the errno is bad address.

Thanks!

UPDATE: It seems a similiar question was asked here: Loading MachineCode From File Into Memory and Executing in C -- mprotect Failing where posix_memalign was suggested, I have tried this and it didn't work.

Upvotes: 0

Views: 822

Answers (1)

Anya Shenanigans
Anya Shenanigans

Reputation: 94729

The problem is that you're not checking for an error in the FILE handle after a short read.

What the system would tell you is that the first fread failed and didn't trigger the fault handler.

If you checked for ferror outside the loop (sloppy as an example):

while(fread(buffer, pagesize*2, 1, file)) {
   if (mprotect(buffer, size,
                PROT_READ) == -1)
        handle_error("mprotect");
}
if (ferror(file) != 0) {
    cout << "Error" << endl;
}

Why it failed is that the underlying read failed, and returned an errno of 14 (EFAULT), which is not quite what is documented to happen when read fails in this situation (it says that Buf points outside the allocated address space.)

You can only trust the signal handler to be triggered in the mprotect case when the code in question is running in the user context, most system calls will fail and return EFAULT in the case that the buffer is invalid or does not have the correct permissions.

Upvotes: 2

Related Questions