100 lines · c
1//===-- protected_pages.h -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// This file provides protected pages that fault when accessing prior or past9// it. This is useful to check memory functions that must not access outside of10// the provided size limited buffer.11//===----------------------------------------------------------------------===//12 13#ifndef LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H14#define LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H15 16#include "src/__support/macros/properties/os.h" // LIBC_TARGET_OS_IS_LINUX17#if defined(LIBC_FULL_BUILD) || !defined(LIBC_TARGET_OS_IS_LINUX)18#error "Protected pages requires mmap and cannot be used in full build mode."19#endif // defined(LIBC_FULL_BUILD) || !defined(LIBC_TARGET_OS_IS_LINUX)20 21#include "hdr/stdint_proxy.h" // uint8_t22#include "src/__support/macros/attributes.h" // LIBC_INLINE23#include <stddef.h> // size_t24#include <sys/mman.h> // mmap, munmap25#include <unistd.h> // sysconf, _SC_PAGESIZE26 27// Returns mmap page size.28LIBC_INLINE size_t GetPageSize() {29 static const size_t PAGE_SIZE = sysconf(_SC_PAGESIZE);30 return PAGE_SIZE;31}32 33// Represents a page of memory whose access can be configured throught the34// 'WithAccess' function. Accessing data above or below this page will trap as35// it is sandwiched between two pages with no read / write access.36struct Page {37 // Returns an aligned pointer that can be accessed up to page_size. Accessing38 // data at ptr[-1] will fault.39 LIBC_INLINE uint8_t *bottom(size_t size) const {40 if (size >= page_size)41 __builtin_trap();42 return page_ptr;43 }44 // Returns a pointer to a buffer that can be accessed up to size. Accessing45 // data at ptr[size] will trap.46 LIBC_INLINE uint8_t *top(size_t size) const {47 return page_ptr + page_size - size;48 }49 50 // protection is one of PROT_READ / PROT_WRITE.51 LIBC_INLINE Page &WithAccess(int protection) {52 if (mprotect(page_ptr, page_size, protection) != 0)53 __builtin_trap();54 return *this;55 }56 57 const size_t page_size;58 uint8_t *const page_ptr;59};60 61// Allocates 5 consecutive pages that will trap if accessed.62// | page layout | access | page name |63// |-------------|--------|:---------:|64// | 0 | trap | |65// | 1 | custom | A |66// | 2 | trap | |67// | 3 | custom | B |68// | 4 | trap | |69//70// The pages A and B can be retrieved as with 'GetPageA' / 'GetPageB' and their71// accesses can be customized through the 'WithAccess' function.72struct ProtectedPages {73 static constexpr size_t PAGES = 5;74 75 ProtectedPages()76 : page_size(GetPageSize()),77 ptr(mmap(/*address*/ nullptr, /*length*/ PAGES * page_size,78 /*protection*/ PROT_NONE,79 /*flags*/ MAP_PRIVATE | MAP_ANONYMOUS, /*fd*/ -1,80 /*offset*/ 0)) {81 if (reinterpret_cast<intptr_t>(ptr) == -1)82 __builtin_trap();83 }84 ~ProtectedPages() { munmap(ptr, PAGES * page_size); }85 86 LIBC_INLINE Page GetPageA() const { return Page{page_size, page<1>()}; }87 LIBC_INLINE Page GetPageB() const { return Page{page_size, page<3>()}; }88 89private:90 template <size_t index> LIBC_INLINE uint8_t *page() const {91 static_assert(index < PAGES);92 return static_cast<uint8_t *>(ptr) + (index * page_size);93 }94 95 const size_t page_size;96 void *const ptr = nullptr;97};98 99#endif // LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H100