[libc-commits] [libc] [libc] implement secure random buffer filling with vDSO (PR #109870)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Wed Oct 2 15:25:37 PDT 2024
================
@@ -0,0 +1,363 @@
+//===- Linux implementation of secure random buffer generation --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "src/__support/OSUtil/linux/cprng.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/mutex.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/OSUtil/linux/vdso.h"
+#include "src/__support/block.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/threads/callonce.h"
+#include "src/__support/threads/linux/raw_mutex.h"
+#include "src/sched/sched_getaffinity.h"
+#include "src/sched/sched_getcpucount.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/unistd/sysconf.h"
+
+extern "C" int __cxa_thread_atexit_impl(void (*)(void *), void *, void *);
+extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
+extern "C" [[gnu::weak, gnu::visibility("hidden")]] void *__dso_handle =
+ nullptr;
+
+namespace LIBC_NAMESPACE_DECL {
+namespace cprng {
+namespace {
+
+using namespace vdso;
+// A block of random state together with enough space to hold all its freelist.
+// ┌───────────┬────────────┐
+// │ │ │
+// │ │ Pages ├──────────────┐
+// │ │ │ ▼
+// │ Prev ├────────────┤ ┌──────────────────┐
+// │ │ │ │ │
+// │ │ State0 ├───►│ Opaque Area │
+// │ │ │ │ │
+// │ ├────────────┤ ├──────────────────┤
+// ├───────────┤ │ │ │
+// │ │ State1 ├───►│ Opaque Area │
+// │ │ │ │ │
+// │ ├────────────┤ ├──────────────────┤
+// │ Next │ │ │ │
+// │ │ State2 ├───►│ Opaque Area │
+// │ │ │ │ │
+// │ ├────────────┤ ├──────────────────┤
+// │ │ ...... │ │ .............. │
+// └───────────┴────────────┘ └──────────────────┘
+// State blocks are doubly linked so that we can iterate bidrectionally as a
+// freelist. We'will use a sentinel to simplify the implementation.
+struct StateBlock {
+ StateBlock *prev;
+ StateBlock *next;
+ void *appendix[0];
+ void *&pages() { return appendix[0]; }
+ void *&freelist(size_t index) { return appendix[index + 1]; }
+};
+
+// Parameters from Linux UAPI. Available only for 6.11+. We manually provide it
+// to support a mean of compiling libc on old kernels.
+struct vgetrandom_opaque_params {
+ unsigned size_of_opaque_state = 0;
+ unsigned mmap_prot = 0;
+ unsigned mmap_flags = 0;
+ unsigned reserved[13];
+};
+
+// Global configuration for state allocation.
+// - System Page Size
+// - Number of pages per block (allocation)
+// - Number of states per page
+// - Parameters for state allocation
+// An opaque state cannot go across page boundaries.
+// We use the number of processors available to the current process to estimate
+// the anticipated number of states.
+class GlobalConfig {
+public:
+ const size_t page_size = 0;
+ const size_t pages_per_block = 0;
+ const size_t states_per_page = 0;
+ const vgetrandom_opaque_params params = {};
+
+private:
+ static size_t guess_cpu_count() {
+ cpu_set_t cpuset{};
+ if (LIBC_NAMESPACE::sched_getaffinity(0, sizeof(cpuset), &cpuset))
+ return 1u;
+ int count = LIBC_NAMESPACE::__sched_getcpucount(sizeof(cpu_set_t), &cpuset);
+ return static_cast<size_t>(count > 1 ? count : 1);
+ }
+
+private:
+ constexpr GlobalConfig(size_t page_size, size_t pages_per_block,
+ size_t states_per_page,
+ vgetrandom_opaque_params params)
+ : page_size(page_size), pages_per_block(pages_per_block),
+ states_per_page(states_per_page), params(params) {}
+
+public:
+ static cpp::optional<GlobalConfig> get() {
+ size_t page_size = 0;
+ size_t states_per_page = 0;
+ size_t pages_per_block = 0;
+ vgetrandom_opaque_params params = {};
+
+ // check symbol availability
+ TypedSymbol<VDSOSym::GetRandom> vgetrandom;
+ if (!vgetrandom)
+ return cpp::nullopt;
+
+ // get valid page size
+ long page_size_res = sysconf(_SC_PAGESIZE);
+ if (page_size_res <= 0)
+ return cpp::nullopt;
+ page_size = static_cast<size_t>(page_size_res);
+
+ // get parameters for state allocation
+ if (vgetrandom(nullptr, 0, 0, ¶ms, ~0U))
+ return cpp::nullopt;
+
+ // Compute the number of states per page. On a valid human-constructable
+ // computer as year 2024, the following operations shall not overflow given
+ // the above operations are correctly returned.
+ size_t guessed_bytes = guess_cpu_count() * params.size_of_opaque_state;
+ size_t aligned_bytes = align_up(guessed_bytes, page_size);
+ states_per_page = page_size / params.size_of_opaque_state;
+ pages_per_block = aligned_bytes / page_size;
+ return GlobalConfig(page_size, pages_per_block, states_per_page, params);
+ }
+};
+
+// Utilities to allocate memory and hold it temporarily.
+template <typename T> struct Allocation {
+ T *ptr;
+ size_t raw_size;
+ Allocation(size_t raw_size) : ptr(nullptr), raw_size(raw_size) {
+ AllocChecker ac{};
+ ptr = static_cast<T *>(
+ /* NOLINT(llvmlibc-callee-namespace) */ ::operator new(raw_size, ac));
----------------
SchrodingerZhu wrote:
Using `::operator new` is the way to get uninitialized memory. The object here is dynamically sized.
https://github.com/llvm/llvm-project/pull/109870
More information about the libc-commits
mailing list