Rework syscall structure lookups.

Avoid always using an O(n^2) loop over known syscall structures with
strcmp() on each system call.  Instead, use a per-ABI cache indexed by
the system call number. The first 1024 system calls (which should cover
all of the normal system calls in currently-supported ABIs) use a flat array
indexed by the system call number to find system call structure.  For other
system calls, a linked list of structures storing an integer to structure
mapping is stored in the ABI.  The linked list isn't very smart, but it
should only be used by buggy applications invoking unknown system calls.

This also fixes handling of unknown system calls which currently trigger
a NULL pointer dereference.

Reviewed by:	kib
MFC after:	2 weeks
This commit is contained in:
jhb 2016-12-06 00:39:00 +00:00
parent 930e9ed67b
commit d833056000
19 changed files with 141 additions and 46 deletions

View File

@ -80,7 +80,9 @@ static struct procabi aarch64_cloudabi64 = {
"CloudABI ELF64",
SYSDECODE_ABI_CLOUDABI64,
aarch64_cloudabi64_fetch_args,
aarch64_cloudabi64_fetch_retval
aarch64_cloudabi64_fetch_retval,
STAILQ_HEAD_INITIALIZER(aarch64_cloudabi64.extra_syscalls),
{ NULL }
};
PROCABI(aarch64_cloudabi64);

View File

@ -102,7 +102,9 @@ static struct procabi aarch64_freebsd = {
"FreeBSD ELF64",
SYSDECODE_ABI_FREEBSD,
aarch64_fetch_args,
aarch64_fetch_retval
aarch64_fetch_retval,
STAILQ_HEAD_INITIALIZER(aarch64_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(aarch64_freebsd);

View File

@ -89,7 +89,9 @@ static struct procabi amd64_cloudabi64 = {
"CloudABI ELF64",
SYSDECODE_ABI_CLOUDABI64,
amd64_cloudabi64_fetch_args,
amd64_cloudabi64_fetch_retval
amd64_cloudabi64_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_cloudabi64.extra_syscalls),
{ NULL }
};
PROCABI(amd64_cloudabi64);

View File

@ -124,7 +124,9 @@ static struct procabi amd64_freebsd = {
"FreeBSD ELF64",
SYSDECODE_ABI_FREEBSD,
amd64_fetch_args,
amd64_fetch_retval
amd64_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(amd64_freebsd);

View File

@ -120,7 +120,9 @@ static struct procabi amd64_freebsd32 = {
"FreeBSD ELF32",
SYSDECODE_ABI_FREEBSD32,
amd64_freebsd32_fetch_args,
amd64_freebsd32_fetch_retval
amd64_freebsd32_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_freebsd32.extra_syscalls),
{ NULL }
};
PROCABI(amd64_freebsd32);
@ -129,7 +131,9 @@ static struct procabi amd64_freebsd32_aout = {
"FreeBSD a.out",
SYSDECODE_ABI_FREEBSD32,
amd64_freebsd32_fetch_args,
amd64_freebsd32_fetch_retval
amd64_freebsd32_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_freebsd32.extra_syscalls),
{ NULL }
};
PROCABI(amd64_freebsd32_aout);

View File

@ -99,7 +99,9 @@ static struct procabi amd64_linux = {
"Linux ELF64",
SYSDECODE_ABI_LINUX,
amd64_linux_fetch_args,
amd64_linux_fetch_retval
amd64_linux_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_linux.extra_syscalls),
{ NULL }
};
PROCABI(amd64_linux);

View File

@ -109,7 +109,9 @@ static struct procabi amd64_linux32 = {
"Linux ELF32",
SYSDECODE_ABI_LINUX32,
amd64_linux32_fetch_args,
amd64_linux32_fetch_retval
amd64_linux32_fetch_retval,
STAILQ_HEAD_INITIALIZER(amd64_linux32.extra_syscalls),
{ NULL }
};
PROCABI(amd64_linux32);

View File

