about summary refs log tree commit diff homepage
path: root/include/klee/KDAlloc/mapping.h
blob: f566a211bf4f91fad11cbfd277247099e85508e7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//===-- mapping.h -----------------------------------------------*- C++ -*-===//
//
//                     The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef KDALLOC_MAPPING_H
#define KDALLOC_MAPPING_H

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <utility>

#include <sys/mman.h>
#include <unistd.h>

#if defined(__linux__)
#include <linux/version.h>
#endif

#if defined(__APPLE__)
#include <mach/vm_map.h>
#include <sys/types.h>
#endif

#include "klee/Support/ErrorHandling.h"

namespace klee::kdalloc {
class Mapping {
  void *baseAddress = MAP_FAILED;
  std::size_t size = 0;

  bool try_map(std::uintptr_t baseAddress) noexcept {
    assert(this->baseAddress == MAP_FAILED);

    int flags = MAP_ANON | MAP_PRIVATE;
#if defined(__linux__)
    flags |= MAP_NORESERVE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
    if (baseAddress != 0) {
      flags |= MAP_FIXED_NOREPLACE;
    }
#endif
#elif defined(__FreeBSD__)
    if (baseAddress != 0) {
      flags |= MAP_FIXED | MAP_EXCL;
    }
#endif

    auto mappedAddress = ::mmap(reinterpret_cast<void *>(baseAddress), size,
                                PROT_READ | PROT_WRITE, flags, -1, 0);
    if (baseAddress != 0 &&
        baseAddress != reinterpret_cast<std::uintptr_t>(mappedAddress)) {
      [[maybe_unused]] int rc = ::munmap(mappedAddress, size);
      assert(rc == 0 && "munmap failed");
      this->baseAddress = MAP_FAILED;
      return false;
    }
    if (mappedAddress == MAP_FAILED) {
      this->baseAddress = MAP_FAILED;
      return false;
    }
    this->baseAddress = mappedAddress;

#if defined(__linux__)
    {
      [[maybe_unused]] int rc =
          ::madvise(this->baseAddress, size,
                    MADV_NOHUGEPAGE | MADV_DONTFORK | MADV_RANDOM);
      assert(rc == 0 && "madvise failed");
    }
#elif defined(__FreeBSD__)
    {
      [[maybe_unused]] int rc =
          ::minherit(this->baseAddress, size, INHERIT_NONE);
      assert(rc == 0 && "minherit failed");
    }
#elif defined(__APPLE__)
    {
      [[maybe_unused]] int rc =
          ::minherit(this->baseAddress, size, VM_INHERIT_NONE);
      assert(rc == 0 && "minherit failed");
    }
#endif

    return true;
  }

public:
  Mapping() = default;

  explicit Mapping(std::size_t size) noexcept : Mapping(0, size) {}

  Mapping(std::uintptr_t baseAddress, std::size_t size) noexcept : size(size) {
    try_map(baseAddress);
    assert(*this && "failed to allocate mapping");
    if (!*this) {
      std::abort();
    }
  }

  Mapping(Mapping const &) = delete;
  Mapping &operator=(Mapping const &) = delete;

  Mapping(Mapping &&other) noexcept
      : baseAddress(other.baseAddress), size(other.size) {
    other.baseAddress = MAP_FAILED;
    other.size = 0;
  }
  Mapping &operator=(Mapping &&other) noexcept {
    if (&other != this) {
      using std::swap;
      swap(other.baseAddress, baseAddress);
      swap(other.size, size);
    }
    return *this;
  }

  [[nodiscard]] void *getBaseAddress() const noexcept {
    assert(*this && "Invalid mapping");
    return baseAddress;
  }

  [[nodiscard]] std::size_t getSize() const noexcept { return size; }

  void clear() {
    assert(*this && "Invalid mapping");

#if defined(__linux__)
    [[maybe_unused]] int rc = ::madvise(baseAddress, size, MADV_DONTNEED);
    assert(rc == 0 && "madvise failed");
#else
    auto address = reinterpret_cast<std::uintptr_t>(baseAddress);
    [[maybe_unused]] int rc = ::munmap(baseAddress, size);
    assert(rc == 0 && "munmap failed");
    baseAddress = MAP_FAILED;
    [[maybe_unused]] auto success = try_map(address);
    assert(success && "could not recreate the mapping");
#endif
  }

  explicit operator bool() const noexcept { return baseAddress != MAP_FAILED; }

  ~Mapping() {
    if (*this) {
      [[maybe_unused]] int rc = ::munmap(baseAddress, size);
      assert(rc == 0 && "munmap failed");
    }
  }
};
} // namespace klee::kdalloc

#endif