blob: 3416b0d93039b171ffbd68641521d67269392148 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2010-2014 Google, Inc. All rights reserved.
* Copyright (c) 2003-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/*
* os_shared.h - shared declarations for os facilities from unix/win32
*/
#ifndef OS_SHARED_H
#define OS_SHARED_H
enum {VM_ALLOCATION_BOUNDARY = 64*1024}; /* 64KB allocation size for Windows */
struct _local_state_t; /* in arch_exports.h */
/* in os.c */
void os_init(void);
/* called on detach and on process exit in debug builds */
void os_slow_exit(void);
/* called on detach and on process exit in both debug and release builds */
void os_fast_exit(void);
void os_tls_init(void);
/* Frees local_state. If the calling thread is exiting (i.e.,
* !other_thread) then also frees kernel resources for the calling
* thread; if other_thread then that may not be possible.
*/
void os_tls_exit(struct _local_state_t *local_state, bool other_thread);
void os_thread_init(dcontext_t *dcontext);
void os_thread_exit(dcontext_t *dcontext, bool other_thread);
/* must only be called for the executing thread */
void os_thread_under_dynamo(dcontext_t *dcontext);
/* must only be called for the executing thread */
void os_thread_not_under_dynamo(dcontext_t *dcontext);
bool os_take_over_all_unknown_threads(dcontext_t *dcontext);
void os_heap_init(void);
void os_heap_exit(void);
/* os provided heap routines */
/* caller is required to handle thread synchronization and to update dynamo vm areas.
* size must be PAGE_SIZE-aligned.
* returns NULL if fails to allocate memory!
* N.B.: only heap.c should call these routines, as it contains the logic to
* handle memory allocation failure!
*
* error_code is set to 0 on success
* on failure it is set to one of the other enum values below or an OS specific status
* code used only for reporting
*/
enum {
HEAP_ERROR_SUCCESS = 0,
/* os_heap_reserve_in_region() only, couldn't find a place to reserve within region */
HEAP_ERROR_CANT_RESERVE_IN_REGION = 1,
/* os_heap_reserve() only, Linux only, mmap failed to reserve at preferred address */
HEAP_ERROR_NOT_AT_PREFERRED = 2,
};
typedef uint heap_error_code_t;
/* flag values for os_raw_mem_alloc */
enum {
#ifdef WINDOWS
/* default is reserve+commit */
RAW_ALLOC_RESERVE_ONLY = 0x0001,
RAW_ALLOC_COMMIT_ONLY = 0x0002,
#endif
#ifdef UNIX
RAW_ALLOC_32BIT = 0x0004,
#endif
};
/* For dr_raw_mem_alloc, try to allocate memory at preferred address. */
void *os_raw_mem_alloc(void *preferred, size_t size, uint prot, uint flags,
heap_error_code_t *error_code);
/* For dr_raw_mem_free, free memory allocated from os_raw_mem_alloc */
bool os_raw_mem_free(void *p, size_t size, uint flags, heap_error_code_t *error_code);
/* Reserve size bytes of virtual address space in one piece without committing swap
* space for it. If preferred is non-NULL then memory will be reserved at that address
* only (if size bytes are unavailable at preferred then the allocation will fail).
* The executable flag is for platforms that divide executable from data memory.
*/
void *os_heap_reserve(void *preferred, size_t size, heap_error_code_t *error_code,
bool executable);
/* Reserve size bytes of virtual address space in one piece entirely within the
* address range specified without committing swap space for it.
* The executable flag is for platforms that divide executable from data memory.
*/
void *os_heap_reserve_in_region(void *start, void *end, size_t size,
heap_error_code_t *error_code, bool executable);
/* commit previously reserved pages, returns false when out of memory */
bool os_heap_commit(void *p, size_t size, uint prot, heap_error_code_t *error_code);
/* decommit previously committed page, so it is reserved for future reuse */
void os_heap_decommit(void *p, size_t size, heap_error_code_t *error_code);
/* frees size bytes starting at address p (note - on windows the entire allocation
* containing p is freed and size is ignored) */
void os_heap_free(void *p, size_t size, heap_error_code_t *error_code);
/* prognosticate whether systemwide memory pressure based on
* last_error_code and systemwide omens
*
* Note it doesn't answer if the hog is current process, nor whether
* it is our own fault in current process.
*/
bool os_heap_systemwide_overcommit(heap_error_code_t last_error_code);
bool os_heap_get_commit_limit(size_t *commit_used, size_t *commit_limit);
thread_id_t get_thread_id(void);
process_id_t get_process_id(void);
void os_thread_yield(void);
void os_thread_sleep(uint64 milliseconds);
bool os_thread_suspend(thread_record_t *tr);
bool os_thread_resume(thread_record_t *tr);
bool os_thread_terminate(thread_record_t *tr);
bool is_thread_currently_native(thread_record_t *tr);
/* If state beyond that in our priv_mcontext_t is needed, os-specific routines
* must be used. These only deal with priv_mcontext_t state.
*/
bool thread_get_mcontext(thread_record_t *tr, priv_mcontext_t *mc);
bool thread_set_mcontext(thread_record_t *tr, priv_mcontext_t *mc);
/* Takes an os-specific context. Does not return. */
void thread_set_self_context(void *cxt);
/* Only sets the priv_mcontext_t state. Does not return. */
void thread_set_self_mcontext(priv_mcontext_t *mc);
/* Assumes target thread is suspended */
bool
os_thread_take_over_suspended_native(dcontext_t *dcontext);
dcontext_t *get_thread_private_dcontext(void);
void set_thread_private_dcontext(dcontext_t *dcontext);
/* converts a local_state_t offset to a segment offset */
ushort os_tls_offset(ushort tls_offs);
struct _local_state_t; /* in arch_exports.h */
struct _local_state_extended_t; /* in arch_exports.h */
struct _local_state_t *get_local_state(void);
struct _local_state_extended_t *get_local_state_extended(void);
/* Returns POINTER_MAX on failure.
* On X86, we assume that cs, ss, ds, and es are flat.
* On ARM, there is no segment. We use it to get TLS base instead.
*/
byte *
get_segment_base(uint seg);
byte *
get_app_segment_base(uint seg);
#ifdef CLIENT_INTERFACE
/* Allocates num_slots tls slots aligned with alignment align */
bool
os_tls_calloc(OUT uint *offset, uint num_slots, uint alignment);
bool
os_tls_cfree(uint offset, uint num_slots);
#endif
/* DR_API EXPORT TOFILE dr_tools.h */
/* DR_API EXPORT BEGIN */
/**************************************************
* STATE SWAPPING TYPES
*/
/**
* Flags that control the behavior of dr_switch_to_app_state_ex()
* and dr_switch_to_dr_state_ex().
*/
typedef enum {
#ifdef WINDOWS
DR_STATE_PEB = 0x0001, /**< Switch the PEB pointer. */
DR_STATE_TEB_MISC = 0x0002, /**< Switch miscellaneous TEB fields. */
DR_STATE_STACK_BOUNDS = 0x0004, /**< Switch the TEB stack bounds fields. */
#endif
DR_STATE_ALL = ~0, /**< Switch all state. */
} dr_state_flags_t;
/* DR_API EXPORT END */
bool
os_should_swap_state(void);
bool
os_using_app_state(dcontext_t *dcontext);
void
os_swap_context(dcontext_t *dcontext, bool to_app, dr_state_flags_t flags);
bool pre_system_call(dcontext_t *dcontext);
void post_system_call(dcontext_t *dcontext);
int os_normalized_sysnum(int num_raw, instr_t *gateway, dcontext_t *dcontext_live);
char *get_application_pid(void);
char *get_application_name(void);
const char *get_application_short_name(void);
char *get_computer_name(void); /* implemented on win32 only, in eventlog.c */
app_pc
get_application_base(void);
app_pc
get_application_end(void);
int get_num_processors(void);
/* Terminate types - the best choice here is TERMINATE_PROCESS, no cleanup*/
typedef enum {
TERMINATE_PROCESS = 0x1,
TERMINATE_THREAD = 0x2, /* terminate thread (and process if last) */
/* Use of TERMINATE_THREAD mode is dangerous,
and can result in the following problems (see MSDN):
* If the target thread owns a critical section,
the critical section will not be released.
* If the target thread is allocating memory from the heap,
the heap lock will not be released.
* If the target thread is executing certain kernel32 calls when it is terminated,
the kernel32 state for the thread's process could be inconsistent.
* If the target thread is manipulating the global state of a shared DLL,
the state of the DLL could be destroyed, affecting other users of the DLL.
*/
TERMINATE_CLEANUP = 0x4 /* cleanup our state before issuing terminal syscall */
} terminate_flags_t;
void os_terminate(dcontext_t *dcontext, terminate_flags_t flags);
void os_terminate_with_code(dcontext_t *dcontext, terminate_flags_t flags, int exit_code);
typedef enum {
ILLEGAL_INSTRUCTION_EXCEPTION,
UNREADABLE_MEMORY_EXECUTION_EXCEPTION,
IN_PAGE_ERROR_EXCEPTION,
} dr_exception_type_t;
void os_forge_exception(app_pc exception_address, dr_exception_type_t type);
/* events for dumpcore_mask */
/* NOTE with DUMPCORE_DEADLOCK and DUMPCORE_ASSERT you will get 2 dumps for
* rank order violations in debug builds */
enum {
DUMPCORE_INTERNAL_EXCEPTION = 0x0001,
DUMPCORE_SECURITY_VIOLATION = 0x0002,
DUMPCORE_DEADLOCK = 0x0004,
DUMPCORE_ASSERTION = 0x0008,
DUMPCORE_FATAL_USAGE_ERROR = 0x0010,
#ifdef CLIENT_INTERFACE
DUMPCORE_CLIENT_EXCEPTION = 0x0020,
#endif
DUMPCORE_TIMEOUT = 0x0040,
DUMPCORE_CURIOSITY = 0x0080,
#ifdef HOT_PATCHING_INTERFACE /* Part of fix for 5357 & 5988. */
/* Errors and exceptions in hot patches are treated the same. Case 5696. */
DUMPCORE_HOTP_FAILURE = 0x0100,
#endif
DUMPCORE_OUT_OF_MEM = 0x0200,
DUMPCORE_OUT_OF_MEM_SILENT = 0x0400, /* not on by default in DEBUG */
#ifdef UNIX
DUMPCORE_INCLUDE_STACKDUMP = 0x0800,
DUMPCORE_WAIT_FOR_DEBUGGER = 0x1000, /* not on by default in DEBUG */
#endif
#ifdef HOT_PATCHING_INTERFACE /* Part of fix for 5988. */
DUMPCORE_HOTP_DETECTION = 0x2000, /* not on by default in DEBUG */
DUMPCORE_HOTP_PROTECTION = 0x4000, /* not on by default in DEBUG */
#endif
#ifdef CLIENT_INTERFACE
DUMPCORE_DR_ABORT = 0x8000,
#endif
/* all exception cases here are off by default since we expect them to
* usually be the app's fault (or normal behavior for the app) */
DUMPCORE_FORGE_ILLEGAL_INST = 0x10000,
DUMPCORE_FORGE_UNREAD_EXEC = 0x20000, /* not including -throw_exception */
/* Note this is ALL app exceptions (including those the app may expect and
* handle without issue) except those created via RaiseException (note
* dr forged exceptions use the eqv. of RaiseException). Would be nice to
* have a flag for just unhandled app exceptions but that's harder to
* implement. */
DUMPCORE_APP_EXCEPTION = 0x40000,
DUMPCORE_TRY_EXCEPT = 0x80000, /* even when we do have a handler */
DUMPCORE_UNSUPPORTED_APP = 0x100000,
#ifdef UNIX
DUMPCORE_OPTION_PAUSE = DUMPCORE_WAIT_FOR_DEBUGGER |
DUMPCORE_INTERNAL_EXCEPTION |
DUMPCORE_SECURITY_VIOLATION |
DUMPCORE_DEADLOCK |
DUMPCORE_ASSERTION |
DUMPCORE_FATAL_USAGE_ERROR |
# ifdef CLIENT_INTERFACE
DUMPCORE_CLIENT_EXCEPTION |
# endif
DUMPCORE_UNSUPPORTED_APP |
DUMPCORE_TIMEOUT |
DUMPCORE_CURIOSITY |
# ifdef CLIENT_INTERFACE
DUMPCORE_DR_ABORT |
# endif
DUMPCORE_OUT_OF_MEM |
DUMPCORE_OUT_OF_MEM_SILENT,
#endif
};
void os_dump_core(const char *msg);
int os_timeout(int time_in_milliseconds);
void os_syslog(syslog_event_type_t priority, uint message_id,
uint substitutions_num, va_list args);
/* DR_API EXPORT TOFILE dr_tools.h */
/* DR_API EXPORT BEGIN */
/**************************************************
* CLIENT AUXILIARY LIBRARY TYPES
*/
#if defined(CLIENT_INTERFACE) || defined(HOT_PATCHING_INTERFACE)
/**
* A handle to a loaded client auxiliary library. This is a different
* type than module_handle_t and is not necessarily the base address.
*/
typedef void * dr_auxlib_handle_t;
/** An exported routine in a loaded client auxiliary library. */
typedef void (*dr_auxlib_routine_ptr_t)();
#if defined(WINDOWS) && !defined(X64)
/**
* A handle to a loaded 64-bit client auxiliary library. This is a different
* type than module_handle_t and is not necessarily the base address.
*/
typedef uint64 dr_auxlib64_handle_t;
/** An exported routine in a loaded 64-bit client auxiliary library. */
typedef uint64 dr_auxlib64_routine_ptr_t;
#endif
/* DR_API EXPORT END */
/* Note that this is NOT identical to module_handle_t: on Linux this
* is a pointer to a loader data structure and NOT the base address
* (xref PR 366195).
* XXX: we're duplicating these types above as dr_auxlib*
*/
typedef void * shlib_handle_t;
typedef void (*shlib_routine_ptr_t)();
shlib_handle_t load_shared_library(const char *name, bool reachable);
#elif defined(WINDOWS) && !defined(X64)
/* Used for non-CLIENT_INTERFACE as well */
typedef uint64 dr_auxlib64_handle_t;
typedef uint64 dr_auxlib64_routine_ptr_t;
#endif
#if defined(CLIENT_INTERFACE)
shlib_routine_ptr_t lookup_library_routine(shlib_handle_t lib, const char *name);
void unload_shared_library(shlib_handle_t lib);
void shared_library_error(char *buf, int maxlen);
/* addr is any pointer known to lie within the library
* for linux, one of addr or name is needed; for windows, neither is needed.
*/
bool
shared_library_bounds(IN shlib_handle_t lib, IN byte *addr,
IN const char *name,
OUT byte **start, OUT byte **end);
#endif
char *get_dynamorio_library_path(void);
/* DR_API EXPORT TOFILE dr_tools.h */
/* DR_API EXPORT BEGIN */
/**************************************************
* MEMORY INFORMATION TYPES
*/
#define DR_MEMPROT_NONE 0x00 /**< No read, write, or execute privileges. */
#define DR_MEMPROT_READ 0x01 /**< Read privileges. */
#define DR_MEMPROT_WRITE 0x02 /**< Write privileges. */
#define DR_MEMPROT_EXEC 0x04 /**< Execute privileges. */
#ifdef WINDOWS
# define DR_MEMPROT_GUARD 0x08 /**< Guard page (Windows only) */
#endif
/**
* DR's default cache consistency strategy modifies the page protection of
* pages containing code, making them read-only. It pretends on application
* and client queries that the page is writable. On a write fault
* to such a region by the application or by client-added instrumentation, DR
* automatically handles the fault and makes the page writable. This requires
* flushing the code from the code cache, which can only be done safely when in
* an application context. Thus, a client writing to such a page is only
* supported when these criteria are met:
*
* -# The client code must be in an application code cache context. This rules
* out all event callbacks (including the basic block event) except for the
* pre and post system call events and the nudge event.
* -# The client must not hold any locks. An exception is a lock marked as
* an application lock (via dr_mutex_mark_as_app(), dr_rwlock_mark_as_app(),
* or dr_recurlock_mark_as_app()).
* -# The client code must not rely on returning to a particular point in the
* code cache, as that point might be flushed and removed during the write
* fault processing. This rules out a clean call (unless
* dr_redirect_execution() is used), but does allow something like
* drwrap_replace_native() which uses a continuation strategy.
*
* A client write fault that does not meet the first two criteria will result in
* a fatal error report and an abort. It is up to the client to ensure it
* satisifies the third criterion.
*
* Even when client writes do meet these criteria, for performance it's best for
* clients to avoid writing to such memory.
*/
#define DR_MEMPROT_PRETEND_WRITE 0x10
/**
* Flags describing memory used by dr_query_memory_ex().
*/
typedef enum {
DR_MEMTYPE_FREE, /**< No memory is allocated here */
DR_MEMTYPE_IMAGE, /**< An executable file is mapped here */
DR_MEMTYPE_DATA, /**< Some other data is allocated here */
DR_MEMTYPE_RESERVED, /**< Reserved address space with no physical storage */
DR_MEMTYPE_ERROR, /**< Query failed for unspecified reason */
/**
* Query failed due to the address being located in Windows kernel space.
* No further information is available so iteration must stop.
*/
DR_MEMTYPE_ERROR_WINKERNEL,
} dr_mem_type_t;
/**
* Describes a memory region. Used by dr_query_memory_ex().
*/
typedef struct _dr_mem_info_t {
/** Starting address of memory region */
byte *base_pc;
/** Size of region */
size_t size;
/** Protection of region (DR_MEMPROT_* flags) */
uint prot;
/** Type of region */
dr_mem_type_t type;
} dr_mem_info_t;
/* DR_API EXPORT END */
#define MEMPROT_NONE DR_MEMPROT_NONE
#define MEMPROT_READ DR_MEMPROT_READ
#define MEMPROT_WRITE DR_MEMPROT_WRITE
#define MEMPROT_EXEC DR_MEMPROT_EXEC
#ifdef WINDOWS
# define MEMPROT_GUARD DR_MEMPROT_GUARD
#endif
bool get_memory_info(const byte *pc, byte **base_pc, size_t *size, uint *prot);
bool query_memory_ex(const byte *pc, OUT dr_mem_info_t *info);
/* We provide this b/c getting the bounds is expensive on Windows (i#1462) */
bool query_memory_cur_base(const byte *pc, OUT dr_mem_info_t *info);
#ifdef UNIX
bool get_memory_info_from_os(const byte *pc, byte **base_pc, size_t *size, uint *prot);
bool query_memory_ex_from_os(const byte *pc, OUT dr_mem_info_t *info);
#endif
bool get_stack_bounds(dcontext_t *dcontext, byte **base, byte **top);
/* Does a safe_read of *src_ptr into dst_var, returning true for success. We
* assert that the size of dst and src match. The other advantage over plain
* safe_read is that the caller doesn't need to pass sizeof(dst), which is
* useful for repeated small memory accesses.
*/
#define SAFE_READ_VAL(dst_var, src_ptr) \
(ASSERT(sizeof(dst_var) == sizeof(*src_ptr)), \
safe_read(src_ptr, sizeof(dst_var), &dst_var))
bool is_readable_without_exception(const byte *pc, size_t size);
bool is_readable_without_exception_query_os(byte *pc, size_t size);
bool safe_read(const void *base, size_t size, void *out_buf);
bool safe_read_ex(const void *base, size_t size, void *out_buf, size_t *bytes_read);
bool safe_write_ex(void *base, size_t size, const void *in_buf, size_t *bytes_written);
bool is_user_address(byte *pc);
/* returns osprot flags preserving all native protection flags except
* for RWX, which are replaced according to memprot */
uint
osprot_replace_memprot(uint old_osprot, uint memprot);
/* returns false if out of memory */
bool set_protection(byte *pc, size_t size, uint prot);
/* Change protections on memory region starting at pc of length size
* (padded to page boundaries). This method is meant to be used on DR memory
* as part of protect from app and is safe with respect to stats and changing
* the protection of the data segment. */
/* returns false if out of memory */
bool change_protection(byte *pc, size_t size, bool writable);
#ifdef WINDOWS
/* makes pc:pc+size (page_padded) writable preserving other flags
* returns false if out of memory
*/
bool make_hookable(byte *pc, size_t size, bool *changed_prot);
/* if changed_prot makes pc:pc+size (page padded) unwritable preserving
* other flags */
void make_unhookable(byte *pc, size_t size, bool changed_prot);
#endif
/* requires that pc is page aligned and size is multiple of the page size
* and marks that memory writable, preserves other flags,
* returns false if out of memory
*/
bool make_writable(byte *pc, size_t size);
/* requires that pc is page aligned and size is multiple of the page size
* and marks that memory NOT writable, preserves other flags */
void make_unwritable(byte *pc, size_t size);
/* like make_writable but adds COW (note: only usable if allocated COW) */
bool make_copy_on_writable(byte *pc, size_t size);
/***************************************************************************
* SELF_PROTECTION
*/
/* Values for protect_mask to specify what is write-protected from malicious or
* inadvertent modification by the application.
* DATA_CXTSW and GLOBAL are done on each context switch
* the rest are on-demand:
* DATASEGMENT, DATA_FREQ, and GENCODE only on the rare occasions when we write to them
* CACHE only when emitting or {,un}linking
* LOCAL only on path in DR that needs to write to local
*/
enum {
/* Options specifying protection of our DR dll data sections: */
/* .data == variables written only at init or exit time or rarely in between */
SELFPROT_DATA_RARE = 0x001,
/* .fspdata == frequently written enough that we separate from .data.
* FIXME case 8073: currently these are unprotected on every cxt switch
*/
SELFPROT_DATA_FREQ = 0x002,
/* .cspdata == so frequently written that to protect them requires unprotecting
* every context switch.
*/
SELFPROT_DATA_CXTSW = 0x004,
/* if GLOBAL && !DCONTEXT, entire dcontext is unprotected, rest of
* global allocs are protected;
* if GLOBAL && DCONTEXT, cache-written fields of dcontext are unprotected,
* rest are protected;
* if !GLOBAL, DCONTEXT should not be used
*/
SELFPROT_GLOBAL = 0x008,
SELFPROT_DCONTEXT = 0x010, /* means we split out unprotected_context_t --
* no actual protection unless SELFPROT_GLOBAL */
SELFPROT_LOCAL = 0x020,
SELFPROT_CACHE = 0x040, /* FIXME: thread-safe NYI when doing all units */
SELFPROT_STACK = 0x080, /* essentially always on with clean-dstack dispatch()
* design, leaving as a bit in case we do more later */
/* protect our generated thread-shared and thread-private code */
SELFPROT_GENCODE = 0x100,
/* FIXME: TEB page on Win32
* Other global structs, like thread-local callbacks on Win32?
* PEB page?
*/
/* options that require action on every context switch
* FIXME: global heap used to be much rarer before shared
* fragments, only containing "important" data, which is why we
* un-protected on every context switch. We should re-think that
* now that most things are shared.
*/
SELFPROT_ON_CXT_SWITCH = (SELFPROT_DATA_CXTSW | SELFPROT_GLOBAL
/* FIXME case 8073: this is only temporary until
* we finish implementing .fspdata unprots */
| SELFPROT_DATA_FREQ),
SELFPROT_ANY_DATA_SECTION = (SELFPROT_DATA_RARE | SELFPROT_DATA_FREQ |
SELFPROT_DATA_CXTSW),
};
/* Values to refer to individual data sections. The order is not
* important here.
*/
enum {
DATASEC_NEVER_PROT = 0,
DATASEC_RARELY_PROT,
DATASEC_FREQ_PROT,
DATASEC_CXTSW_PROT,
DATASEC_NUM,
};
/* in dynamo.c */
extern const uint DATASEC_SELFPROT[];
extern const char * const DATASEC_NAMES[];
/* see dynamo.c for why this is not an array */
extern const uint datasec_writable_neverprot;
extern uint datasec_writable_rareprot;
extern uint datasec_writable_freqprot;
extern uint datasec_writable_cxtswprot;
/* this is a uint not a bool */
#define DATASEC_WRITABLE(which) \
((which) == DATASEC_RARELY_PROT ? datasec_writable_rareprot : \
((which) == DATASEC_CXTSW_PROT ? datasec_writable_cxtswprot : \
((which) == DATASEC_FREQ_PROT ? datasec_writable_freqprot : \
datasec_writable_neverprot)))
/* these must be plain literals since we need these in pragmas/attributes */
#define NEVER_PROTECTED_SECTION ".nspdata"
#define RARELY_PROTECTED_SECTION ".data"
#define FREQ_PROTECTED_SECTION ".fspdata"
#define CXTSW_PROTECTED_SECTION ".cspdata"
/* note that asserting !protected is safe, but asserting protection
* is racy as another thread could be in an unprot window.
* see also check_should_be_protected().
*/
#define DATASEC_PROTECTED(which) (DATASEC_WRITABLE(which) == 0)
/* User MUST supply an init value, or else cl will leave in .bss (will show up
* at end of .data), which is why we hardcode the = here!
* We use varargs to allow commas in the init if a struct.
*/
#define DECLARE_FREQPROT_VAR(var, ...) \
START_DATA_SECTION(FREQ_PROTECTED_SECTION, "w") \
var VAR_IN_SECTION(FREQ_PROTECTED_SECTION) = __VA_ARGS__; \
END_DATA_SECTION()
#define DECLARE_CXTSWPROT_VAR(var, ...) \
START_DATA_SECTION(CXTSW_PROTECTED_SECTION, "w") \
var VAR_IN_SECTION(CXTSW_PROTECTED_SECTION) = __VA_ARGS__; \
END_DATA_SECTION()
#define DECLARE_NEVERPROT_VAR(var, ...) \
START_DATA_SECTION(NEVER_PROTECTED_SECTION, "w") \
var VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = __VA_ARGS__; \
END_DATA_SECTION()
#define SELF_PROTECT_ON_CXT_SWITCH \
(TESTANY(SELFPROT_ON_CXT_SWITCH, DYNAMO_OPTION(protect_mask)) \
|| INTERNAL_OPTION(single_privileged_thread))
/* macros to put mask check outside of function, for efficiency */
#define SELF_PROTECT_LOCAL(dc, w) do { \
if (TEST(SELFPROT_LOCAL, dynamo_options.protect_mask)) \
protect_local_heap(dc, w); \
} while (0);
#define SELF_PROTECT_GLOBAL(w) do { \
if (TEST(SELFPROT_GLOBAL, dynamo_options.protect_mask)) \
protect_global_heap(w); \
} while (0);
#define ASSERT_LOCAL_HEAP_PROTECTED(dcontext) \
ASSERT(!TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) || \
local_heap_protected(dcontext))
#define ASSERT_LOCAL_HEAP_UNPROTECTED(dcontext) \
ASSERT(!TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) || \
!local_heap_protected(dcontext))
#define SELF_PROTECT_DATASEC(which) do { \
if (TEST(DATASEC_SELFPROT[which], DYNAMO_OPTION(protect_mask))) \
protect_data_section(which, READONLY); \
} while (0);
#define SELF_UNPROTECT_DATASEC(which) do { \
if (TEST(DATASEC_SELFPROT[which], DYNAMO_OPTION(protect_mask))) \
protect_data_section(which, WRITABLE); \
} while (0);
/***************************************************************************/
#ifdef DEBUG
void mem_stats_snapshot(void);
#endif
app_pc get_dynamorio_dll_start(void);
app_pc get_dynamorio_dll_preferred_base(void);
bool is_in_dynamo_dll(app_pc pc);
int find_dynamo_library_vm_areas(void);
int find_executable_vm_areas(void);
/* all_memory_areas is !HAVE_MEMINFO-only: nop elsewhere */
void all_memory_areas_lock(void);
void all_memory_areas_unlock(void);
/* pass -1 if type is unchanged */
void update_all_memory_areas(app_pc start, app_pc end, uint prot, int type);
bool remove_from_all_memory_areas(app_pc start, app_pc end);
/* file operations */
/* defaults to read only access, if write is not set ignores others */
#define OS_OPEN_READ 0x01
#define OS_OPEN_WRITE 0x02
#define OS_OPEN_APPEND 0x04 /* if not set, the file is truncated */
#define OS_OPEN_REQUIRE_NEW 0x08
#define OS_EXECUTE 0x10 /* only used on win32, currently */
#define OS_SHARE_DELETE 0x20 /* only used on win32, currently */
#define OS_OPEN_FORCE_OWNER 0x40 /* only used on win32, currently */
#define OS_OPEN_ALLOW_LARGE 0x80 /* only used on linux32, currently */
#define OS_OPEN_CLOSE_ON_FORK 0x100 /* only used on linux */
#define OS_OPEN_RESERVED 0x10000000 /* used for fd_table on linux */
/* always use OS_OPEN_REQUIRE_NEW when asking for OS_OPEN_WRITE, in
* order to avoid hard link or symbolic link attacks if the file is in
* a world writable locations and the process may have high
* privileges.
*/
file_t os_open(const char *fname, int os_open_flags);
file_t os_open_protected(const char *fname, int os_open_flags);
file_t os_open_directory(const char *fname, int os_open_flags);
bool os_file_exists(const char *fname, bool is_dir);
bool os_get_file_size(const char *file, uint64 *size); /* NYI on Linux */
bool os_get_file_size_by_handle(file_t fd, uint64 *size);
bool os_get_current_dir(char *buf, size_t bufsz);
typedef enum {
CREATE_DIR_ALLOW_EXISTING = 0x0,
/* should always use CREATE_DIR_REQUIRE_NEW, see reference in
* OS_OPEN_REQUIRE_NEW - to avoid symlink attacks (although only
* an issue when files we create in these directories have
* predictible names - case 9138)
*/
CREATE_DIR_REQUIRE_NEW = 0x1,
CREATE_DIR_FORCE_OWNER = 0x2,
} create_directory_flags_t;
bool os_create_dir(const char *fname, create_directory_flags_t create_dir_flags);
bool os_delete_dir(const char *fname);
void os_close(file_t f);
void os_close_protected(file_t f);
/* returns number of bytes written, negative if failure */
ssize_t os_write(file_t f, const void *buf, size_t count);
/* returns number of bytes read, negative if failure */
ssize_t os_read(file_t f, void *buf, size_t count);
void os_flush(file_t f);
/* For use with os_file_seek(), specifies the origin at which to apply the offset
* NOTE - keep in synch with DR_SEEK_* in insturment.h and SEEK_* from Linux headers */
#define OS_SEEK_SET 0 /* start of file */
#define OS_SEEK_CUR 1 /* current file position */
#define OS_SEEK_END 2 /* end of file */
/* seek the current file position to offset bytes from origin, return true if successful */
bool os_seek(file_t f, int64 offset, int origin);
/* return the current file position, -1 on failure */
int64 os_tell(file_t f);
bool os_delete_file(const char *file_name);
bool os_delete_mapped_file(const char *filename);
bool os_rename_file(const char *orig_name, const char *new_name, bool replace);
/* These routines do not update dynamo_areas; use the non-os_-prefixed versions */
/* File mapping on Linux uses a 32-bit offset, with mmap2 considering
* it a multiple of the page size in order to have a larger reach; on
* Windows we have a 64-bit offset. We go with the widest here, a
* 64-bit offset, to avoid limiting Windows.
*/
/* When fixed==true, DR tries to allocate memory from the address specified
* by addr.
* In Linux, it has the same semantic as mmap system call with MAP_FIXED flags,
* If the memory region specified by addr and size overlaps pages of
* any existing mapping(s), then the overlapped part of the existing mapping(s)
* will be discarded. If the specified address cannot be used, it will fail
* and returns NULL.
* XXX: in Windows, fixed argument is currently ignored (PR 214077 / case 9642),
* and handling it is covered by PR 214097.
*/
byte *os_map_file(file_t f, size_t *size INOUT, uint64 offs, app_pc addr,
uint prot, map_flags_t map_flags);
bool os_unmap_file(byte *map, size_t size);
/* unlike set_protection, os_set_protection does not update
* the allmem info in Linux. */
bool os_set_protection(byte *pc, size_t length, uint prot/*MEMPROT_*/);
bool
os_current_user_directory(char *directory_prefix /* INOUT */,
uint directory_size,
bool create);
bool
os_validate_user_owned(file_t file_or_directory_handle);
bool
os_get_disk_free_space(/*IN*/ file_t file_handle,
/*OUT*/ uint64 *AvailableQuotaBytes /*OPTIONAL*/,
/*OUT*/ uint64 *TotalQuotaBytes /*OPTIONAL*/,
/*OUT*/ uint64 *TotalVolumeBytes /*OPTIONAL*/);
#ifdef PROFILE_RDTSC
extern uint kilo_hertz;
#endif
/* Despite the name, this enum is used for all types of critical events:
es
* asserts and crashes for all builds, and security violations for PROGRAM_SHEPHERDING builds
* When a security_violation is being reported, enum value must be negative!
*/
typedef enum {
#ifdef PROGRAM_SHEPHERDING
STACK_EXECUTION_VIOLATION = -1,
HEAP_EXECUTION_VIOLATION = -2,
RETURN_TARGET_VIOLATION = -3,
RETURN_DIRECT_RCT_VIOLATION = -4, /* NYI DIRECT_CALL_CHECK */
INDIRECT_CALL_RCT_VIOLATION = -5,
INDIRECT_JUMP_RCT_VIOLATION = -6,
# ifdef HOT_PATCHING_INTERFACE
HOT_PATCH_DETECTOR_VIOLATION = -7,
HOT_PATCH_PROTECTOR_VIOLATION = -8,
/* Errors and exceptions in hot patches are treated the same. Case 5696. */
HOT_PATCH_FAILURE = -9,
# endif
/* internal */
ATTACK_SIMULATION_VIOLATION = -10,
ATTACK_SIM_NUDGE_VIOLATION = -11,
#endif
/* not really PROGRAM_SHEPHERDING */
ASLR_TARGET_VIOLATION = -12,
#ifdef GBOP
GBOP_SOURCE_VIOLATION = -13,
#endif /* GBOP */
#ifdef PROCESS_CONTROL
PROCESS_CONTROL_VIOLATION = -14, /* case 8594 */
#endif
APC_THREAD_SHELLCODE_VIOLATION = -15, /* note still presented externally as .B */
/* Not a valid violation; used for initializing security_violation_t vars. */
INVALID_VIOLATION = 0,
#ifdef PROGRAM_SHEPHERDING
/* Add new violation types above this line as consecutive negative
* numbers, and update get_security_violation_name() for the
* appropriate letter obfuscation */
ALLOWING_OK = 1,
ALLOWING_BAD = 2,
#endif
NO_VIOLATION_BAD_INTERNAL_STATE = 3,
NO_VIOLATION_OK_INTERNAL_STATE = 4
} security_violation_t;
/* in diagnost.c */
void report_diagnostics(const char *message, const char *name,
security_violation_t violation_type);
void append_diagnostics(file_t diagnostics_file, const char *message, const char *name,
security_violation_t violation_type);
void diagnost_exit(void);
bool check_for_unsupported_modules(void);
#ifdef RETURN_AFTER_CALL
typedef enum {
INITIAL_STACK_EMPTY = 0,
INITIAL_STACK_BOTTOM_REACHED = 1,
INITIAL_STACK_BOTTOM_NOT_REACHED = 2
} initial_call_stack_status_t;
initial_call_stack_status_t at_initial_stack_bottom(dcontext_t *dcontext, app_pc target_pc);
bool at_known_exception(dcontext_t *dcontext, app_pc target_pc, app_pc source_fragment);
#endif
/* contended path of mutex operations */
bool ksynch_var_initialized(KSYNCH_TYPE *var);
void mutex_wait_contended_lock(mutex_t *lock);
void mutex_notify_released_lock(mutex_t *lock);
void mutex_free_contended_event(mutex_t *lock);
/* contended path of rwlock operations */
void rwlock_wait_contended_writer(read_write_lock_t *rwlock);
void rwlock_notify_writer(read_write_lock_t *rwlock);
void rwlock_wait_contended_reader(read_write_lock_t *rwlock);
void rwlock_notify_readers(read_write_lock_t *rwlock);
/* The event interface */
/* An event object. */
#ifdef WINDOWS
typedef HANDLE event_t;
#else
typedef struct linux_event_t *event_t;
#endif
event_t create_event(void);
void destroy_event(event_t e);
void signal_event(event_t e);
void reset_event(event_t e);
void wait_for_event(event_t e);
/* get current timer frequency in KHz */
/* NOTE: Keep in mind that with voltage scaling in power saving mode
* this may not be a reliable way to obtain time information */
timestamp_t
get_timer_frequency(void);
/* Returns the number of seconds since Jan 1, 1601 (this is
* the current UTC time).
*/
uint
query_time_seconds(void);
/* Returns the number of milliseconds since Jan 1, 1601 (this is
* the current UTC time).
*/
uint64
query_time_millis(void);
/* gives a good but not necessarily crypto-strength random seed */
uint
os_random_seed(void);
/* module map/unmap processing relevant to the RCT policies */
/* cf. rct_analyze_module_at_violation() which can be called lazily */
void
rct_process_module_mmap(app_pc module_base, size_t module_size,
bool add, bool already_relocated);
/* module boundary and code section analysis for RCT policies */
/* FIXME: should be better abstracted by calling a routine that
* enumerates code sections, while keeping the general driver in rct.c
*/
bool
rct_analyze_module_at_violation(dcontext_t *dcontext, app_pc target_pc);
bool
aslr_is_possible_attack(app_pc target);
app_pc
aslr_possible_preferred_address(app_pc target_addr);
#ifdef WINDOWS
/* dispatch places next pc in asynch_target and clears it after syscall handling
* completes, so a zero value means shared syscall was used and the next pc
* is in the esi slot. If asynch_target == BACK_TO_NATIVE_AFTER_SYSCALL then the
* thread is native at an intercepted syscall and the real post syscall target is in
* native_exec_postsyscall. */
# define POST_SYSCALL_PC(dc) \
((dc)->asynch_target == NULL ? \
ASSERT(DYNAMO_OPTION(shared_syscalls)), (app_pc) get_mcontext((dc))->xsi : \
((dc)->asynch_target == BACK_TO_NATIVE_AFTER_SYSCALL ? \
(dc)->native_exec_postsyscall : (dc)->asynch_target))
#else
/* on linux asynch target always holds the post syscall pc */
# define POST_SYSCALL_PC(dc) ((dc)->asynch_target)
#endif
/* This has been exposed from win32 module so that hotp_only_gateway can
* return the right return code.
*/
typedef enum {
AFTER_INTERCEPT_LET_GO,
AFTER_INTERCEPT_LET_GO_ALT_DYN, /* alternate direct execution target,
* usable only with
* AFTER_INTERCEPT_DYNAMIC_DECISION,
* not by itself */
AFTER_INTERCEPT_TAKE_OVER,
AFTER_INTERCEPT_DYNAMIC_DECISION, /* handler returns one of prev values */
/* handler is expected to restore original instructions */
AFTER_INTERCEPT_TAKE_OVER_SINGLE_SHOT, /* static only with alternative target */
} after_intercept_action_t;
/* Argument structure for intercept function; contains app state at the point
* our intercept routine will take over. Fix for case 7597.
*
* -- CAUTION -- the number, order and size of fields in this structure are
* assumed in emit_intercept_code(). Changing anything here is likely to cause
* DR hooks & hotp_only to fail.
*/
typedef struct {
void *callee_arg; /* Argument passed to the intercept routine. */
app_pc start_pc; /* optimization: could use mc.pc instead */
priv_mcontext_t mc; /* note: 8-byte aligned */
} app_state_at_intercept_t;
/* note that only points intercepted with DYNAMIC_DECISION (currently only
* [un]load_dll) care about the return value */
typedef after_intercept_action_t intercept_function_t(app_state_at_intercept_t *args);
/* opcodes and encoding constants that we sometimes directly use */
#ifdef X86
/* FIXME: if we add more opcodes maybe should be exported by arch/ */
enum {
JMP_REL32_OPCODE = 0xe9,
JMP_REL32_SIZE = 5, /* size in bytes of 32-bit rel jmp */
CALL_REL32_OPCODE = 0xe8,
# ifdef X64
JMP_ABS_IND64_OPCODE = 0xff,
JMP_ABS_IND64_SIZE = 6, /* size in bytes of a 64-bit abs ind jmp */
JMP_ABS_MEM_IND64_MODRM = 0x25,
# endif
};
#elif defined(ARM)
enum {
/* FIXME i#1551: this is for A32 for now to get things compiling */
JMP_REL32_OPCODE = 0xec000000,
JMP_REL32_SIZE = 4,
CALL_REL32_OPCODE = 0xed000000,
};
#else
# error only X86 and ARM supported
#endif /* X86 */
#ifdef WINDOWS
/* used for option early_inject_location */
/* use 0 to supply an arbitrary address via -early_inject_address */
/* NOTE - these enum values are passed between processes, don't change/reuse
* any existing values. */
/* NtMapViewOfSection doesn't work as a place to LoadDll (ldr can't handle the
* re-entrancy), but might be a good place to takeover if we remote mapped in
* and needed ntdll initialized (first MapView is for kernel32.dll fom what
* I've seen, though beware the LdrLists aren't consistent at this point). */
/* As currently implemented KiUserException location doesn't work, but we might
* be able to find a better exception location (maybe point the image's import
* section to invalid memory instead? TODO try). */
/* see early_inject_init() in os.c for more notes on best location for each
* os version */
/* NOTE - be carefull changing this enum as it's shared across proccesses.
* Also values <= LdrDefault are assumed to need address computation while
* those > are assumed to not need it. */
#define INJECT_LOCATION_IS_LDR(loc) (loc <= INJECT_LOCATION_LdrDefault)
#define INJECT_LOCATION_IS_LDR_NON_DEFAULT(loc) (loc < INJECT_LOCATION_LdrDefault)
enum {
INJECT_LOCATION_Invalid = -100, /* invalid sentinel */
INJECT_LOCATION_LdrpLoadDll = 0, /* Good for XP, 2k3 */
INJECT_LOCATION_LdrpLoadImportModule = 1, /* Good for 2k */
INJECT_LOCATION_LdrCustom = 2, /* Specify custom address */
INJECT_LOCATION_LdrLoadDll = 3, /* Good for 2k3 */
INJECT_LOCATION_LdrDefault = 4, /* Pick best location based on OS */
/* For NT the best location is LdrpLoadImportModule, but we can't find that
* automatically on NT, so LdrDefault for NT uses -early_inject_address if
* specified or else disables early injection (xref 7806). */
/* Beyond this point not expected to need address determination */
/* Earliest injection via remote map.
* On Vista+ this is treated as LdrInitializeThunk as there is no init APC.
*/
INJECT_LOCATION_KiUserApc = 5,
INJECT_LOCATION_KiUserException = 6, /* No good, Ldr init issues */
/* Clients that depend on private libraries have trouble running
* at the early and earliest injection points. At the image
* entry point all the app libraries are loaded and hence it is suitable
* for those clients which are using private libraries those depends
* on some app libraries being initialized
*/
INJECT_LOCATION_ImageEntry = 7,
INJECT_LOCATION_MAX = INJECT_LOCATION_ImageEntry,
};
#endif
void take_over_primary_thread(void);
#ifdef HOT_PATCHING_INTERFACE
void *
get_drmarker_hotp_policy_status_table(void);
void
set_drmarker_hotp_policy_status_table(void *new_table);
/* Used for -hotp_only; can be exposed if needed elsewhere later on. */
byte *
hook_text(byte *hook_code_buf, const app_pc image_addr,
intercept_function_t hook_func, const void *callee_arg,
const after_intercept_action_t action_after,
const bool abort_if_hooked, const bool ignore_cti,
byte **app_code_copy_p, byte **alt_exit_tgt_p);
void
unhook_text(byte *hook_code_buf, app_pc image_addr);
void
insert_jmp_at_tramp_entry(byte *trampoline, byte *target);
#endif /* HOT_PATCHING_INTERFACE */
/* checks for compatibility OS specific options, returns true if
* modified the value of any options to make them compatible
*/
bool
os_check_option_compatibility(void);
/* Introduced as part of PR 250294 - 64-bit hook reachability. */
#define LANDING_PAD_AREA_SIZE 64*1024
#define MAX_HOOK_DISPLACED_LENGTH (JMP_LONG_LENGTH + MAX_INSTR_LENGTH)
#ifdef X64
/* 8 bytes for the 64-bit abs addr, 6 for abs ind jmp to the trampoline and 5
* for return jmp back to the instruction after the hook. Plus displaced instr(s).
*/
# define LANDING_PAD_SIZE (19 + MAX_HOOK_DISPLACED_LENGTH)
#else
/* 5 bytes each for the two relative jumps (one to the trampoline and the
* other back to instruction after hook. Plus displaced instr(s).
*/
# define LANDING_PAD_SIZE (10 + MAX_HOOK_DISPLACED_LENGTH)
#endif
byte *alloc_landing_pad(app_pc addr_to_hook);
bool
trim_landing_pad(byte *lpad_start, size_t space_used);
void landing_pads_to_executable_areas(bool add);
/* in loader_shared.c */
app_pc load_private_library(const char *filename, bool reachable);
bool unload_private_library(app_pc modbase);
/* searches in standard paths instead of requiring abs path */
app_pc locate_and_load_private_library(const char *name, bool reachable);
void loader_init(void);
void loader_exit(void);
void loader_thread_init(dcontext_t *dcontext);
void loader_thread_exit(dcontext_t *dcontext);
bool in_private_library(app_pc pc);
#endif /* OS_SHARED_H */