Reputation: 3350
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?
Upvotes: 0
Views: 618
Reputation: 85827
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.
Upvotes: 2