xref: /llvm-project/libc/test/src/string/memory_utils/protected_pages.h (revision 292b300c5131e54b9977305bb4aca9a03e1b4fed)
1*292b300cSGuillaume Chatelet //===-- protected_pages.h -------------------------------------------------===//
2*292b300cSGuillaume Chatelet //
3*292b300cSGuillaume Chatelet // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*292b300cSGuillaume Chatelet // See https://llvm.org/LICENSE.txt for license information.
5*292b300cSGuillaume Chatelet // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*292b300cSGuillaume Chatelet //
7*292b300cSGuillaume Chatelet //===----------------------------------------------------------------------===//
8*292b300cSGuillaume Chatelet // This file provides protected pages that fault when accessing prior or past
9*292b300cSGuillaume Chatelet // it. This is useful to check memory functions that must not access outside of
10*292b300cSGuillaume Chatelet // the provided size limited buffer.
11*292b300cSGuillaume Chatelet //===----------------------------------------------------------------------===//
12*292b300cSGuillaume Chatelet 
13*292b300cSGuillaume Chatelet #ifndef LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H
14*292b300cSGuillaume Chatelet #define LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H
15*292b300cSGuillaume Chatelet 
16*292b300cSGuillaume Chatelet #include "src/__support/macros/properties/os.h" // LIBC_TARGET_OS_IS_LINUX
17*292b300cSGuillaume Chatelet #if defined(LIBC_FULL_BUILD) || !defined(LIBC_TARGET_OS_IS_LINUX)
18*292b300cSGuillaume Chatelet #error "Protected pages requires mmap and cannot be used in full build mode."
19*292b300cSGuillaume Chatelet #endif // defined(LIBC_FULL_BUILD) || !defined(LIBC_TARGET_OS_IS_LINUX)
20*292b300cSGuillaume Chatelet 
21*292b300cSGuillaume Chatelet #include "src/__support/macros/attributes.h" // LIBC_INLINE
22*292b300cSGuillaume Chatelet #include <stddef.h>                          // size_t
23*292b300cSGuillaume Chatelet #include <stdint.h>                          // uint8_t
24*292b300cSGuillaume Chatelet #include <sys/mman.h>                        // mmap, munmap
25*292b300cSGuillaume Chatelet #include <unistd.h>                          // sysconf, _SC_PAGESIZE
26*292b300cSGuillaume Chatelet 
27*292b300cSGuillaume Chatelet // Returns mmap page size.
GetPageSize()28*292b300cSGuillaume Chatelet LIBC_INLINE size_t GetPageSize() {
29*292b300cSGuillaume Chatelet   static const size_t PAGE_SIZE = sysconf(_SC_PAGESIZE);
30*292b300cSGuillaume Chatelet   return PAGE_SIZE;
31*292b300cSGuillaume Chatelet }
32*292b300cSGuillaume Chatelet 
33*292b300cSGuillaume Chatelet // Represents a page of memory whose access can be configured throught the
34*292b300cSGuillaume Chatelet // 'WithAccess' function. Accessing data above or below this page will trap as
35*292b300cSGuillaume Chatelet // it is sandwiched between two pages with no read / write access.
36*292b300cSGuillaume Chatelet struct Page {
37*292b300cSGuillaume Chatelet   // Returns an aligned pointer that can be accessed up to page_size. Accessing
38*292b300cSGuillaume Chatelet   // data at ptr[-1] will fault.
bottomPage39*292b300cSGuillaume Chatelet   LIBC_INLINE uint8_t *bottom(size_t size) const {
40*292b300cSGuillaume Chatelet     if (size >= page_size)
41*292b300cSGuillaume Chatelet       __builtin_trap();
42*292b300cSGuillaume Chatelet     return page_ptr;
43*292b300cSGuillaume Chatelet   }
44*292b300cSGuillaume Chatelet   // Returns a pointer to a buffer that can be accessed up to size. Accessing
45*292b300cSGuillaume Chatelet   // data at ptr[size] will trap.
topPage46*292b300cSGuillaume Chatelet   LIBC_INLINE uint8_t *top(size_t size) const {
47*292b300cSGuillaume Chatelet     return page_ptr + page_size - size;
48*292b300cSGuillaume Chatelet   }
49*292b300cSGuillaume Chatelet 
50*292b300cSGuillaume Chatelet   // protection is one of PROT_READ / PROT_WRITE.
WithAccessPage51*292b300cSGuillaume Chatelet   LIBC_INLINE Page &WithAccess(int protection) {
52*292b300cSGuillaume Chatelet     if (mprotect(page_ptr, page_size, protection) != 0)
53*292b300cSGuillaume Chatelet       __builtin_trap();
54*292b300cSGuillaume Chatelet     return *this;
55*292b300cSGuillaume Chatelet   }
56*292b300cSGuillaume Chatelet 
57*292b300cSGuillaume Chatelet   const size_t page_size;
58*292b300cSGuillaume Chatelet   uint8_t *const page_ptr;
59*292b300cSGuillaume Chatelet };
60*292b300cSGuillaume Chatelet 
61*292b300cSGuillaume Chatelet // Allocates 5 consecutive pages that will trap if accessed.
62*292b300cSGuillaume Chatelet // | page layout | access | page name |
63*292b300cSGuillaume Chatelet // |-------------|--------|:---------:|
64*292b300cSGuillaume Chatelet // | 0           | trap   |           |
65*292b300cSGuillaume Chatelet // | 1           | custom |     A     |
66*292b300cSGuillaume Chatelet // | 2           | trap   |           |
67*292b300cSGuillaume Chatelet // | 3           | custom |     B     |
68*292b300cSGuillaume Chatelet // | 4           | trap   |           |
69*292b300cSGuillaume Chatelet //
70*292b300cSGuillaume Chatelet // The pages A and B can be retrieved as with 'GetPageA' / 'GetPageB' and their
71*292b300cSGuillaume Chatelet // accesses can be customized through the 'WithAccess' function.
72*292b300cSGuillaume Chatelet struct ProtectedPages {
73*292b300cSGuillaume Chatelet   static constexpr size_t PAGES = 5;
74*292b300cSGuillaume Chatelet 
ProtectedPagesProtectedPages75*292b300cSGuillaume Chatelet   ProtectedPages()
76*292b300cSGuillaume Chatelet       : page_size(GetPageSize()),
77*292b300cSGuillaume Chatelet         ptr(mmap(/*address*/ nullptr, /*length*/ PAGES * page_size,
78*292b300cSGuillaume Chatelet                  /*protection*/ PROT_NONE,
79*292b300cSGuillaume Chatelet                  /*flags*/ MAP_PRIVATE | MAP_ANONYMOUS, /*fd*/ -1,
80*292b300cSGuillaume Chatelet                  /*offset*/ 0)) {
81*292b300cSGuillaume Chatelet     if (reinterpret_cast<intptr_t>(ptr) == -1)
82*292b300cSGuillaume Chatelet       __builtin_trap();
83*292b300cSGuillaume Chatelet   }
~ProtectedPagesProtectedPages84*292b300cSGuillaume Chatelet   ~ProtectedPages() { munmap(ptr, PAGES * page_size); }
85*292b300cSGuillaume Chatelet 
GetPageAProtectedPages86*292b300cSGuillaume Chatelet   LIBC_INLINE Page GetPageA() const { return Page{page_size, page<1>()}; }
GetPageBProtectedPages87*292b300cSGuillaume Chatelet   LIBC_INLINE Page GetPageB() const { return Page{page_size, page<3>()}; }
88*292b300cSGuillaume Chatelet 
89*292b300cSGuillaume Chatelet private:
pageProtectedPages90*292b300cSGuillaume Chatelet   template <size_t index> LIBC_INLINE uint8_t *page() const {
91*292b300cSGuillaume Chatelet     static_assert(index < PAGES);
92*292b300cSGuillaume Chatelet     return static_cast<uint8_t *>(ptr) + (index * page_size);
93*292b300cSGuillaume Chatelet   }
94*292b300cSGuillaume Chatelet 
95*292b300cSGuillaume Chatelet   const size_t page_size;
96*292b300cSGuillaume Chatelet   void *const ptr = nullptr;
97*292b300cSGuillaume Chatelet };
98*292b300cSGuillaume Chatelet 
99*292b300cSGuillaume Chatelet #endif // LIBC_TEST_SRC_STRING_MEMORY_UTILS_PROTECTED_PAGES_H
100