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