* Copyright (c) 2010-2022 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.
*/
* os_shared.h - shared declarations for os facilities from unix/win32
*/
#ifndef OS_SHARED_H
#define OS_SHARED_H
#include "os_api.h"
enum { VM_ALLOCATION_BOUNDARY = 64 * 1024 };
struct _local_state_t;
void
d_r_os_init(void);
void
os_slow_exit(void);
void
os_fast_exit(void);
void
os_tls_init(void);
* !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_data);
* like synch have initialized.
*/
void
os_thread_init_finalize(dcontext_t *dcontext, void *os_data);
void
os_thread_exit(dcontext_t *dcontext, bool other_thread);
void
os_thread_under_dynamo(dcontext_t *dcontext);
void
os_thread_not_under_dynamo(dcontext_t *dcontext);
void
os_process_under_dynamorio_initiate(dcontext_t *dcontext);
void
os_process_under_dynamorio_complete(dcontext_t *dcontext);
void
os_process_not_under_dynamorio(dcontext_t *dcontext);
bool
os_take_over_all_unknown_threads(dcontext_t *dcontext);
bool
detach_do_not_translate(thread_record_t *tr);
void
detach_finalize_translation(thread_record_t *tr, priv_mcontext_t *mc);
void
detach_finalize_cleanup(void);
void
os_heap_init(void);
void
os_heap_exit(void);
* 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,
HEAP_ERROR_CANT_RESERVE_IN_REGION = 1,
HEAP_ERROR_NOT_AT_PREFERRED = 2,
};
typedef uint heap_error_code_t;
enum {
#ifdef WINDOWS
RAW_ALLOC_RESERVE_ONLY = 0x0001,
RAW_ALLOC_COMMIT_ONLY = 0x0002,
#endif
#ifdef UNIX
RAW_ALLOC_32BIT = 0x0004,
#endif
};
void *
os_raw_mem_alloc(void *preferred, size_t size, uint prot, uint flags,
heap_error_code_t *error_code);
bool
os_raw_mem_free(void *p, size_t size, uint flags, heap_error_code_t *error_code);
* 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);
* 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);
bool
os_heap_commit(void *p, size_t size, uint prot, heap_error_code_t *error_code);
void
os_heap_decommit(void *p, size_t size, heap_error_code_t *error_code);
* containing p is freed and size is ignored) */
void
os_heap_free(void *p, size_t size, heap_error_code_t *error_code);
* 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
d_r_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);
* 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);
void
thread_set_self_context(void *cxt);
void
thread_set_self_mcontext(priv_mcontext_t *mc);
bool
os_thread_take_over_suspended_native(dcontext_t *dcontext);
dcontext_t *
os_thread_take_over_secondary(priv_mcontext_t *mc);
* Returns whether the thread is known.
*/
bool
os_thread_re_take_over(void);
dcontext_t *
get_thread_private_dcontext(void);
void
set_thread_private_dcontext(dcontext_t *dcontext);
ushort
os_tls_offset(ushort tls_offs);
ushort
os_local_state_offset(ushort seg_offs);
struct _local_state_t;
struct _local_state_extended_t;
struct _local_state_t *
get_local_state(void);
struct _local_state_extended_t *
get_local_state_extended(void);
* 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);
bool
os_tls_calloc(OUT uint *offset, uint num_slots, uint alignment);
bool
os_tls_cfree(uint offset, uint num_slots);
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);
int
num_app_args();
int
get_app_args(OUT dr_app_arg_t *args_array, int args_count);
const char *
get_application_short_name(void);
char *
get_computer_name(void);
app_pc
get_application_base(void);
app_pc
get_application_end(void);
int
get_num_processors(void);
typedef enum {
TERMINATE_PROCESS = 0x1,
TERMINATE_THREAD = 0x2,
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
} 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,
GUARD_PAGE_EXCEPTION,
SINGLE_STEP_EXCEPTION,
} dr_exception_type_t;
void
os_forge_exception(app_pc exception_address, dr_exception_type_t type);
* 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,
DUMPCORE_CLIENT_EXCEPTION = 0x0020,
DUMPCORE_TIMEOUT = 0x0040,
DUMPCORE_CURIOSITY = 0x0080,
#ifdef HOT_PATCHING_INTERFACE
DUMPCORE_HOTP_FAILURE = 0x0100,
#endif
DUMPCORE_OUT_OF_MEM = 0x0200,
DUMPCORE_OUT_OF_MEM_SILENT = 0x0400,
#ifdef UNIX
DUMPCORE_INCLUDE_STACKDUMP = 0x0800,
DUMPCORE_WAIT_FOR_DEBUGGER = 0x1000,
#endif
#ifdef HOT_PATCHING_INTERFACE
DUMPCORE_HOTP_DETECTION = 0x2000,
DUMPCORE_HOTP_PROTECTION = 0x4000,
#endif
DUMPCORE_DR_ABORT = 0x8000,
* usually be the app's fault (or normal behavior for the app) */
DUMPCORE_FORGE_ILLEGAL_INST = 0x10000,
DUMPCORE_FORGE_UNREAD_EXEC = 0x20000,
* 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,
DUMPCORE_UNSUPPORTED_APP = 0x100000,
DUMPCORE_STACK_OVERFLOW = 0x200000,
#ifdef UNIX
DUMPCORE_OPTION_PAUSE = DUMPCORE_WAIT_FOR_DEBUGGER | DUMPCORE_INTERNAL_EXCEPTION |
DUMPCORE_SECURITY_VIOLATION | DUMPCORE_DEADLOCK | DUMPCORE_ASSERTION |
DUMPCORE_FATAL_USAGE_ERROR | DUMPCORE_CLIENT_EXCEPTION |
DUMPCORE_UNSUPPORTED_APP | DUMPCORE_TIMEOUT | DUMPCORE_CURIOSITY |
DUMPCORE_DR_ABORT | 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);
* 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);
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);
* 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);
char *
get_dynamorio_library_path(void);
#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
#else
# define MEMPROT_VDSO DR_MEMPROT_VDSO
#endif
#define MEMPROT_HAS_COMMENT DR_MEMPROT_GUARD
#define MEMPROT_META_FLAGS (MEMPROT_VDSO | MEMPROT_HAS_COMMENT)
* version for DynamoRIO's internal use. Since PAGE_SIZE looks like
* it might be a constant, in new code it would be better to use an
* explicit function call.
*/
#undef PAGE_SIZE
#define PAGE_SIZE os_page_size()
* It uses a function call so be careful where performance is critical.
*/
#define PAGE_START(x) (((ptr_uint_t)(x)) & ~(os_page_size() - 1))
#define PAGE_START64(x) (((uint64)(x)) & ~((uint64)os_page_size() - 1))
size_t
os_page_size(void);
#ifdef UNIX
void
os_page_size_init(const char **env, bool env_followed_by_auxv);
size_t
os_minsigstksz(void);
#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);
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);
void
os_check_new_app_module(dcontext_t *dcontext, app_pc pc);
#endif
bool
get_stack_bounds(dcontext_t *dcontext, byte **base, byte **top);
* 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)), \
d_r_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
is_readable_without_exception_query_os_noblock(byte *pc, size_t size);
bool
d_r_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);
* for RWX, which are replaced according to memprot */
uint
osprot_replace_memprot(uint old_osprot, uint memprot);
bool
set_protection(byte *pc, size_t size, uint prot);
* (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. */
bool
change_protection(byte *pc, size_t size, bool writable);
#ifdef WINDOWS
* returns false if out of memory
*/
bool
make_hookable(byte *pc, size_t size, bool *changed_prot);
* other flags */
void
make_unhookable(byte *pc, size_t size, bool changed_prot);
#endif
* and marks that memory writable, preserves other flags,
* returns false if out of memory
*/
bool
make_writable(byte *pc, size_t size);
* and marks that memory NOT writable, preserves other flags */
void
make_unwritable(byte *pc, size_t size);
bool
make_copy_on_writable(byte *pc, size_t size);
* SELF_PROTECTION
*/
* 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 {
SELFPROT_DATA_RARE = 0x001,
* FIXME case 8073: currently these are unprotected on every cxt switch
*/
SELFPROT_DATA_FREQ = 0x002,
* every context switch.
*/
SELFPROT_DATA_CXTSW = 0x004,
* 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,
* no actual protection unless SELFPROT_GLOBAL */
SELFPROT_LOCAL = 0x020,
SELFPROT_CACHE = 0x040,
SELFPROT_STACK = 0x080,
* design, leaving as a bit in case we do more later */
SELFPROT_GENCODE = 0x100,
* Other global structs, like thread-local callbacks on Win32?
* PEB page?
*/
* 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
* we finish implementing .fspdata unprots */
| SELFPROT_DATA_FREQ),
SELFPROT_ANY_DATA_SECTION =
(SELFPROT_DATA_RARE | SELFPROT_DATA_FREQ | SELFPROT_DATA_CXTSW),
};
* important here.
*/
enum {
DATASEC_NEVER_PROT = 0,
DATASEC_RARELY_PROT,
DATASEC_FREQ_PROT,
DATASEC_CXTSW_PROT,
DATASEC_NUM,
};
extern const uint DATASEC_SELFPROT[];
extern const char *const DATASEC_NAMES[];
extern const uint datasec_writable_neverprot;
extern uint datasec_writable_rareprot;
extern uint datasec_writable_freqprot;
extern uint datasec_writable_cxtswprot;
#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)))
#define NEVER_PROTECTED_SECTION ".nspdata"
#define RARELY_PROTECTED_SECTION ".data"
#define FREQ_PROTECTED_SECTION ".fspdata"
#define CXTSW_PROTECTED_SECTION ".cspdata"
* 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)
* 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))
#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);
void
all_memory_areas_lock(void);
void
all_memory_areas_unlock(void);
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);
#define OS_OPEN_READ 0x001
#define OS_OPEN_WRITE 0x002
#define OS_OPEN_WRITE_ONLY 0x004
#define OS_OPEN_APPEND 0x008
#define OS_OPEN_REQUIRE_NEW 0x010
#define OS_EXECUTE 0x020
#define OS_SHARE_DELETE 0x040
#define OS_OPEN_FORCE_OWNER 0x080
#define OS_OPEN_ALLOW_LARGE 0x100
#define OS_OPEN_CLOSE_ON_FORK 0x200
#define OS_OPEN_RESERVED 0x10000000
* 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);
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,
* 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);
ssize_t
os_write(file_t f, const void *buf, size_t count);
ssize_t
os_read(file_t f, void *buf, size_t count);
void
os_flush(file_t f);
* NOTE - keep in synch with DR_SEEK_* in insturment.h and SEEK_* from Linux headers */
#define OS_SEEK_SET 0
#define OS_SEEK_CUR 1
#define OS_SEEK_END 2
bool
os_seek(file_t f, int64 offset, int origin);
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);
* 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.
*/
* 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);
file_t
os_create_memory_file(const char *name, size_t size);
void
os_delete_memory_file(const char *name, file_t fd);
* the allmem info in Linux. */
bool
os_set_protection(byte *pc, size_t length, uint prot );
bool
os_current_user_directory(char *directory_prefix , uint directory_size,
bool create);
bool
os_validate_user_owned(file_t file_or_directory_handle);
bool
os_get_disk_free_space( file_t file_handle,
uint64 *AvailableQuotaBytes ,
uint64 *TotalQuotaBytes ,
uint64 *TotalVolumeBytes );
#ifdef PROFILE_RDTSC
extern uint kilo_hertz;
#endif
* 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,
INDIRECT_CALL_RCT_VIOLATION = -5,
INDIRECT_JUMP_RCT_VIOLATION = -6,
# ifdef HOT_PATCHING_INTERFACE
HOT_PATCH_DETECTOR_VIOLATION = -7,
HOT_PATCH_PROTECTOR_VIOLATION = -8,
HOT_PATCH_FAILURE = -9,
# endif
ATTACK_SIMULATION_VIOLATION = -10,
ATTACK_SIM_NUDGE_VIOLATION = -11,
#endif
ASLR_TARGET_VIOLATION = -12,
#ifdef GBOP
GBOP_SOURCE_VIOLATION = -13,
#endif
#ifdef PROCESS_CONTROL
PROCESS_CONTROL_VIOLATION = -14,
#endif
APC_THREAD_SHELLCODE_VIOLATION = -15,
INVALID_VIOLATION = 0,
#ifdef PROGRAM_SHEPHERDING
* 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;
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
bool
ksynch_var_initialized(KSYNCH_TYPE *var);
* mcontext (THREAD_SYNCH_VALID_MCONTEXT). Note that means that all fields of
* \p mc must be valid (e.g. PC, control, integer, MMX fields).
*/
void
mutex_wait_contended_lock(mutex_t *lock, priv_mcontext_t *mc);
void
mutex_notify_released_lock(mutex_t *lock);
void
mutex_free_contended_event(mutex_t *lock);
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);
#ifdef WINDOWS
typedef HANDLE event_t;
#else
typedef struct linux_event_t *event_t;
#endif
event_t
create_event(void);
event_t
create_broadcast_event(void);
void
destroy_event(event_t e);
void
signal_event(event_t e);
void
reset_event(event_t e);
bool
wait_for_event(event_t e, int timeout_ms);
* this may not be a reliable way to obtain time information */
timestamp_t
get_timer_frequency(void);
* the current UTC time).
*/
uint
query_time_seconds(void);
* the current UTC time).
*/
uint64
query_time_millis(void);
uint64
query_time_micros();
uint
os_random_seed(void);
void
rct_process_module_mmap(app_pc module_base, size_t module_size, bool add,
bool already_relocated);
* 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
* 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
# define POST_SYSCALL_PC(dc) ((dc)->asynch_target)
#endif
* return the right return code.
*/
typedef enum {
AFTER_INTERCEPT_LET_GO,
AFTER_INTERCEPT_LET_GO_ALT_DYN,
* usable only with
* AFTER_INTERCEPT_DYNAMIC_DECISION,
* not by itself */
AFTER_INTERCEPT_TAKE_OVER,
AFTER_INTERCEPT_DYNAMIC_DECISION,
AFTER_INTERCEPT_TAKE_OVER_SINGLE_SHOT,
} after_intercept_action_t;
* 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;
app_pc start_pc;
priv_mcontext_t mc;
} app_state_at_intercept_t;
* [un]load_dll) care about the return value */
typedef after_intercept_action_t
intercept_function_t(app_state_at_intercept_t *args);
#ifdef X86
enum {
JMP_REL32_OPCODE = 0xe9,
JMP_REL32_SIZE = 5,
CALL_REL32_OPCODE = 0xe8,
JMP_ABS_IND64_OPCODE = 0xff,
JMP_ABS_IND64_SIZE = 6,
JMP_ABS_MEM_IND64_MODRM = 0x25,
};
#elif defined(AARCHXX)
enum {
JMP_REL32_OPCODE = 0xec000000,
JMP_REL32_SIZE = 4,
CALL_REL32_OPCODE = 0xed000000,
};
#elif defined(RISCV64)
enum {
JMP_REL32_OPCODE = 0xec000000,
JMP_REL32_SIZE = 4,
CALL_REL32_OPCODE = 0xed000000,
};
#else
# error only X86 and ARM supported
#endif
#ifdef WINDOWS
* any existing values. */
* 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). */
* be able to find a better exception location (maybe point the image's import
* section to invalid memory instead? TODO try). */
* os version */
* 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,
INJECT_LOCATION_LdrpLoadDll = 0,
INJECT_LOCATION_LdrpLoadImportModule = 1,
INJECT_LOCATION_LdrCustom = 2,
INJECT_LOCATION_LdrLoadDll = 3,
INJECT_LOCATION_LdrDefault = 4,
* automatically on NT, so LdrDefault for NT uses -early_inject_address if
* specified or else disables early injection (xref 7806). */
* On Vista+ this is treated as LdrInitializeThunk as there is no init APC.
*/
INJECT_LOCATION_KiUserApc = 5,
INJECT_LOCATION_KiUserException = 6,
* 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,
* rely on reaching the image entry, which not all apps do (e.g., .NET).
* This is equivalent to RtlUserThreadStart.
*/
INJECT_LOCATION_ThreadStart = 8,
INJECT_LOCATION_MAX = INJECT_LOCATION_ThreadStart,
};
#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);
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(dcontext_t *dcontext, byte *trampoline, byte *target);
#endif
* modified the value of any options to make them compatible
*/
bool
os_check_option_compatibility(void);
#define LANDING_PAD_AREA_SIZE 64 * 1024
#define MAX_HOOK_DISPLACED_LENGTH (JMP_LONG_LENGTH + MAX_INSTR_LENGTH)
#ifdef X64
* for return jmp back to the instruction after the hook. Plus displaced instr(s).
*/
# define LANDING_PAD_SIZE (19 + MAX_HOOK_DISPLACED_LENGTH)
#else
* 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);
app_pc
load_private_library(const char *filename, bool reachable);
bool
unload_private_library(app_pc modbase);
app_pc
locate_and_load_private_library(const char *name, bool reachable);
void
loader_init_prologue(void);
void
loader_init_epilogue(dcontext_t *dcontext);
void
loader_exit(void);
void
loader_thread_init(dcontext_t *dcontext);
void
loader_thread_exit(dcontext_t *dcontext);
void
loader_make_exit_calls(dcontext_t *dcontext);
bool
in_private_library(app_pc pc);
void
loader_allow_unsafe_static_behavior(void);
#endif