Anonymous Entity Anonymous Entity - 3 months ago 11
C++ Question

mprotect fails after some number of calls

I am trying to write a program where I allocate a block of memory, and then selectively change the protection of page-sized and page-aligned subsets of the block. But when I try to call

mprotect
on parts of the memory that are > 8 pages into the block,
mprotect
fails with the error "Cannot allocate memory".

Here is a minimal, complete, and verifiable example that reproduces the problem:

#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <cerrno>
#include <cstring>
#include <iostream>

int main() {
const int n_pages = 12;
const int page_size = sysconf(_SC_PAGE_SIZE); // 4096
const int block_size = n_pages * page_size; // 65536

char* block_addr = (char*)aligned_alloc(page_size, block_size);
char* chunks[n_pages];
char* pointer = block_addr;

for (int n = 0; n < n_pages; n++) {
pointer = pointer + (n * page_size);
chunks[n] = pointer;
}
std::cout << "Set chunks read-only.\n";
for (int n = 0; n < n_pages; n++) {
if (mprotect(chunks[n], page_size, PROT_READ) != 0) {
std::cerr << n+1 << '/' << n_pages << ' '
<< "mprotect failed: " << std::strerror(errno) << '\n';
}
}
std::cout << "Set chunks read/write.\n";
for (int n = 0; n < n_pages; n++) {
if (mprotect(chunks[n], page_size, PROT_READ|PROT_WRITE) != 0) {
std::cerr << n+1 << '/' << n_pages << ' '
<< "mprotect failed: " << std::strerror(errno) << '\n';
}
}
free(block_addr);
}


This consistently fails for chunks n>8, giving the following message:

Set chunks read-only.
9/12 mprotect failed: Cannot allocate memory
10/12 mprotect failed: Cannot allocate memory
11/12 mprotect failed: Cannot allocate memory
12/12 mprotect failed: Cannot allocate memory
Set chunks read/write.
9/12 mprotect failed: Cannot allocate memory
10/12 mprotect failed: Cannot allocate memory
11/12 mprotect failed: Cannot allocate memory
12/12 mprotect failed: Cannot allocate memory


I found a question in which the OP seems to get the same error as I, where David Hammen usefully provides some hints to the source of the problem, but I don't really understand what he is talking about. Unfortunately OP doesn't provide their code so we can't know exactly what they were doing or how they fixed it.

So basically my question is: Why does
mprotect
produce that error, and how can I fix it?

Answer
    pointer = pointer + (n * page_size);

This line looks very suspicious. It should probably be either

    pointer = block_addr + (n * page_size);

or

    chunks[n] = pointer;  // need to assign to chunks[n] first
    pointer = pointer + page_size;

Otherwise pointer will walk off (in steps of 0, 1 (= 0 + 1), 3 (= 0 + 1 + 2), 6 (= 0 + 1 + 2 + 3), ...) into memory that doesn't belong to your process because you didn't allocate it (i.e. it's "unmapped"). This would explain why mprotect fails for the last couple of chunks.

Comments