| // Copyright 2017 The Crashpad Authors. All rights reserved. |
| // |
| // 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 "util/posix/scoped_mmap.h" |
| |
| #include <unistd.h> |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/numerics/safe_math.h" |
| |
| namespace { |
| |
| bool Munmap(uintptr_t addr, size_t len) { |
| if (munmap(reinterpret_cast<void*>(addr), len) != 0) { |
| PLOG(ERROR) << "munmap"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| size_t RoundPage(size_t size) { |
| const size_t kPageMask = base::checked_cast<size_t>(getpagesize()) - 1; |
| return (size + kPageMask) & ~kPageMask; |
| } |
| |
| } // namespace |
| |
| namespace crashpad { |
| |
| ScopedMmap::ScopedMmap() {} |
| |
| ScopedMmap::~ScopedMmap() { |
| if (is_valid()) { |
| Munmap(reinterpret_cast<uintptr_t>(addr_), RoundPage(len_)); |
| } |
| } |
| |
| bool ScopedMmap::Reset() { |
| return ResetAddrLen(MAP_FAILED, 0); |
| } |
| |
| bool ScopedMmap::ResetAddrLen(void* addr, size_t len) { |
| const uintptr_t new_addr = reinterpret_cast<uintptr_t>(addr); |
| const size_t new_len_round = RoundPage(len); |
| |
| if (addr == MAP_FAILED) { |
| DCHECK_EQ(len, 0u); |
| } else { |
| DCHECK_NE(len, 0u); |
| DCHECK_EQ(new_addr % getpagesize(), 0u); |
| DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (new_len_round - 1)) |
| .IsValid()); |
| } |
| |
| bool result = true; |
| |
| if (is_valid()) { |
| const uintptr_t old_addr = reinterpret_cast<uintptr_t>(addr_); |
| const size_t old_len_round = RoundPage(len_); |
| if (old_addr < new_addr) { |
| result &= Munmap(old_addr, std::min(old_len_round, new_addr - old_addr)); |
| } |
| if (old_addr + old_len_round > new_addr + new_len_round) { |
| uintptr_t unmap_start = std::max(old_addr, new_addr + new_len_round); |
| result &= Munmap(unmap_start, old_addr + old_len_round - unmap_start); |
| } |
| } |
| |
| addr_ = addr; |
| len_ = len; |
| |
| return result; |
| } |
| |
| bool ScopedMmap::ResetMmap(void* addr, |
| size_t len, |
| int prot, |
| int flags, |
| int fd, |
| off_t offset) { |
| // Reset() first, so that a new anonymous mapping can use the address space |
| // occupied by the old mapping if appropriate. The new mapping will be |
| // attempted even if there was something wrong with the old mapping, so don’t |
| // consider the return value from Reset(). |
| Reset(); |
| |
| void* new_addr = mmap(addr, len, prot, flags, fd, offset); |
| if (new_addr == MAP_FAILED) { |
| PLOG(ERROR) << "mmap"; |
| return false; |
| } |
| |
| // The new mapping is effective even if there was something wrong with the old |
| // mapping, so don’t consider the return value from ResetAddrLen(). |
| ResetAddrLen(new_addr, len); |
| return true; |
| } |
| |
| bool ScopedMmap::Mprotect(int prot) { |
| if (mprotect(addr_, RoundPage(len_), prot) < 0) { |
| PLOG(ERROR) << "mprotect"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace crashpad |