@ -131,7 +131,9 @@ static struct procabi arm_freebsd = {
"FreeBSD ELF32",
SYSDECODE_ABI_FREEBSD,
arm_fetch_args,
arm_fetch_retval
arm_fetch_retval,
STAILQ_HEAD_INITIALIZER(arm_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(arm_freebsd);

View File

@ -113,7 +113,9 @@ static struct procabi i386_freebsd = {
"FreeBSD ELF32",
SYSDECODE_ABI_FREEBSD,
i386_fetch_args,
i386_fetch_retval
i386_fetch_retval,
STAILQ_HEAD_INITIALIZER(i386_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(i386_freebsd);
@ -122,7 +124,9 @@ static struct procabi i386_freebsd_aout = {
"FreeBSD a.out",
SYSDECODE_ABI_FREEBSD,
i386_fetch_args,
i386_fetch_retval
i386_fetch_retval,
STAILQ_HEAD_INITIALIZER(i386_freebsd_aout.extra_syscalls),
{ NULL }
};
PROCABI(i386_freebsd_aout);

View File

@ -106,7 +106,9 @@ static struct procabi i386_linux = {
"Linux ELF",
SYSDECODE_ABI_LINUX,
i386_linux_fetch_args,
i386_linux_fetch_retval
i386_linux_fetch_retval,
STAILQ_HEAD_INITIALIZER(i386_linux.extra_syscalls),
{ NULL }
};
PROCABI(i386_linux);

View File

@ -134,7 +134,9 @@ static struct procabi mips_freebsd = {
#endif
SYSDECODE_ABI_FREEBSD,
mips_fetch_args,
mips_fetch_retval
mips_fetch_retval,
STAILQ_HEAD_INITIALIZER(mips_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(mips_freebsd);

View File

@ -115,7 +115,9 @@ static struct procabi powerpc_freebsd = {
"FreeBSD ELF32",
SYSDECODE_ABI_FREEBSD,
powerpc_fetch_args,
powerpc_fetch_retval
powerpc_fetch_retval,
STAILQ_HEAD_INITIALIZER(powerpc_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(powerpc_freebsd);

View File

@ -111,7 +111,9 @@ static struct procabi powerpc64_freebsd = {
"FreeBSD ELF64",
SYSDECODE_ABI_FREEBSD,
powerpc64_fetch_args,
powerpc64_fetch_retval
powerpc64_fetch_retval,
STAILQ_HEAD_INITIALIZER(powerpc64_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(powerpc64_freebsd);

View File

@ -120,7 +120,9 @@ static struct procabi powerpc64_freebsd32 = {
"FreeBSD ELF32",
SYSDECODE_ABI_FREEBSD32,
powerpc64_freebsd32_fetch_args,
powerpc64_freebsd32_fetch_retval
powerpc64_freebsd32_fetch_retval,
STAILQ_HEAD_INITIALIZER(powerpc64_freebsd32.extra_syscalls),
{ NULL }
};
PROCABI(powerpc64_freebsd32);

View File

@ -344,7 +344,7 @@ alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl)
assert(t->in_syscall == 0);
assert(t->cs.number == 0);
assert(t->cs.name == NULL);
assert(t->cs.sc == NULL);
assert(t->cs.nargs == 0);
for (i = 0; i < nitems(t->cs.s_args); i++)
assert(t->cs.s_args[i] == NULL);
@ -378,12 +378,11 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t,
return;
}
t->cs.name = sysdecode_syscallname(t->proc->abi->abi, t->cs.number);
if (t->cs.name == NULL)
sc = get_syscall(t, t->cs.number, narg);
if (sc->unknown)
fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n",
t->proc->abi->type, t->cs.number);
sc = get_syscall(t->cs.name, narg);
t->cs.nargs = sc->nargs;
assert(sc->nargs <= nitems(t->cs.s_args));
@ -396,25 +395,22 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t,
* now. This doesn't currently support arguments that are
* passed in *and* out, however.
*/
if (t->cs.name != NULL) {
#if DEBUG
fprintf(stderr, "syscall %s(", t->cs.name);
fprintf(stderr, "syscall %s(", sc->name);
#endif
for (i = 0; i < t->cs.nargs; i++) {
for (i = 0; i < t->cs.nargs; i++) {
#if DEBUG
fprintf(stderr, "0x%lx%s", sc ?
t->cs.args[sc->args[i].offset] : t->cs.args[i],
i < (t->cs.nargs - 1) ? "," : "");
fprintf(stderr, "0x%lx%s", t->cs.args[sc->args[i].offset],
i < (t->cs.nargs - 1) ? "," : "");
#endif
if (!(sc->args[i].type & OUT)) {
t->cs.s_args[i] = print_arg(&sc->args[i],
t->cs.args, 0, info);
}
if (!(sc->args[i].type & OUT)) {
t->cs.s_args[i] = print_arg(&sc->args[i],
t->cs.args, 0, info);
}
#if DEBUG
fprintf(stderr, ")\n");
#endif
}
#if DEBUG
fprintf(stderr, ")\n");
#endif
clock_gettime(CLOCK_REALTIME, &t->before);
}

View File

@ -118,7 +118,9 @@ static struct procabi sparc64_freebsd = {
"FreeBSD ELF64",
SYSDECODE_ABI_FREEBSD,
sparc64_fetch_args,
sparc64_fetch_retval
sparc64_fetch_retval,
STAILQ_HEAD_INITIALIZER(sparc64_freebsd.extra_syscalls),
{ NULL }
};
PROCABI(sparc64_freebsd);

View File

@ -72,9 +72,10 @@ struct syscall {
struct timespec time; /* Time spent for this call */
int ncalls; /* Number of calls */
int nerror; /* Number of calls that returned with error */
bool unknown; /* Unknown system call */
};
struct syscall *get_syscall(const char *, int nargs);
struct syscall *get_syscall(struct threadinfo *, u_int, u_int);
char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *);
/*

View File

@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
@ -819,21 +820,66 @@ init_syscalls(void)
for (sc = decoded_syscalls; sc->name != NULL; sc++)
STAILQ_INSERT_HEAD(&syscalls, sc, entries);
}
static struct syscall *
find_syscall(struct procabi *abi, u_int number)
{
struct extra_syscall *es;
if (number < nitems(abi->syscalls))
return (abi->syscalls[number]);
STAILQ_FOREACH(es, &abi->extra_syscalls, entries) {
if (es->number == number)
return (es->sc);
}
return (NULL);
}
static void
add_syscall(struct procabi *abi, u_int number, struct syscall *sc)
{
struct extra_syscall *es;
if (number < nitems(abi->syscalls)) {
assert(abi->syscalls[number] == NULL);
abi->syscalls[number] = sc;
} else {
es = malloc(sizeof(*es));
es->sc = sc;
es->number = number;
STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries);
}
}
/*
* If/when the list gets big, it might be desirable to do it
* as a hash table or binary search.
*/
struct syscall *
get_syscall(const char *name, int nargs)
get_syscall(struct threadinfo *t, u_int number, u_int nargs)
{
struct syscall *sc;
int i;
const char *name;
char *new_name;
u_int i;
if (name == NULL)
return (NULL);
STAILQ_FOREACH(sc, &syscalls, entries)
if (strcmp(name, sc->name) == 0)
sc = find_syscall(t->proc->abi, number);
if (sc != NULL)
return (sc);
name = sysdecode_syscallname(t->proc->abi->abi, number);
if (name == NULL) {
asprintf(&new_name, "#%d", number);
name = new_name;
} else
new_name = NULL;
STAILQ_FOREACH(sc, &syscalls, entries) {
if (strcmp(name, sc->name) == 0) {
add_syscall(t->proc->abi, number, sc);
free(new_name);
return (sc);
}
}
/* It is unknown. Add it into the list. */
#if DEBUG
@ -842,7 +888,9 @@ get_syscall(const char *name, int nargs)
#endif
sc = calloc(1, sizeof(struct syscall));
sc->name = strdup(name);
sc->name = name;
if (new_name != NULL)
sc->unknown = true;
sc->ret_type = 1;
sc->nargs = nargs;
for (i = 0; i < nargs; i++) {
@ -851,6 +899,7 @@ get_syscall(const char *name, int nargs)
sc->args[i].type = LongHex;
}
STAILQ_INSERT_HEAD(&syscalls, sc, entries);
add_syscall(t->proc->abi, number, sc);
return (sc);
}
@ -1866,7 +1915,7 @@ print_syscall(struct trussinfo *trussinfo)
t = trussinfo->curthread;
name = t->cs.name;
name = t->cs.sc->name;
nargs = t->cs.nargs;
s_args = t->cs.s_args;

View File

@ -38,13 +38,29 @@
#define DISPLAYTIDS 0x00000080
struct procinfo;
struct syscall;
struct trussinfo;
/*
* The lookup of normal system calls are optimized by using a fixed
* array for the first 1024 system calls that can be indexed directly.
* Unknown system calls with other IDs are stored in a linked list.
*/
#define SYSCALL_NORMAL_COUNT 1024
struct extra_syscall {
STAILQ_ENTRY(extra_syscall) entries;
struct syscall *sc;
u_int number;
};
struct procabi {
const char *type;
enum sysdecode_abi abi;
int (*fetch_args)(struct trussinfo *, u_int);
int (*fetch_retval)(struct trussinfo *, long *, int *);
STAILQ_HEAD(, extra_syscall) extra_syscalls;
struct syscall *syscalls[SYSCALL_NORMAL_COUNT];
};
#define PROCABI(abi) DATA_SET(procabi, abi)
@ -64,10 +80,9 @@ struct procabi {
*/
struct current_syscall {
struct syscall *sc;
const char *name;
int number;
unsigned long args[10];
unsigned int number;
unsigned int nargs;
unsigned long args[10];
char *s_args[10]; /* the printable arguments */
};