/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "berberis/base/mmap.h" #include #include #include #include #include // for old versions of GLIBC only (see below) #include "berberis/base/checks.h" namespace berberis { #if defined(__LP64__) && !defined(__x86_64__) namespace { // arc4random was introduced in GLIBC 2.36 #if defined(__GLIBC__) && ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 36))) uint32_t arc4random_uniform(uint32_t upper_bound) { // Fall back to implementation-defined stl random static std::random_device random_device("/dev/urandom"); static std::mt19937 generator(random_device()); std::uniform_int_distribution distrib(0, upper_bound); return distrib(generator); } #endif void* TryMmap32Bit(MmapImplArgs args) { // Outside of x86_64 mapping in the lower 32bit address space // is achieved by trying to map at the random 32bit address with // hint and then verifying that the resulted map indeed falls in // lower 32bit address space. Note that if another mapping already // exists "the kernel picks a new address that may or may not // depend on the hint." which makes it more difficult. constexpr uintptr_t kMinAddress = 0x10000; // This is always positive hence no sign-extend. constexpr uintptr_t kMaxAddress = std::numeric_limits::max(); // This number is somewhat arbitrary. We want it to be big enough so that it // doesn't fail prematurely when 2G space has lower availability, but not too // big so it doesn't take forever. constexpr size_t kMaxMapAttempts = 512; static std::atomic saved_hint = 0; uintptr_t hint = saved_hint.load(); uintptr_t arc4_random_upper_bound = kMaxAddress - kMinAddress; if (args.size == 0) { return MAP_FAILED; } if (__builtin_usubl_overflow(arc4_random_upper_bound, args.size, &arc4_random_upper_bound)) { return MAP_FAILED; } CHECK_LE(arc4_random_upper_bound, kMaxAddress - kMinAddress); if (hint == 0 || hint > (arc4_random_upper_bound + kMinAddress)) { hint = arc4random_uniform(static_cast(arc4_random_upper_bound)) + kMinAddress; } for (size_t i = 0; i < kMaxMapAttempts; i++) { // PROT_NONE, MAP_NORESERVE to make it faster since this may take several attempts. // We'll do another mmap() with proper flags on top of this one below. void* addr = mmap(reinterpret_cast(hint), args.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, 0, 0); if (addr == MAP_FAILED) { return MAP_FAILED; } uintptr_t start = reinterpret_cast(addr); uintptr_t end = start + args.size; if (end <= kMaxAddress) { saved_hint.store(AlignUpPageSize(end)); // next hint return mmap(addr, args.size, args.prot, MAP_FIXED | args.flags, args.fd, args.offset); } hint = arc4random_uniform(static_cast(arc4_random_upper_bound)) + kMinAddress; } saved_hint.store(0); return MAP_FAILED; } } // namespace #endif // defined(__LP64__) && !defined(__x86_64__) void* MmapImpl(MmapImplArgs args) { if ((args.berberis_flags & kMmapBerberis32Bit) != 0) { // This doesn't make sense for MAP_FIXED CHECK_EQ(args.flags & MAP_FIXED, 0); #if defined(__x86_64__) args.flags |= MAP_32BIT; #elif defined(__LP64__) return TryMmap32Bit(args); #endif } return mmap(args.addr, args.size, args.prot, args.flags, args.fd, args.offset); } void* MmapImplOrDie(MmapImplArgs args) { void* ptr = MmapImpl(args); CHECK_NE(ptr, MAP_FAILED); return ptr; } void MunmapOrDie(void* ptr, size_t size) { int res = munmap(ptr, size); CHECK_EQ(res, 0); } void MprotectOrDie(void* ptr, size_t size, int prot) { int res = mprotect(ptr, size, prot); CHECK_EQ(res, 0); } } // namespace berberis