0

After hearing about it in a C course I've been experimenting with self-modifying code. I have a function which sets the memory protections for a segment of memory using mprotect:

int SetMemoryProtections(void* addr, size_t length, int protection_flag)
{
  size_t addr_as_int = (size_t)(byte*)addr;
  size_t page_size = getpagesize();
  //Address must be multiple of system page size
  size_t aligned_addr_as_int = (addr_as_int / page_size) * page_size;
  size_t additional_length = addr_as_int - aligned_addr_as_int;
  void* aligned_addr = (byte*)addr - additional_length;
  if(mprotect(aligned_addr, length + additional_length, protection_flag))
  {
    return -1;
  }
  else
  {
    return 0;
  }
}

However when I try calling it on my chunk of dynamically allocated memory, I get an error with error code EACCES. Why is this happening, and is there any way to get around it? When f_data_buffer is allocated on the stack, there's no error message (that's the commented out line).

Code calling SetMemoryProtections:

//byte f_data_buffer[16384];//Converts function pointer to byte pointer
byte* f_data_buffer = malloc(sizeof(byte) * getpagesize() * 3);
byte* f_data = f_data_buffer + getpagesize();
if(SetMemoryProtections(f_data, 20, PROT_READ | PROT_WRITE | PROT_EXEC))
{
  printf("SetMemoryProtections encountered error. Error code: ");
  switch(errno)
  {
    case EACCES: printf("EACCES"); break;
    case EINVAL: printf("EINVAL"); break;
    case ENOMEM: printf("ENOMEM"); break;
  }
  printf("\n");
  return -1;
}

Update: I rewrote the code to that it reads errno before calling printf. (It creates a local variable called err, sets err to errno, then calls the switch with err) and I get the same EACCES error code.

Alecto Irene Perez
  • 10,321
  • 23
  • 46
  • I'm not sure about this, but try not using PROT_WRITE and PROT_EXEC at the same time. – zwol Jan 05 '18 at 01:43
  • It works if f_data_buffer is allocated on the stack – Alecto Irene Perez Jan 05 '18 at 01:47
  • *`EACCES`: The memory cannot be given the specified access. This can happen, for example, if you `mmap(2)` a file to which you have read-only access, then ask `mprotect()` to mark it `PROT_WRITE`.* – melpomene Jan 05 '18 at 01:50
  • 2
    Your code doesn't check `errno` correctly. It doesn't examine the value of `errno` immediately after a failing operation; it calls `printf` first, which can overwrite `errno`. Do this instead: `if (...) { fprintf(stderr, "SetMemoryProtection encountered an error (%d): %s\n", errno, strerror(errno)); return -1; }` – melpomene Jan 05 '18 at 01:54
  • @melpomene Please don't tell people to print the numeric value of errno. – zwol Jan 05 '18 at 02:00
  • @zwol Why not? \ – melpomene Jan 05 '18 at 02:01
  • @melpomene They're both architecture and OS specific, so someone comes here and says "errno is 2" and we have no idea what they mean. It's better to tell people to print only `strerror(errno)`. – zwol Jan 05 '18 at 02:15
  • 3
    TL;DR version: Use [`mmap()`](http://man7.org/linux/man-pages/man2/mmap.2.html) with `MAP_ANONYMOUS` to allocate full pages (multiples of `sysconf(_SC_PAGESIZE)`), instead of using `malloc()`. The anonymous pages are freed when the process exits, so if they exist for the duration of the process, you do not need to clean them up. If you want to release the pages, use `munmap()`. Longer version: Small `malloc()`s tend to end up in BSS, and they might overlap with pages mapped from the ELF executable (as `rw-p` in `/proc/self/maps`), so you cannot reliably use `mprotect()` on `malloc()`'d memory. – Nominal Animal Jan 05 '18 at 22:58
  • @zwol *It's better to tell people to print only `strerror(errno)`* And then some non-technical user mangles the output from `strerror()` into something from Harry Potter and you're left without useful data. At least when you also emit the raw `errno` value you can say, "OK, what's the number next to that message?" – Andrew Henle Jan 05 '18 at 23:13
  • @AndrewHenle I say "complete and unedited error message" a lot too, but the raw number is meaningless without a whole bunch of context that will also likely get mangled, and telling people to print it might make them think it's useful in itself. – zwol Jan 05 '18 at 23:24

0 Answers0