blob: 5d7cfc1e2cab703ba4f3ea3f564f81c7597874af [file] [log] [blame]
// Copyright 2012 Google Inc. 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.
//
// Implements HeapProxy, a class that wraps Win32 heap allocations but adds
// heap/tail redzones.
#ifndef SYZYGY_AGENT_ASAN_ASAN_HEAP_H_
#define SYZYGY_AGENT_ASAN_ASAN_HEAP_H_
#include <windows.h> // NOLINT
#include "base/debug/stack_trace.h"
#include "base/synchronization/lock.h"
#include "syzygy/agent/common/dlist.h"
namespace agent {
namespace asan {
// Makes like a Win32 heap manager heap, but adds a redzone before and after
// each allocation and maintains a quarantine list of freed blocks.
class HeapProxy {
public:
HeapProxy();
~HeapProxy();
// @name Cast to/from HANDLE.
// @{
static HANDLE ToHandle(HeapProxy* proxy);
static HeapProxy* FromHandle(HANDLE heap);
// @}
// @name Heap interface.
// @{
bool Create(DWORD options,
size_t initial_size,
size_t maximum_size);
bool Destroy();
void* Alloc(DWORD flags, size_t bytes);
void* ReAlloc(DWORD flags, void* mem, size_t bytes);
bool Free(DWORD flags, void* mem);
size_t Size(DWORD flags, const void* mem);
bool Validate(DWORD flags, const void* mem);
size_t Compact(DWORD flags);
bool Lock();
bool Unlock();
bool Walk(PROCESS_HEAP_ENTRY* entry);
bool SetInformation(HEAP_INFORMATION_CLASS info_class,
void* info,
size_t info_length);
bool QueryInformation(HEAP_INFORMATION_CLASS info_class,
void* info,
size_t info_length,
unsigned long* return_length);
// @}
// Report a bad access to the heap.
// @param addr The red-zoned address causing a bad access.
// @returns true if the address belongs to a memory block, false otherwise.
bool OnBadAccess(const void* addr);
// Report an unknown error while attempting the red-zoned heap address @addr.
static void ReportUnknownError(const void* addr);
// @name Cast to/from HANDLE.
// @{
static LIST_ENTRY* ToListEntry(HeapProxy* proxy);
static HeapProxy* FromListEntry(LIST_ENTRY* list_entry);
// @}
protected:
// Enumeration of the different kind of bad heap access that we can encounter.
enum BadAccessKind {
UNKNOWN_BAD_ACCESS,
USE_AFTER_FREE,
HEAP_BUFFER_OVERFLOW,
HEAP_BUFFER_UNDERFLOW,
};
enum BlockState {
ALLOCATED,
FREED,
QUARANTINED,
// This enum value should always be last.
MAX_STATE,
};
// Every allocated block starts with a BlockHeader.
struct BlockHeader {
size_t magic_number;
size_t size;
BlockState state;
void* alloc_stack_trace;
void* free_stack_trace;
uint8 alloc_stack_trace_size;
uint8 free_stack_trace_size;
};
// Returns the block header for an alloc.
BlockHeader* ToBlock(const void* alloc);
// Returns alloc for a block.
uint8* ToAlloc(BlockHeader* block);
// Find the memory block containing @p addr.
// @returns a pointer to this memory block in case of success, NULL otherwise.
BlockHeader* FindAddressBlock(const void* addr);
// Give the type of a bad heap access corresponding to an address.
// @param addr The address causing a bad heap access.
// @param header The header of the block containing this address.
BadAccessKind GetBadAccessKind(const void* addr, BlockHeader* header);
private:
// Magic number to identify the beginning of a block header.
static const size_t kBlockHeaderSignature = 0x03CA80E7;
// Free blocks are linked together.
struct FreeBlockHeader : public BlockHeader {
FreeBlockHeader* next;
};
// Returns a string describing a bad access kind.
static char* AccessTypeToStr(BadAccessKind bad_access_kind);
// Quarantines @p block and flushes quarantine overage.
void QuarantineBlock(BlockHeader* block);
// Calculates the underlying allocation size for a requested
// allocation of @p bytes.
static size_t GetAllocSize(size_t bytes);
// Print the information about an address belonging to a memory block. This
// function will print the relative position of this address inside a block
// and the bounds of this block.
// @param addr The address for which we want information.
// @param header The block containing the address.
// @param bad_access_kind The kind of bad access corresponding to this
// address.
void PrintAddressInformation(const void* addr,
BlockHeader* header,
BadAccessKind bad_access_kind);
// Report a basic Asan error to stderr. This function just dump the stack
// without providing information relative to the shadow memory.
// @param bug_descr The description of the error.
// @param addr The address causing an error.
// @param bad_access_kind The kind of error.
static void ReportAsanErrorBase(const char* bug_descr,
const void* addr,
BadAccessKind bad_access_kind);
// Report an Asan error to stderr with information about the address causing
// this error.
// @param bug_descr The description of the error.
// @param addr The address causing an error.
// @param bad_access_kind The kind of error.
// @param header The header of the block containing this address.
void ReportAsanError(const char* bug_descr,
const void* addr,
BadAccessKind bad_access_kind,
BlockHeader* header);
// Contains the underlying heap we delegate to.
HANDLE heap_;
base::Lock lock_;
// Points to the head of the quarantine queue.
FreeBlockHeader* head_; // Under lock_.
// Points to the tail of the quarantine queue.
FreeBlockHeader* tail_; // Under lock_.
// Total size of blocks in quarantine.
size_t quarantine_size_; // Under lock_.
// The entry linking to us.
LIST_ENTRY list_entry_;
};
} // namespace asan
} // namespace agent
#endif // SYZYGY_AGENT_ASAN_ASAN_HEAP_H_