/*++ Copyright (c) Microsoft. All rights reserved. Module Name: mprotect.c Abstract: This file contains test cases for mprotect(). --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define LXT_NAME "mprotect" #define MAPPING_PAGE_SIZE 4096 #define MAX_BUF_SIZE 256 #define PROCFS_MNT "/proc" int MprotectMainVariation(PLXT_ARGS Args); int MunmapMainVariation(PLXT_ARGS Args); int MsyncMainVariation(PLXT_ARGS Args); int MadviseMainVariation(PLXT_ARGS Args); int MremapMainVariation(PLXT_ARGS Args); int MprotectStackVariation(PLXT_ARGS Args); static const LXT_VARIATION g_LxtVariations[] = { {"mprotect main variation", MprotectMainVariation}, {"munmap main variation", MunmapMainVariation}, {"msync main variation", MsyncMainVariation}, {"madvise main variation", MadviseMainVariation}, {"mremap main variation", MremapMainVariation}, {"mprotect stack variation", MprotectStackVariation}, }; int MprotectMainVariation(PLXT_ARGS Args) { int Result; int ROFileDescriptor; int RWFileDescriptor; char FileBuffer[3 * MAPPING_PAGE_SIZE]; char* ROMapping; char* RWMapping; int MapSize; char* RemappedMemory; MapSize = sizeof(FileBuffer); ROFileDescriptor = open("/data/mprotect_test.bin", O_RDONLY | O_CREAT | O_TRUNC, S_IRWXU); if (ROFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } RWFileDescriptor = open("/data/mprotect_test.bin", O_RDWR); if (RWFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } write(RWFileDescriptor, FileBuffer, MapSize); ROMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, ROFileDescriptor, 0); if (ROMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("ROMapping: %p", ROMapping); RWMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, RWFileDescriptor, 0); if (RWMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping: %p", RWMapping); // // Checking behavior of mprotect with zero size mappings. // LxtLogInfo("Checking mprotect behavior with zero size mappings") LxtCheckErrno(mprotect(NULL, 0, PROT_WRITE)); LxtCheckErrno(mprotect(NULL, 0, 0xDEADBEEF)); LxtCheckErrno(mprotect(RWMapping, 0, 0xDEADBEEF)); LxtCheckErrno(mprotect(ROMapping, 0, PROT_WRITE)); LxtCheckErrno(mprotect(ROMapping, 0, PROT_READ)); LxtCheckErrno(mprotect(RWMapping, 0, PROT_WRITE)); LxtCheckErrno(mprotect(RWMapping, 0, PROT_READ)); // // Check behavior of mprotect with a bad address. // LxtLogInfo("Checking mprotect behavior with a bad address") LxtCheckErrnoFailure(mprotect(NULL, MAPPING_PAGE_SIZE, PROT_WRITE), ENOMEM); LxtCheckErrnoFailure(mprotect(RWMapping + 300, MAPPING_PAGE_SIZE, PROT_WRITE), EINVAL); // // Check behavior with bad protection flags. // LxtLogInfo("Checking mprotect behavior with bad protection flags") LxtCheckErrnoFailure(mprotect(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); // // Checking mprotect on non-zero size mappings. // Result = mprotect(RWMapping, MAPPING_PAGE_SIZE, PROT_WRITE); if (Result == -1) { Result = errno; LxtLogError("Protection change failed unexpectedly! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping protection succeeded"); Result = mprotect(ROMapping, MAPPING_PAGE_SIZE, PROT_WRITE); if (Result != -1) { LxtLogError("Protection change on RO file succeeded unexpectedly!"); goto ErrorExit; } Result = errno; if (Result != EACCES) { LxtLogError("RO protection change failed but not with EACCES! %d", Result); goto ErrorExit; } LxtLogInfo("ROMapping protection failed as expected"); RemappedMemory = mremap(ROMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE); if (RemappedMemory == MAP_FAILED) { Result = errno; LxtLogError("Remap on RO file failed unexpectedly! %d!", Result); goto ErrorExit; } LxtLogInfo("Remapping succeeded"); RemappedMemory = mremap(RWMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE); if (RemappedMemory == MAP_FAILED) { Result = errno; LxtLogError("Remap on RO file failed unexpectedly! %d!", Result); goto ErrorExit; } LxtLogInfo("Remapping succeeded"); LxtLogPassed("Success!"); Result = 0; ErrorExit: return Result; } int MunmapMainVariation(PLXT_ARGS Args) { char FileBuffer[3 * MAPPING_PAGE_SIZE]; int MapSize; int Result; int RWFileDescriptor; char* RWMapping; // // Create a file and write garbage data to it. // MapSize = sizeof(FileBuffer); RWFileDescriptor = open("/data/mprotect_test.bin", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); if (RWFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } write(RWFileDescriptor, FileBuffer, MapSize); // // Map a memory segment that will be backed by the file. // RWMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, RWFileDescriptor, 0); if (RWMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping: %p", RWMapping); // // Check different variations of bad arguments with munmap. // LxtLogInfo("Checking munmap with bad arguments"); LxtCheckErrnoFailure(munmap(NULL, 0), EINVAL); LxtCheckErrnoFailure(munmap(RWMapping, 0), EINVAL); LxtCheckErrnoFailure(munmap(RWMapping + 300, MapSize), EINVAL); LxtCheckErrno(munmap(NULL, MapSize)); // // Unmap the memory mapping. // LxtLogInfo("Unmapping the mapping"); LxtCheckErrno(munmap(RWMapping, MapSize)); // // All tests passed at this point. // LxtLogPassed("Success!") Result = 0; ErrorExit: if (RWFileDescriptor >= 0) { if (LxtClose(RWFileDescriptor) < 0) { LxtLogInfo("Failed to close test file at the end of the test"); } } return Result; } int MsyncMainVariation(PLXT_ARGS Args) { char FileBuffer[3 * MAPPING_PAGE_SIZE]; int MapSize; int Result; int RWFileDescriptor; char* RWMapping; // // Create a file and write garbage data to it. // MapSize = sizeof(FileBuffer); RWFileDescriptor = open("/data/mprotect_test.bin", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); if (RWFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } write(RWFileDescriptor, FileBuffer, MapSize); // // Map a memory segment that will be backed by the file. // RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0); if (RWMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping: %p", RWMapping); // // Read the first byte from the file, increment it and write it back. // *RWMapping = *RWMapping + 1; // // Checking behavior of msync with zero length. // LxtLogInfo("Checking msync behavior with zero length") LxtCheckErrno(msync(NULL, 0, MS_SYNC)); LxtCheckErrno(msync(RWMapping, 0, MS_SYNC)); LxtCheckErrnoFailure(msync(NULL, 0, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure(msync(RWMapping, 0, 0xDEADBEEF), EINVAL); // // Check behavior of mprotect with a bad address. // LxtLogInfo("Checking msync behavior with a bad address") LxtCheckErrnoFailure(msync(NULL, MAPPING_PAGE_SIZE, MS_SYNC), ENOMEM); LxtCheckErrnoFailure(msync(RWMapping + 300, MAPPING_PAGE_SIZE, MS_SYNC), EINVAL); // // Check behavior of msync with bad flags. // LxtLogInfo("Checking msync behavior with bad protection flags") LxtCheckErrnoFailure(msync(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); // // Sync the changes. // LxtLogInfo("Sync the changes to the file"); LxtCheckErrno(msync(RWMapping, MAPPING_PAGE_SIZE, MS_SYNC)); // // All tests passed at this point. // LxtLogPassed("Success!") Result = 0; ErrorExit: if (RWFileDescriptor >= 0) { if (LxtClose(RWFileDescriptor) < 0) { LxtLogInfo("Failed to close test file at the end of the test"); } } return Result; } int MadviseMainVariation(PLXT_ARGS Args) { char FileBuffer[3 * MAPPING_PAGE_SIZE]; int MapSize; int Result; int RWFileDescriptor; char* RWMapping; char DummyByte; // // Create a file and write garbage data to it. // MapSize = sizeof(FileBuffer); RWFileDescriptor = open("/data/mprotect_test.bin", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); if (RWFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } write(RWFileDescriptor, FileBuffer, MapSize); // // Map a memory segment that will be backed by the file. // RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0); if (RWMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping: %p", RWMapping); // // Checking behavior of mremap with zero length. // LxtLogInfo("Checking madvise behavior with zero length") LxtCheckErrno(madvise(NULL, 0, MADV_RANDOM)); LxtCheckErrno(madvise(RWMapping, 0, MADV_RANDOM)); LxtCheckErrnoFailure(madvise(NULL, 0, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure(madvise(RWMapping, 0, 0xDEADBEEF), EINVAL); // // Check behavior of madvise with a bad address. // LxtLogInfo("Checking madvise behavior with a bad address") LxtCheckErrnoFailure(madvise(NULL, MAPPING_PAGE_SIZE, MADV_RANDOM), ENOMEM); LxtCheckErrnoFailure(madvise(RWMapping + 300, MAPPING_PAGE_SIZE, MADV_RANDOM), EINVAL); // // Check behavior of madvise with bad flags. // LxtLogInfo("Checking madvise behavior with bad protection flags") LxtCheckErrnoFailure(madvise(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); // // Advise the kernel on access partners. // LxtLogInfo("Advise the kernel on access patterns."); LxtCheckErrno(madvise(RWMapping, MAPPING_PAGE_SIZE, MADV_RANDOM)); // // All tests passed at this point. // LxtLogPassed("Success!") Result = 0; ErrorExit: if (RWFileDescriptor >= 0) { if (LxtClose(RWFileDescriptor) < 0) { LxtLogInfo("Failed to close test file at the end of the test"); } } return Result; } int MremapMainVariation(PLXT_ARGS Args) { char FileBuffer[3 * MAPPING_PAGE_SIZE]; int MapSize; char* RemappedMemory; int Result; int RWFileDescriptor; char* RWMapping; char DummyByte; // // Create a file and write garbage data to it. // MapSize = sizeof(FileBuffer); RWFileDescriptor = open("/data/mprotect_test.bin", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); if (RWFileDescriptor == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } write(RWFileDescriptor, FileBuffer, MapSize); // // Map a memory segment that will be backed by the file. // RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0); if (RWMapping == MAP_FAILED) { Result = errno; LxtLogError("ROMapping allocation failed! %d", Result); goto ErrorExit; } LxtLogInfo("RWMapping: %p", RWMapping); // // Checking behavior of mremap with bad arguments. // LxtLogInfo("Checking mremap behavior with bad arguments"); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" LxtCheckErrnoFailure((int)mremap(NULL, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EFAULT); LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 0, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure((int)mremap(NULL, 0, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EFAULT); LxtCheckErrnoFailure((int)mremap(NULL, 0, 0, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure((int)mremap(NULL, 0, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure((int)mremap(RWMapping, 0, 0, MREMAP_MAYMOVE), EINVAL); LxtCheckErrnoFailure((int)mremap(NULL, 0, 0, MREMAP_MAYMOVE), EINVAL); LxtCheckErrnoFailure((int)mremap(NULL, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL); LxtCheckErrnoFailure((int)mremap(RWMapping + 300, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EINVAL); LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 0, MREMAP_MAYMOVE), EINVAL); #pragma GCC diagnostic pop // // Success cases. // // // Shrink the mapped memory. // RemappedMemory = (char*)mremap(RWMapping, 3 * MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE); if (RemappedMemory == MAP_FAILED) { LxtLogInfo("Mapping Failed"); Result = errno; goto ErrorExit; } LxtLogInfo("RemappedMemory = %p", RemappedMemory); // // All tests passed at this point. // LxtLogPassed("Success!") Result = 0; ErrorExit: if (RWFileDescriptor >= 0) { if (LxtClose(RWFileDescriptor) < 0) { LxtLogInfo("Failed to close test file at the end of the test"); } } return Result; } int MprotectStackVariation(PLXT_ARGS Args) { void* Address; int Result; char StackBuffer[20000]; // // Ensure PROT_GROWSDOWN doesn't work on normal allocations. // Address = mmap(NULL, 4096, PROT_NONE, MAP_ANONYMOUS, -1, 0); LxtCheckErrnoFailure(mprotect(Address, 4096, PROT_READ | PROT_WRITE | PROT_GROWSDOWN), EINVAL); munmap(Address, 4096); // // Make sure PROT_GROWSDOWN works on the stack. There's not an easy // way to validate that it actually worked without parsing /proc/self/maps... // StackBuffer[0] = 'x'; Address = (void*)(((long)StackBuffer + sizeof(StackBuffer)) & ~4095); LxtCheckErrno(mprotect(Address, 4096, PROT_READ | PROT_WRITE | PROT_GROWSDOWN)); Result = 0; ErrorExit: return Result; } int MprotectTestEntry(int Argc, char* Argv[]) { LXT_ARGS Args; int Result; LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return LXT_RESULT_SUCCESS; }