clinuxmprotect

Calling mprotect to set the memory on the stack as read-only directly leads to a program SIGSEGV


I want to set the access attribute of an object on the stack to read-only, but as soon as I call mprotect, the process crashes. The approximate code is as follows:

constexpr int kPageSize = 4096;
CHECK_EQ(kPageSize, sysconf(_SC_PAGE_SIZE));
volatile char padding1[kPageSize] = {0};
NetDataHead head;
volatile char padding2[kPageSize] = {0};

// ...
auto align_down_4k_ptr = steel::AlignDownPointer(&head, kPageSize);
CHECK_EQ(reinterpret_cast<std::uint64_t>(align_down_4k_ptr) % kPageSize, 0);
if (auto rc = mprotect(align_down_4k_ptr, kPageSize, PROT_READ); rc != 0) {  // A
  LOG_F("Failed to mprotect ptr: %p, rc: %d", align_down_4k_ptr, rc);
}

// Read-only codes ...

if (auto rc = mprotect(align_down_4k_ptr, kPageSize, PROT_READ | PROT_WRITE); rc != 0) {
  LOG_F("Failed to mprotect ptr: %p, rc: %d", align_down_4k_ptr, rc);
}

The program crashes directly at point A, which is the location of the first mprotect call.

By referring to the manual, it appears that this should not cause a crash in Linux. Does anyone know why this might be happening?

man 2 mprotect

I would like to protect the "head" structure on the stack using mprotect.


Solution

  • The man page says it is permissible, and indeed it appears the kernel makes the stack region read only. Yet the granularity of the memory protection hardware is coarse, typically 4K, 8K or more so other areas of the stack got protected too, such as space used to save registers, which causes a crash as soon as the system call returns.

    Your attempt at isolating the data by defining 2 padding arrays does not work. Contiguous definitions do not guarantee contiguous instantiation. The padding arrays might not get instantiated at all if you do not use them. Making them volatile is irrelevant.

    You should either define a larger structure or use a mmapped memory block for this.