blob: 36bc5b6005c976465dc62290909dccfa888e1b75 [file]
/* **********************************************************
* Copyright (c) 2014-2024 Google, Inc. All rights reserved.
* **********************************************************/
/* Dr. Memory: the memory debugger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License, and no later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dr_api.h"
#include "drsyscall.h"
#include "drsyscall_os.h"
#include <sys/syscall.h>
#include <string.h>
#include <fcntl.h>
/* FIXME i#1440: finish porting Dr. Syscall to Mac OSX */
/***************************************************************************
* SYSTEM CALLS FOR MAC
*/
/* 64-bit and 32-bit have the same numbers, which is convenient */
/* Table that maps system call number to a syscall_info_t* */
#define SYSTABLE_HASH_BITS 9 /* ~2x the # of entries */
hashtable_t systable;
/* Table that maps secondary system call number to a syscall_info_t*
* We leave this table empty to be in sync with Windows & Linux solutions.
* xref i#1438 i#1549.
*/
#define SECONDARY_SYSTABLE_HASH_BITS 1 /* now we haven't any entries */
hashtable_t secondary_systable;
/* The tables are in separate files as they are quite large */
/* BSD syscalls */
extern syscall_info_t syscall_info_bsd[];
extern size_t count_syscall_info_bsd;
/* FIXME i#1440: add mach syscall table */
#define SYSCALL_NUM_MARKER_MACH 0x1000000
#define SYSCALL_NUM_MARKER_BSD 0x2000000 /* x64 only */
#define SYSCALL_NUM_MARKER_MACHDEP 0x3000000
/* FIXME i#1440: add machdep syscall table */
/***************************************************************************
* PER-SYSCALL HANDLING
*/
void
os_handle_pre_syscall(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
switch (ii->arg->sysnum.number) {
case SYS_open:
case SYS_open_nocancel: {
/* 3rd arg is only required for O_CREAT */
int flags = (int) pt->sysarg[1];
if (TEST(O_CREAT, flags)) {
if (!report_sysarg_type(ii, 2, SYSARG_READ, sizeof(int),
DRSYS_TYPE_SIGNED_INT, NULL))
return;
}
break;
}
}
/* If you add any handling here: need to check ii->abort first */
}
void
os_handle_post_syscall(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* each handler checks result for success */
switch (ii->arg->sysnum.number) {
/* FIXME i#1440: add handling */
}
/* If you add any handling here: need to check ii->abort first */
}
/***************************************************************************
* SHADOW PER-ARG-TYPE HANDLING
*/
/* XXX i#1440: share w/ Linux */
static bool
handle_cstring_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size/*in bytes*/)
{
return handle_cstring(ii, arg_info->param, arg_info->flags,
NULL, start, size, NULL,
/* let normal check ensure full size is addressable */
false);
}
/* XXX i#1440: share w/ Linux */
static void
check_strarray(sysarg_iter_info_t *ii, char **array, int ordinal, const char *id)
{
char *str;
int i = 0;
# define STR_ARRAY_MAX_ITER 64*1024 /* safety check */
while (safe_read(&array[i], sizeof(str), &str) && str != NULL
&& i < STR_ARRAY_MAX_ITER) {
handle_cstring(ii, ordinal, SYSARG_READ, id, (app_pc)str, 0, NULL, false);
i++;
}
}
/* XXX i#1440: share w/ Linux */
static bool
handle_strarray_access(sysarg_iter_info_t *ii, const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
char id[16];
dr_snprintf(id, BUFFER_SIZE_ELEMENTS(id), "%s%d", "parameter #", arg_info->param);
NULL_TERMINATE_BUFFER(id);
check_strarray(ii, (char **)start, arg_info->param, id);
return true; /* check_strarray checks whole array */
}
static bool
os_handle_syscall_arg_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
if (!TEST(SYSARG_COMPLEX_TYPE, arg_info->flags))
return false;
switch (arg_info->misc) {
case SYSARG_TYPE_CSTRING:
return handle_cstring_access(ii, arg_info, start, size);
case DRSYS_TYPE_CSTRARRAY:
return handle_strarray_access(ii, arg_info, start, size);
/* FIXME i#1440: add more handling -- probably also want
* SYSARG_TYPE_SOCKADDR? Share w/ Linux?
*/
}
return false;
}
bool
os_handle_pre_syscall_arg_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
return os_handle_syscall_arg_access(ii, arg_info, start, size);
}
bool
os_handle_post_syscall_arg_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
return os_handle_syscall_arg_access(ii, arg_info, start, size);
}
/***************************************************************************
* TOP_LEVEL
*/
/* Table that maps syscall names to numbers. Payload points at num in syscall_info_*
* tables.
*/
#define NAME2NUM_TABLE_HASH_BITS 10 /* <500 of them */
static hashtable_t name2num_table;
drmf_status_t
drsyscall_os_init(void *drcontext)
{
uint i;
hashtable_init_ex(&systable, SYSTABLE_HASH_BITS, HASH_INTPTR, false/*!strdup*/,
false/*!synch*/, NULL, sysnum_hash, sysnum_cmp);
/* We initialize & leave secondary_systable empty to be in sync with our
* Windows & Linux solutions. Xref i#1438 i#1549.
*/
hashtable_init_ex(&secondary_systable, SECONDARY_SYSTABLE_HASH_BITS,
HASH_INTPTR, false/*!strdup*/, false/*!synch*/, NULL,
sysnum_hash, sysnum_cmp);
hashtable_init(&name2num_table, NAME2NUM_TABLE_HASH_BITS, HASH_STRING,
false/*!strdup*/);
dr_recurlock_lock(systable_lock);
for (i = 0; i < count_syscall_info_bsd; i++) {
#if defined(MACOS) && defined(X64)
/* We want to use SYS_ enums in the table so we could add the BSD marker
* here via:
* syscall_info_bsd[i].num.number |= SYSCALL_NUM_MARKER_BSD;
* however, DR is removing the marker from the numbers we see, so we leave it
* off. XXX: Will a user look at raw syscalls and include it?
*/
#endif
IF_DEBUG(bool ok =)
hashtable_add(&systable, (void *) &syscall_info_bsd[i].num,
(void *) &syscall_info_bsd[i]);
ASSERT(ok, "no dups");
IF_DEBUG(ok =)
hashtable_add(&name2num_table, (void *) syscall_info_bsd[i].name,
(void *) &syscall_info_bsd[i].num);
ASSERT(ok || strcmp(syscall_info_bsd[i].name, "ni_syscall") == 0, "no dups");
}
dr_recurlock_unlock(systable_lock);
return DRMF_SUCCESS;
}
void
drsyscall_os_exit(void)
{
hashtable_delete(&systable);
hashtable_delete(&secondary_systable);
hashtable_delete(&name2num_table);
}
void
drsyscall_os_thread_init(void *drcontext)
{
}
void
drsyscall_os_thread_exit(void *drcontext)
{
}
void
drsyscall_os_module_load(void *drcontext, const module_data_t *info, bool loaded)
{
}
bool
os_syscall_get_num(const char *name, drsys_sysnum_t *num_out DR_PARAM_OUT)
{
drsys_sysnum_t *num = (drsys_sysnum_t *)
hashtable_lookup(&name2num_table, (void *)name);
if (num != NULL) {
*num_out = *num;
return true;
}
return false;
}
/* Either sets arg->reg to DR_REG_NULL and sets arg->start_addr, or sets arg->reg
* to non-DR_REG_NULL
*/
void
drsyscall_os_get_sysparam_location(cls_syscall_t *pt, uint argnum, drsys_arg_t *arg)
{
#ifdef X64
switch (argnum) {
case 0: arg->reg = DR_REG_RDI;
case 1: arg->reg = DR_REG_RSI;
case 2: arg->reg = DR_REG_RDX;
case 3: arg->reg = DR_REG_R10; /* rcx = retaddr for OP_syscall */
case 4: arg->reg = DR_REG_R8;
case 5: arg->reg = DR_REG_R9;
default: arg->reg = DR_REG_NULL; /* error */
}
arg->start_addr = NULL;
#else
/* Args are on stack, past retaddr from syscall wrapper */
arg->reg = DR_REG_NULL;
arg->start_addr = (app_pc) (((reg_t *)arg->mc->esp) + 1/*retaddr*/ + argnum);
#endif
}
drmf_status_t
drsys_syscall_type(drsys_syscall_t *syscall, drsys_syscall_type_t *type DR_PARAM_OUT)
{
if (syscall == NULL || type == NULL)
return DRMF_ERROR_INVALID_PARAMETER;
*type = DRSYS_SYSCALL_TYPE_KERNEL;
return DRMF_SUCCESS;
}
bool
os_syscall_succeeded(drsys_sysnum_t sysnum, syscall_info_t *info, cls_syscall_t *pt)
{
if (TEST(SYSCALL_NUM_MARKER_MACH, sysnum.number)) {
/* FIXME i#1440: Mach syscalls vary (for some KERN_SUCCESS=0 is success,
* for others that return mach_port_t 0 is failure (I think?).
*/
return ((ptr_int_t)pt->mc.xax >= 0);
} else
return !TEST(EFLAGS_CF, pt->mc.xflags);
}
bool
os_syscall_succeeded_custom(drsys_sysnum_t sysnum, syscall_info_t *info,
cls_syscall_t *pt)
{
return false;
}