blob: 4cc7670d5b991ba82be6708686b058f8677d1d55 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ledger/lib/coroutine/context/stack.h"
#include <lib/zx/vmar.h>
#include <stdlib.h>
#include "src/ledger/lib/logging/logging.h"
namespace context {
namespace {
size_t ToFullPages(size_t value) { return (value + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)); }
// ASAN doesn't instrument vmo mappings. Use traditional malloc/free when
// running with ASAN.
#if __has_feature(address_sanitizer)
// ASAN doesn't support safe stack.
static_assert(!__has_feature(safe_stack), "Add support for safe stack under ASAN");
void AllocateASAN(size_t stack_size, uintptr_t* stack) {
*stack = reinterpret_cast<uintptr_t>(malloc(stack_size));
LEDGER_DCHECK(*stack);
}
void ReleaseASAN(uintptr_t stack) { free(reinterpret_cast<void*>(stack)); }
#else // __has_feature(address_sanitizer)
constexpr size_t kStackGuardSize = PAGE_SIZE;
#if __has_feature(safe_stack)
constexpr size_t kVmoSizeMultiplier = 2u;
#else
constexpr size_t kVmoSizeMultiplier = 1u;
#endif
size_t VmoSize(size_t stack_size) {
return (kVmoSizeMultiplier * stack_size) + Stack::shadow_call_stack_size();
}
void AllocateStack(const zx::vmo& vmo, size_t vmo_offset, size_t stack_size, zx::vmar* vmar,
uintptr_t* addr) {
uintptr_t allocate_address;
zx_status_t status = zx::vmar::root_self()->allocate(
0, stack_size + 2 * kStackGuardSize,
ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, vmar, &allocate_address);
LEDGER_DCHECK(status == ZX_OK);
status = vmar->map(kStackGuardSize, vmo, vmo_offset, stack_size,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC, addr);
LEDGER_DCHECK(status == ZX_OK);
}
#endif // __has_feature(address_sanitizer)
} // namespace
#if __has_feature(address_sanitizer)
Stack::Stack(size_t stack_size) : stack_size_(ToFullPages(stack_size)) {
LEDGER_DCHECK(stack_size_);
AllocateASAN(stack_size_, &safe_stack_);
#if __has_feature(shadow_call_stack)
AllocateASAN(kShadowCallStackSize, &shadow_call_stack_);
#endif
}
Stack::~Stack() {
ReleaseASAN(safe_stack_);
#if __has_feature(shadow_call_stack)
ReleaseASAN(shadow_call_stack_);
#endif
}
void Stack::Release() {
ReleaseASAN(safe_stack_);
AllocateASAN(stack_size_, &safe_stack_);
#if __has_feature(shadow_call_stack)
ReleaseASAN(shadow_call_stack_);
AllocateASAN(kShadowCallStackSize, &shadow_call_stack_);
#endif
}
#else // __has_feature(address_sanitizer)
Stack::Stack(size_t stack_size) : stack_size_(ToFullPages(stack_size)) {
LEDGER_DCHECK(stack_size_);
zx_status_t status = zx::vmo::create(VmoSize(stack_size_), 0, &vmo_);
LEDGER_DCHECK(status == ZX_OK);
AllocateStack(vmo_, 0, stack_size_, &safe_stack_mapping_, &safe_stack_);
LEDGER_DCHECK(safe_stack_);
#if __has_feature(safe_stack)
AllocateStack(vmo_, stack_size_, stack_size_, &unsafe_stack_mapping_, &unsafe_stack_);
LEDGER_DCHECK(unsafe_stack_);
#endif
#if __has_feature(shadow_call_stack)
AllocateStack(vmo_, stack_size_ * kVmoSizeMultiplier, kShadowCallStackSize,
&shadow_call_stack_mapping_, &shadow_call_stack_);
LEDGER_DCHECK(shadow_call_stack_);
#endif
}
Stack::~Stack() {
safe_stack_mapping_.destroy();
#if __has_feature(safe_stack)
unsafe_stack_mapping_.destroy();
#endif
#if __has_feature(shadow_call_stack)
shadow_call_stack_mapping_.destroy();
#endif
}
void Stack::Release() {
zx_status_t status = vmo_.op_range(ZX_VMO_OP_DECOMMIT, 0, VmoSize(stack_size_), nullptr, 0);
LEDGER_DCHECK(status == ZX_OK);
}
#endif // __has_feature(address_sanitizer)
} // namespace context