about summary refs log tree commit diff homepage
path: root/include
diff options
context:
space:
mode:
authorDaniel Schemmel <daniel@schemmel.net>2022-10-13 14:24:07 +0100
committerFrank Busse <f.busse@imperial.ac.uk>2023-03-16 11:57:59 +0000
commit44f9772f87b45ca7bef7767e3962e6b61a2e5c4d (patch)
treed84f9b223b8602f3bb9ba3595cefe61ee4d02662 /include
parent140e08ac039959ffc34adcbd8022b3d565f50ee8 (diff)
downloadklee-44f9772f87b45ca7bef7767e3962e6b61a2e5c4d.tar.gz
Add the KDAlloc allocator using both of its suballocators
Diffstat (limited to 'include')
-rw-r--r--include/klee/KDAlloc/allocator.h264
-rw-r--r--include/klee/KDAlloc/kdalloc.h21
-rw-r--r--include/klee/KDAlloc/mapping.h158
3 files changed, 443 insertions, 0 deletions
diff --git a/include/klee/KDAlloc/allocator.h b/include/klee/KDAlloc/allocator.h
new file mode 100644
index 00000000..f59223ed
--- /dev/null
+++ b/include/klee/KDAlloc/allocator.h
@@ -0,0 +1,264 @@
+//===-- allocator.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_ALLOCATOR_H
+#define KDALLOC_ALLOCATOR_H
+
+#include "define.h"
+#include "location_info.h"
+#include "mapping.h"
+#include "suballocators/loh.h"
+#include "suballocators/slot_allocator.h"
+#include "tagged_logger.h"
+
+#include "klee/ADT/Bits.h"
+#include "klee/ADT/Ref.h"
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <ostream>
+#include <type_traits>
+
+namespace klee::kdalloc {
+/// Wraps a mapping that is shared with other allocators.
+class Allocator final : public TaggedLogger<Allocator> {
+public:
+  class Control final {
+    friend class Allocator;
+    friend class AllocatorFactory;
+
+    static constexpr const std::uint32_t unlimitedQuarantine =
+        static_cast<std::uint32_t>(-1);
+
+    /// @todo This should really be a data member `static constexpr const
+    /// std::array meta = { ... }`.
+    static inline const std::array<std::size_t, 8> &getMeta() noexcept {
+      static const std::array<std::size_t, 8> meta = {
+          1u,    // bool
+          4u,    // int
+          8u,    // pointer size
+          16u,   // double
+          32u,   // compound types #1
+          64u,   // compound types #2
+          256u,  // compound types #3
+          2048u, // reasonable buffers
+      };
+      return meta;
+    }
+
+    [[nodiscard]] static inline int
+    convertSizeToBinIndex(std::size_t const size) noexcept {
+      for (std::size_t i = 0; i < getMeta().size(); ++i) {
+        if (getMeta()[i] >= size) {
+          return i;
+        }
+      }
+      return getMeta().size();
+    }
+
+  public:
+    mutable class klee::ReferenceCounter _refCount;
+
+  private:
+    Mapping mapping;
+    std::array<suballocators::SlotAllocator::Control,
+               std::tuple_size<std::decay_t<decltype(getMeta())>>::value>
+        sizedBins;
+    suballocators::LargeObjectAllocator::Control largeObjectBin;
+
+  public:
+    Control() = delete;
+    Control(Control const &) = delete;
+    Control &operator=(Control const &) = delete;
+    Control(Control &&) = delete;
+    Control &operator=(Control &&) = delete;
+
+  private:
+    Control(Mapping &&mapping) : mapping(std::move(mapping)) {}
+  };
+
+  static constexpr const auto unlimitedQuarantine =
+      Control::unlimitedQuarantine;
+
+private:
+  klee::ref<Control> control;
+
+  std::array<suballocators::SlotAllocator,
+             std::tuple_size<std::decay_t<decltype(Control::getMeta())>>::value>
+      sizedBins;
+  suballocators::LargeObjectAllocator largeObjectBin;
+
+public:
+  std::ostream &logTag(std::ostream &out) const noexcept {
+    return out << "[alloc] ";
+  }
+
+  Allocator() = default;
+  Allocator(Allocator const &rhs) = default;
+  Allocator &operator=(Allocator const &rhs) = default;
+  Allocator(Allocator &&) = default;
+  Allocator &operator=(Allocator &&) = default;
+
+  Allocator(klee::ref<Control> control) : control(std::move(control)) {}
+
+  explicit operator bool() const noexcept { return !control.isNull(); }
+
+  auto const &getSizedBinInfo() const noexcept { return Control::getMeta(); }
+
+  [[nodiscard]] void *allocate(std::size_t size) {
+    assert(*this && "Invalid allocator");
+
+    auto const bin = Control::convertSizeToBinIndex(size);
+    traceLine("Allocating ", size, " bytes in bin ", bin);
+
+    void *result = nullptr;
+    if (bin < static_cast<int>(sizedBins.size())) {
+      result = sizedBins[bin].allocate(control->sizedBins[bin]);
+    } else {
+      result = largeObjectBin.allocate(control->largeObjectBin, size);
+    }
+    traceLine("Allocated ", result);
+    return result;
+  }
+
+  void free(void *ptr, std::size_t size) {
+    assert(*this && "Invalid allocator");
+    assert(ptr && "Freeing nullptrs is not supported"); // we are not ::free!
+
+    auto const bin = Control::convertSizeToBinIndex(size);
+    traceLine("Freeing ", ptr, " of size ", size, " in bin ", bin);
+
+    if (bin < static_cast<int>(sizedBins.size())) {
+      return sizedBins[bin].deallocate(control->sizedBins[bin], ptr);
+    } else {
+      return largeObjectBin.deallocate(control->largeObjectBin, ptr, size);
+    }
+  }
+
+  LocationInfo location_info(void const *const ptr,
+                             std::size_t const size) const noexcept {
+    assert(*this && "Invalid allocator");
+
+    if (!ptr || reinterpret_cast<std::uintptr_t>(ptr) < 4096) {
+      return LocationInfo::LI_NullPage;
+    }
+
+    // the following is technically UB if `ptr` does not actually point inside
+    // the mapping at all
+    for (std::size_t i = 0; i < Allocator::Control::getMeta().size(); ++i) {
+      if (control->sizedBins[i].mapping_begin() <= ptr &&
+          ptr < control->sizedBins[i].mapping_end()) {
+        if (reinterpret_cast<char const *>(ptr) + size <=
+            control->sizedBins[i].mapping_end()) {
+          return sizedBins[i].getLocationInfo(control->sizedBins[i], ptr, size);
+        } else {
+          return LocationInfo::LI_SpansSuballocators;
+        }
+      }
+    }
+    if (control->largeObjectBin.mapping_begin() <= ptr &&
+        ptr < control->largeObjectBin.mapping_end()) {
+      if (reinterpret_cast<char const *>(ptr) + size <=
+          control->largeObjectBin.mapping_end()) {
+        return largeObjectBin.getLocationInfo(control->largeObjectBin, ptr,
+                                              size);
+      } else {
+        // the loh is the suballocator using the largest addresses, therefore
+        // its mapping is not followed by that of another suballocator, but
+        // rather the end of the mapping
+        return LocationInfo::LI_NonNullOutsideMapping;
+      }
+    }
+
+    return LocationInfo::LI_NonNullOutsideMapping;
+  }
+};
+
+class AllocatorFactory {
+public:
+  static constexpr const auto unlimitedQuarantine =
+      Allocator::Control::unlimitedQuarantine;
+
+private:
+  klee::ref<Allocator::Control> control;
+
+public:
+  AllocatorFactory() = default;
+
+  AllocatorFactory(std::size_t const size, std::uint32_t const quarantineSize)
+      : AllocatorFactory(Mapping{0, size}, quarantineSize) {}
+
+  AllocatorFactory(std::uintptr_t const address, std::size_t const size,
+                   std::uint32_t const quarantineSize)
+      : AllocatorFactory(Mapping{address, size}, quarantineSize) {}
+
+  AllocatorFactory(Mapping &&mapping, std::uint32_t const quarantineSize) {
+    assert(mapping && "Invalid mapping");
+    assert(mapping.getSize() >
+               Allocator::Control::getMeta().size() * 4096 + 3 * 4096 &&
+           "Mapping is *far* to small");
+
+    control = new Allocator::Control(std::move(mapping));
+    auto const binSize =
+        static_cast<std::size_t>(1)
+        << (std::numeric_limits<std::size_t>::digits - 1 -
+            countLeadingZeroes(control->mapping.getSize() /
+                               (Allocator::Control::getMeta().size() + 1)));
+    char *const base = static_cast<char *>(control->mapping.getBaseAddress());
+    std::size_t totalSize = 0;
+    for (std::size_t i = 0; i < Allocator::Control::getMeta().size(); ++i) {
+      control->sizedBins[i].initialize(
+          base + totalSize, binSize, Allocator::Control::getMeta()[i],
+          quarantineSize == unlimitedQuarantine,
+          quarantineSize == unlimitedQuarantine ? 0 : quarantineSize);
+
+      totalSize += binSize;
+      assert(totalSize <= control->mapping.getSize() && "Mapping too small");
+    }
+
+    auto largeObjectBinSize = control->mapping.getSize() - totalSize;
+    assert(largeObjectBinSize > 0);
+    control->largeObjectBin.initialize(
+        base + totalSize, largeObjectBinSize,
+        quarantineSize == unlimitedQuarantine,
+        quarantineSize == unlimitedQuarantine ? 0 : quarantineSize);
+  }
+
+  explicit operator bool() const noexcept { return !control.isNull(); }
+
+  Mapping &getMapping() noexcept {
+    assert(!!*this && "Cannot get mapping of uninitialized factory.");
+    return control->mapping;
+  }
+
+  Mapping const &getMapping() const noexcept {
+    assert(!!*this && "Cannot get mapping of uninitialized factory.");
+    return control->mapping;
+  }
+
+  Allocator makeAllocator() const {
+    assert(!!*this &&
+           "Can only create an allocator from an initialized factory.");
+
+    return Allocator{control};
+  }
+
+  explicit operator Allocator() && {
+    assert(!!*this &&
+           "Can only create an allocator from an initialized factory.");
+
+    return Allocator{std::move(control)};
+  }
+};
+} // namespace klee::kdalloc
+
+#endif
diff --git a/include/klee/KDAlloc/kdalloc.h b/include/klee/KDAlloc/kdalloc.h
new file mode 100644
index 00000000..c04a4ebf
--- /dev/null
+++ b/include/klee/KDAlloc/kdalloc.h
@@ -0,0 +1,21 @@
+//===-- kdalloc.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_KDALLOC_H
+#define KDALLOC_KDALLOC_H
+
+#include "allocator.h"
+#include "mapping.h"
+
+namespace klee::kdalloc {
+using StackAllocator = Allocator;
+using StackAllocatorFactory = AllocatorFactory;
+} // namespace klee::kdalloc
+
+#endif
diff --git a/include/klee/KDAlloc/mapping.h b/include/klee/KDAlloc/mapping.h
new file mode 100644
index 00000000..4afc9d86
--- /dev/null
+++ b/include/klee/KDAlloc/mapping.h
@@ -0,0 +1,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;
+    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