Compare commits

..

No commits in common. "esyr/looping_threads" and "master" have entirely different histories.

31 changed files with 246 additions and 822 deletions

View File

@ -168,7 +168,6 @@ strace_SOURCES = \
linux/asm_stat.h \
linux/x32/asm_stat.h \
linux/x86_64/asm_stat.h \
list.h \
listen.c \
lookup_dcookie.c \
loop.c \

20
defs.h
View File

@ -37,7 +37,6 @@
# include "error_prints.h"
# include "gcc_compat.h"
# include "kernel_types.h"
# include "list.h"
# include "macros.h"
# include "mpers_type.h"
# include "string_to_uint.h"
@ -207,9 +206,7 @@ struct tcb {
const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */
void *_priv_data; /* Private data for syscall decoding functions */
void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
const struct_sysent *s_ent; /* sysent[scno] or a stub struct for bad
* scno. Use tcp_sysent() macro for access.
*/
const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad scno */
const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" msg */
struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
struct timespec stime; /* System time usage as of last process wait */
@ -219,15 +216,6 @@ struct tcb {
struct mmap_cache_t *mmap_cache;
/*
* Data that is stored during process wait traversal.
* We use indices as the actual data is stored in an array
* that is realloc'ed in runtime.
*/
size_t wait_data_idx;
struct list_item wait_list;
# ifdef HAVE_LINUX_KVM_H
struct vcpu_info *vcpu_info_list;
# endif
@ -297,10 +285,6 @@ struct tcb {
# define syscall_delayed(tcp) ((tcp)->flags & TCB_DELAYED)
# define syscall_tampered_nofail(tcp) ((tcp)->flags & TCB_TAMPERED_NO_FAIL)
extern const struct_sysent stub_sysent;
# define tcp_sysent(tcp) (tcp->s_ent ?: &stub_sysent)
# define n_args(tcp) (tcp_sysent(tcp)->nargs)
# include "xlat.h"
extern const struct xlat addrfams[];
@ -443,8 +427,6 @@ extern bool get_instruction_pointer(struct tcb *, kernel_ulong_t *);
extern bool get_stack_pointer(struct tcb *, kernel_ulong_t *);
extern void print_instruction_pointer(struct tcb *);
extern void print_syscall_resume(struct tcb *tcp);
extern int syscall_entering_decode(struct tcb *);
extern int syscall_entering_trace(struct tcb *, unsigned int *);
extern void syscall_entering_finish(struct tcb *, int);

2
ipc.c
View File

@ -21,7 +21,7 @@ SYS_FUNC(ipc)
printxval_u(ipccalls, call, NULL);
unsigned int i;
for (i = 1; i < n_args(tcp); ++i)
for (i = 1; i < tcp->s_ent->nargs; ++i)
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
return RVAL_DECODED;

View File

@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
{
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, REG_A0+i, &tcp->u_arg[i]) < 0)
return -1;
return 1;

View File

@ -14,7 +14,7 @@ arch_get_syscall_args(struct tcb *tcp)
};
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, argreg[i], &tcp->u_arg[i]) < 0)
return -1;
return 1;

View File

@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
{
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, PT_GR26-4*i, &tcp->u_arg[i]) < 0)
return -1;
return 1;

View File

@ -18,7 +18,7 @@ arch_get_syscall_args(struct tcb *tcp)
unsigned long *out0 = ia64_rse_skip_regs(rbs_end, -sof + sol);
unsigned int i;
for (i = 0; i < n_args(tcp); ++i) {
for (i = 0; i < tcp->s_ent->nargs; ++i) {
if (umove(tcp,
(unsigned long) ia64_rse_skip_regs(out0, i),
&tcp->u_arg[i]) < 0) {

View File

@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
{
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, (5 + i) * 4, &tcp->u_arg[i]) < 0)
return -1;
return 1;

View File

@ -21,16 +21,16 @@ arch_get_syscall_args(struct tcb *tcp)
tcp->u_arg[1] = mips_REG_A1;
tcp->u_arg[2] = mips_REG_A2;
tcp->u_arg[3] = mips_REG_A3;
if (n_args(tcp) > 4
if (tcp->s_ent->nargs > 4
&& umoven(tcp, mips_REG_SP + 4 * sizeof(tcp->u_arg[0]),
(n_args(tcp) - 4) * sizeof(tcp->u_arg[0]),
(tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]),
&tcp->u_arg[4]) < 0) {
/*
* Let's proceed with the first 4 arguments
* instead of reporting the failure.
*/
memset(&tcp->u_arg[4], 0,
(n_args(tcp) - 4) * sizeof(tcp->u_arg[0]));
(tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]));
}
#else
# error unsupported mips abi
@ -54,7 +54,7 @@ decode_syscall_subcall(struct tcb *tcp)
* and sync_file_range) requires additional code,
* see linux/mips/get_syscall_args.c
*/
if (n_args(tcp) == MAX_ARGS) {
if (tcp->s_ent->nargs == MAX_ARGS) {
if (umoven(tcp,
mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
sizeof(tcp->u_arg[0]),

View File

@ -19,7 +19,7 @@ arch_get_syscall_args(struct tcb *tcp)
};
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, syscall_regs[i], &tcp->u_arg[i]) < 0)
return -1;
return 1;

View File

@ -13,7 +13,7 @@ arch_get_syscall_args(struct tcb *tcp)
static const int syscall_regs[MAX_ARGS] = { 2, 3, 4, 5, 6, 7 };
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, REG_GENERAL(syscall_regs[i]),
&tcp->u_arg[i]) < 0)
return -1;

View File

@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
{
if (x86_io.iov_len != sizeof(i386_regs)) {
/* x86-64 or x32 ABI */
if (tcp_sysent(tcp)->sys_flags & COMPAT_SYSCALL_TYPES) {
if (tcp->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
/*
* X32 compat syscall: zero-extend from 32 bits.
* Use truncate_klong_to_current_wordsize(tcp->u_arg[N])

View File

@ -20,7 +20,7 @@ arch_get_syscall_args(struct tcb *tcp)
};
unsigned int i;
for (i = 0; i < n_args(tcp); ++i)
for (i = 0; i < tcp->s_ent->nargs; ++i)
if (upeek(tcp, xtensaregs[i], &tcp->u_arg[i]) < 0)
return -1;
return 1;

295
list.h
View File

@ -1,295 +0,0 @@
/*
* Some simple implementation of lists similar to the one used in the kernel.
*
* Copyright (c) 2016-2018 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*/
#ifndef STRACE_LIST_H
#define STRACE_LIST_H
/*
* struct list_item and related macros and functions provide an interface
* for manipulating and iterating linked lists. In order to have list
* associated with its payload, struct list_item has to be embedded into
* a structure type representing payload, and (optionally) an additional
* struct list_item should be added somewhere as a starting point for list
* iteration (creating a list with a designated head). A situation where
* no designated head exists, and each embedded struct list_head is considered
* a head (i.e. starting point for list iteration), is also possible.
*
* List head has to be initialised with list_init() call. Statically allocated
* list heads can also be defined with an EMPTY_LIST() macro.
*
* In order to get a pointer to list item from a struct list_item, list_elem
* macro is used.
*
* When a designated head is used, list_head() and list_tail() can be used
* for getting pointer to the first and the last list item, respectively.
*
* list_next() and list_prev() macros can be used for obtaining pointers
* to the next and the previous items in the list, respectively. Note that
* they do not perform additional checks for the validity of these pointers,
* so they have to be guarded with respective list_head/list_tail checks in case
* of lists with designated heads (where the list's head is not embedded withing
* a list item.
*
* list_{insert,append,remove,remove_tail,remove_head,replace} provide some
* basic means of list manipulation.
*
* list_foreach() and list_foreach_safe() are wrapper macros for simplifying
* iteration over a list, with the latter having an additional argument
* for storing temporary pointer, thus allowing list manipulations during
* its iteration.
*
* A simple example:
*
* struct my_struct {
* int a;
* struct list_item l1;
* struct list_item l2;
* };
*
* EMPTY_LIST(list_1); <--- Defining a designated head for list
*
* struct my_struct *item =
* calloc(1, sizeof(*item));
* list_init(&item->l2); <--- Initialising structure field that
* is used for lists without designated
* head.
* list_insert(&list_1, &item->l1); <--- Inserting an item into the list
*
* item = calloc(1, sizeof(*item));
* list_init(&item->l2);
*
* list_append(&(list_head(list_1, struct my_struct, l1)->l2), &item->l2);
*
* struct my_struct *cur = item; <--- Iteration over a headless list
* do {
* printf("%d\n", cur->a);
* } while ((cur = list_next(&cur, l2)) != item);
*
* struct my_struct *i;
* list_foreach(i, list_1, l1) { <--- Iteration over list_1 without list
* printf("%d\n", i->a); modification
* }
*
* struct my_struct *tmp; <--- Iteration with modification
* list_foreach_safe(i, list_1, l1, tmp) {
* list_remove(&i->l1);
* free(i);
* }
*
* See also:
* "Linux kernel design patterns - part 2", section "Linked Lists"
* https://lwn.net/Articles/336255/
*/
#include "macros.h"
struct list_item {
struct list_item *prev;
struct list_item *next;
};
/**
* Define an empty list head.
*
* @param l_ List head variable name.
*/
#define EMPTY_LIST(l_) struct list_item l_ = { &l_, &l_ }
/** Initialise an empty list. */
static inline void
list_init(struct list_item *l)
{
l->prev = l;
l->next = l;
}
/** Check whether list is empty. */
static inline bool
list_is_empty(struct list_item *l)
{
return (l->next == l) && (l->prev == l);
}
/**
* Convert a pointer to a struct list_item to a pointer to a list item.
*
* @param var Pointer to struct list_item.
* @param type Type of the list's item.
* @param field Name of the field that holds the respective struct list_item.
*/
#define list_elem(var, type, field) containerof((var), type, field)
/**
* Get the first element in a list.
*
* @param head Pointer to the list's head.
* @param type Type of the list's item.
* @param field Name of the field that holds the respective struct list_item.
*/
#define list_head(head, type, field) \
(list_is_empty(head) ? NULL : list_elem((head)->next, type, field))
/**
* Get the last element in a list.
*
* @param head Pointer to the list's head.
* @param type Type of the list's item.
* @param field Name of the field that holds the respective struct list_item.
*/
#define list_tail(head, type, field) \
(list_is_empty(head) ? NULL : list_elem((head)->prev, type, field))
/**
* Get the next element in a list.
*
* @param var Pointer to a list item.
* @param field Name of the field that holds the respective struct list_item.
*/
#define list_next(var, field) \
list_elem((var)->field.next, typeof(*(var)), field)
/**
* Get the previous element in a list.
*
* @param var Pointer to a list item.
* @param field Name of the field that holds the respective struct list_item.
*/
#define list_prev(var, field) \
list_elem((var)->field.prev, typeof(*(var)), field)
/**
* Insert an item into a list. The item is placed as a next list item
* to the head.
*/
static inline void
list_insert(struct list_item *head, struct list_item *item)
{
item->next = head->next;
item->prev = head;
head->next->prev = item;
head->next = item;
}
/**
* Insert an item into a list. The item is placed as a previous list item
* to the head.
*/
static inline void
list_append(struct list_item *head, struct list_item *item)
{
item->next = head;
item->prev = head->prev;
head->prev->next = item;
head->prev = item;
}
/** Remove an item from a list. */
static inline void
list_remove(struct list_item *item)
{
if (!item->next || !item->prev)
return;
item->prev->next = item->next;
item->next->prev = item->prev;
item->next = item->prev = item;
}
/**
* Remove the last element of a list.
*
* @param head Pointer to the list's head.
* @return Pointer to struct list_item removed from the list;
* or NULL, if the list is empty.
*/
static inline struct list_item *
list_remove_tail(struct list_item *head)
{
struct list_item *t = list_is_empty(head) ? NULL : head->prev;
if (t)
list_remove(t);
return t;
}
/**
* Remove the first element of a list.
*
* @param head Pointer to the list's head.
* @return Pointer to struct list_item removed from the list;
* or NULL, if the list is empty.
*/
static inline struct list_item *
list_remove_head(struct list_item *head)
{
struct list_item *h = list_is_empty(head) ? NULL : head->next;
if (h)
list_remove(h);
return h;
}
/** Replace an old struct list_item in a list with the new one. */
static inline void
list_replace(struct list_item *old, struct list_item *new)
{
new->next = old->next;
new->prev = old->prev;
old->prev->next = new;
old->next->prev = new;
old->next = old->prev = old;
}
/**
* List iteration wrapper for non-destructive operations.
*
* @param var_ Variable holding pointer to a current list item.
* @param head_ Pointer to the list's head.
* @param field_ Name of the field containing the respective struct list_item
* inside list items.
*/
#define list_foreach(var_, head_, field_) \
for (var_ = list_elem((head_)->next, typeof(*var_), field_); \
&(var_->field_) != (head_); var_ = list_next(var_, field_))
/**
* List iteration wrapper for destructive operations.
*
* @param var_ Variable holding pointer to a current list item.
* @param head_ Pointer to the list's head.
* @param field_ Name of the field containing the respective struct list_item
* inside list items.
* @param _tmp Temporary variable for storing pointer to the next item.
*/
#define list_foreach_safe(var_, head_, field_, _tmp) \
for (var_ = list_elem((head_)->next, typeof(*var_), field_), \
_tmp = list_elem((var_)->field_.next, typeof(*var_), field_); \
&var_->field_ != head_; var_ = _tmp, _tmp = list_next(_tmp, field_))
#endif /* !STRACE_LIST_H */

View File

@ -37,15 +37,6 @@
(offsetof(type_, member_) + sizeof(((type_ *)0)->member_))
# endif
#ifndef cast_ptr
# define cast_ptr(type, var) ((type) (uintptr_t) (const volatile void *) (var))
#endif
#ifndef containerof
# define containerof(x, s, m) \
cast_ptr(s *, (const volatile char *) (x) - offsetof(s, m))
#endif
static inline bool
is_filled(const char *ptr, char fill, size_t size)
{

View File

@ -167,7 +167,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
{
const struct_sysent *s;
s = tcp_sysent(tcp);
s = tcp->s_ent;
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
return false;

View File

@ -61,7 +61,7 @@ print_prctl_args(struct tcb *tcp, const unsigned int first)
{
unsigned int i;
for (i = first; i < n_args(tcp); ++i)
for (i = first; i < tcp->s_ent->nargs; ++i)
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
}

View File

@ -405,10 +405,10 @@ SYS_FUNC(sgetmask)
SYS_FUNC(sigsuspend)
{
#ifdef MIPS
print_sigset_addr_len(tcp, tcp->u_arg[n_args(tcp) - 1],
print_sigset_addr_len(tcp, tcp->u_arg[tcp->s_ent->nargs - 1],
current_wordsize);
#else
tprint_old_sigmask_val("", tcp->u_arg[n_args(tcp) - 1]);
tprint_old_sigmask_val("", tcp->u_arg[tcp->s_ent->nargs - 1]);
#endif
return RVAL_DECODED;

342
strace.c
View File

@ -143,17 +143,10 @@ static struct tcb *current_tcp;
struct tcb_wait_data {
enum trace_event te; /**< Event passed to dispatch_event() */
int status; /**< status, returned by wait4() */
unsigned long msg; /**< Value returned by PTRACE_GETEVENTMSG */
siginfo_t si; /**< siginfo, returned by PTRACE_GETSIGINFO */
};
static struct tcb **tcbtab;
/*
* Since the queueing of tracees stops as soon as wait4() returns EAGAIN,
* or at least two events for a single tracee, tab_wait_tab size shouldn't
* exceed tcbtabsize + 1.
*/
static struct tcb_wait_data *tcb_wait_tab;
static unsigned int nprocs;
static size_t tcbtabsize;
@ -731,9 +724,6 @@ expand_tcbtab(void)
for (tcb_ptr = tcbtab + old_tcbtabsize;
tcb_ptr < tcbtab + tcbtabsize; tcb_ptr++, newtcbs++)
*tcb_ptr = newtcbs;
tcb_wait_tab = xreallocarray(tcb_wait_tab, sizeof(*tcb_wait_tab),
tcbtabsize + 1);
}
static struct tcb *
@ -837,8 +827,6 @@ droptcb(struct tcb *tcp)
if (printing_tcp == tcp)
printing_tcp = NULL;
list_remove(&tcp->wait_list);
memset(tcp, 0, sizeof(*tcp));
}
@ -2054,8 +2042,10 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
{
FILE *fp;
struct tcb *execve_thread;
long old_pid = tcb_wait_tab[tcp->wait_data_idx].msg;
long old_pid = 0;
if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, &old_pid) < 0)
return tcp;
/* Avoid truncation in pid2tcb() param passing */
if (old_pid <= 0 || old_pid == pid)
return tcp;
@ -2191,7 +2181,12 @@ print_event_exit(struct tcb *tcp)
set_current_tcp(tcp);
}
print_syscall_resume(tcp);
if ((followfork < 2 && printing_tcp != tcp)
|| (tcp->flags & TCB_REPRINT)) {
tcp->flags &= ~TCB_REPRINT;
printleader(tcp);
tprintf("<... %s resumed>", tcp->s_ent->sys_name);
}
if (!(tcp->sys_func_rval & RVAL_DECODED)) {
/*
@ -2211,27 +2206,17 @@ print_event_exit(struct tcb *tcp)
static const struct tcb_wait_data *
next_event(void)
{
static struct tcb_wait_data wait_data;
int pid;
int status;
struct tcb *tcp;
struct tcb_wait_data *wd = &wait_data;
struct rusage ru;
if (interrupted)
return NULL;
struct tcb *tcp = NULL;
struct list_item *elem;
static EMPTY_LIST(pending_tcps);
if (!list_is_empty(&pending_tcps))
goto next_event_get_tcp;
static struct tcb *extra_tcp;
static size_t wait_extra_data_idx;
if (extra_tcp) {
tcp = extra_tcp;
extra_tcp = NULL;
tcp->wait_data_idx = wait_extra_data_idx;
debug_msg("dequeued extra event for pid %u", tcp->pid);
goto next_event_exit;
}
/*
* Used to exit simply when nprocs hits zero, but in this testcase:
* int main(void) { _exit(!!fork()); }
@ -2273,10 +2258,8 @@ next_event(void)
* then the system call will be interrupted and
* the expiration will be handled by the signal handler.
*/
int status;
struct rusage ru;
int pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
int wait_errno = errno;
pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
const int wait_errno = errno;
/*
* The window of opportunity to handle expirations
@ -2292,194 +2275,135 @@ next_event(void)
return NULL;
}
size_t wait_tab_pos = 0;
bool wait_nohang = false;
for (;;) {
struct tcb_wait_data *wd;
if (pid < 0) {
if (wait_errno == EINTR)
break;
if (wait_nohang)
break;
if (nprocs == 0 && wait_errno == ECHILD)
return NULL;
/*
* If nprocs > 0, ECHILD is not expected,
* treat it as any other error here:
*/
errno = wait_errno;
perror_msg_and_die("wait4(__WALL)");
if (pid < 0) {
if (wait_errno == EINTR) {
wd->te = TE_NEXT;
return wd;
}
if (nprocs == 0 && wait_errno == ECHILD)
return NULL;
/*
* If nprocs > 0, ECHILD is not expected,
* treat it as any other error here:
*/
errno = wait_errno;
perror_msg_and_die("wait4(__WALL)");
}
if (!pid)
break;
wd->status = status;
if (pid == popen_pid) {
if (!WIFSTOPPED(status))
popen_pid = 0;
break;
}
if (pid == popen_pid) {
if (!WIFSTOPPED(status))
popen_pid = 0;
wd->te = TE_NEXT;
return wd;
}
if (debug_flag)
print_debug_info(pid, status);
if (debug_flag)
print_debug_info(pid, status);
/* Look up 'pid' in our table. */
tcp = pid2tcb(pid);
/* Look up 'pid' in our table. */
tcp = pid2tcb(pid);
if (!tcp) {
tcp = maybe_allocate_tcb(pid, status);
if (!tcp) {
tcp = maybe_allocate_tcb(pid, status);
if (!tcp)
break;
wd->te = TE_NEXT;
return wd;
}
if (cflag) {
struct timespec stime = {
.tv_sec = ru.ru_stime.tv_sec,
.tv_nsec = ru.ru_stime.tv_usec * 1000
};
ts_sub(&tcp->dtime, &stime, &tcp->stime);
tcp->stime = stime;
}
if (wait_tab_pos > tcbtabsize)
error_func_msg_and_die("Wait data storage overflow "
"(wait_tab_pos %zu, nprocs %u, "
"tcbtabsize %zu)", wait_tab_pos,
nprocs, tcbtabsize);
wd = tcb_wait_tab + wait_tab_pos;
memset(wd, 0, sizeof(*wd));
if (WIFSIGNALED(status)) {
wd->te = TE_SIGNALLED;
} else if (WIFEXITED(status)) {
wd->te = TE_EXITED;
} else {
/*
* As WCONTINUED flag has not been specified to wait4,
* it cannot be WIFCONTINUED(status), so the only case
* that remains is WIFSTOPPED(status).
*/
const unsigned int sig = WSTOPSIG(status);
const unsigned int event = (unsigned int) status >> 16;
switch (event) {
case 0:
/*
* Is this post-attach SIGSTOP?
* Interestingly, the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happened to attach
* just before the process takes a signal.
*/
if (sig == SIGSTOP &&
(tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
debug_func_msg("ignored SIGSTOP on "
"pid %d", tcp->pid);
tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
wd->te = TE_RESTART;
} else if (sig == syscall_trap_sig) {
wd->te = TE_SYSCALL_STOP;
} else {
/*
* True if tracee is stopped by signal
* (as opposed to "tracee received
* signal").
* TODO: shouldn't we check for
* errno == EINVAL too?
* We can get ESRCH instead, you know...
*/
bool stopped = ptrace(PTRACE_GETSIGINFO,
pid, 0, &wd->si) < 0;
wd->te = stopped ? TE_GROUP_STOP
: TE_SIGNAL_DELIVERY_STOP;
}
break;
case PTRACE_EVENT_STOP:
/*
* PTRACE_INTERRUPT-stop or group-stop.
* PTRACE_INTERRUPT-stop has sig == SIGTRAP here.
*/
switch (sig) {
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
wd->te = TE_GROUP_STOP;
break;
default:
wd->te = TE_RESTART;
}
break;
case PTRACE_EVENT_EXEC:
/*
* TODO: shouldn't we check for
* errno == EINVAL here, too?
* We can get ESRCH instead, you know...
*/
if (ptrace(PTRACE_GETEVENTMSG, pid, NULL,
&wd->msg) < 0)
wd->msg = 0;
wd->te = TE_STOP_BEFORE_EXECVE;
break;
case PTRACE_EVENT_EXIT:
wd->te = TE_STOP_BEFORE_EXIT;
break;
default:
wd->te = TE_RESTART;
}
}
if (tcp->wait_list.next) {
wait_extra_data_idx = wait_tab_pos;
extra_tcp = tcp;
debug_func_msg("queued extra pid %d", tcp->pid);
} else {
tcp->wait_data_idx = wait_tab_pos;
list_append(&pending_tcps, &tcp->wait_list);
debug_func_msg("queued pid %d", tcp->pid);
}
wd->status = status;
wait_tab_pos++;
if (extra_tcp)
break;
pid = wait4(-1, &status, __WALL | WNOHANG, (cflag ? &ru : NULL));
wait_errno = errno;
wait_nohang = true;
}
next_event_get_tcp:
elem = list_remove_head(&pending_tcps);
if (!elem) {
memset(tcb_wait_tab, 0, sizeof(*tcb_wait_tab));
tcb_wait_tab->te = TE_NEXT;
return tcb_wait_tab;
} else {
tcp = list_elem(elem, struct tcb, wait_list);
debug_func_msg("dequeued pid %d", tcp->pid);
}
next_event_exit:
/* Is this the very first time we see this tracee stopped? */
if (tcp->flags & TCB_STARTUP)
startup_tcb(tcp);
clear_regs(tcp);
/* Set current output file */
set_current_tcp(tcp);
return tcb_wait_tab + tcp->wait_data_idx;
if (cflag) {
struct timespec stime = {
.tv_sec = ru.ru_stime.tv_sec,
.tv_nsec = ru.ru_stime.tv_usec * 1000
};
ts_sub(&tcp->dtime, &stime, &tcp->stime);
tcp->stime = stime;
}
if (WIFSIGNALED(status)) {
wd->te = TE_SIGNALLED;
return wd;
}
if (WIFEXITED(status)) {
wd->te = TE_EXITED;
return wd;
}
/*
* As WCONTINUED flag has not been specified to wait4,
* it cannot be WIFCONTINUED(status), so the only case
* that remains is WIFSTOPPED(status).
*/
/* Is this the very first time we see this tracee stopped? */
if (tcp->flags & TCB_STARTUP)
startup_tcb(tcp);
const unsigned int sig = WSTOPSIG(status);
const unsigned int event = (unsigned int) status >> 16;
switch (event) {
case 0:
/*
* Is this post-attach SIGSTOP?
* Interestingly, the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happened to attach
* just before the process takes a signal.
*/
if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
debug_func_msg("ignored SIGSTOP on pid %d", tcp->pid);
tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
wd->te = TE_RESTART;
} else if (sig == syscall_trap_sig) {
wd->te = TE_SYSCALL_STOP;
} else {
memset(&wd->si, 0, sizeof(wd->si));
/*
* True if tracee is stopped by signal
* (as opposed to "tracee received signal").
* TODO: shouldn't we check for errno == EINVAL too?
* We can get ESRCH instead, you know...
*/
bool stopped = ptrace(PTRACE_GETSIGINFO, pid, 0, &wd->si) < 0;
wd->te = stopped ? TE_GROUP_STOP : TE_SIGNAL_DELIVERY_STOP;
}
break;
case PTRACE_EVENT_STOP:
/*
* PTRACE_INTERRUPT-stop or group-stop.
* PTRACE_INTERRUPT-stop has sig == SIGTRAP here.
*/
switch (sig) {
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
wd->te = TE_GROUP_STOP;
break;
default:
wd->te = TE_RESTART;
}
break;
case PTRACE_EVENT_EXEC:
wd->te = TE_STOP_BEFORE_EXECVE;
break;
case PTRACE_EVENT_EXIT:
wd->te = TE_STOP_BEFORE_EXIT;
break;
default:
wd->te = TE_RESTART;
}
return wd;
}
static int

View File

@ -341,7 +341,7 @@ decode_ipc_subcall(struct tcb *tcp)
tcp->qual_flg = qual_flags(tcp->scno);
tcp->s_ent = &sysent[tcp->scno];
const unsigned int n = n_args(tcp);
const unsigned int n = tcp->s_ent->nargs;
unsigned int i;
for (i = 0; i < n; i++)
tcp->u_arg[i] = tcp->u_arg[i + 1];
@ -361,7 +361,7 @@ dumpio(struct tcb *tcp)
return;
if (is_number_in_set(fd, write_set)) {
switch (tcp_sysent(tcp)->sen) {
switch (tcp->s_ent->sen) {
case SEN_write:
case SEN_pwrite:
case SEN_send:
@ -388,7 +388,7 @@ dumpio(struct tcb *tcp)
return;
if (is_number_in_set(fd, read_set)) {
switch (tcp_sysent(tcp)->sen) {
switch (tcp->s_ent->sen) {
case SEN_read:
case SEN_pread:
case SEN_recv:
@ -548,9 +548,10 @@ syscall_entering_decode(struct tcb *tcp)
int res = get_scno(tcp);
if (res == 0)
return res;
int scno_good = res;
if (res != 1 || (res = get_syscall_args(tcp)) != 1) {
printleader(tcp);
tprintf("%s(", tcp_sysent(tcp)->sys_name);
tprintf("%s(", scno_good == 1 ? tcp->s_ent->sys_name : "????");
/*
* " <unavailable>" will be added later by the code which
* detects ptrace errors.
@ -562,7 +563,7 @@ syscall_entering_decode(struct tcb *tcp)
|| defined SYS_socket_subcall \
|| defined SYS_syscall_subcall
for (;;) {
switch (tcp_sysent(tcp)->sen) {
switch (tcp->s_ent->sen) {
# ifdef SYS_ipc_subcall
case SEN_ipc:
decode_ipc_subcall(tcp);
@ -576,7 +577,7 @@ syscall_entering_decode(struct tcb *tcp)
# ifdef SYS_syscall_subcall
case SEN_syscall:
decode_syscall_subcall(tcp);
if (tcp_sysent(tcp)->sen != SEN_syscall)
if (tcp->s_ent->sen != SEN_syscall)
continue;
break;
# endif
@ -598,10 +599,12 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
*/
tcp->qual_flg &= ~QUAL_INJECT;
switch (tcp_sysent(tcp)->sen) {
switch (tcp->s_ent->sen) {
case SEN_execve:
case SEN_execveat:
#if defined SPARC || defined SPARC64
case SEN_execv:
#endif
/*
* First exec* syscall makes the log visible.
*/
@ -630,14 +633,14 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
#ifdef ENABLE_STACKTRACE
if (stack_trace_enabled) {
if (tcp_sysent(tcp)->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
unwind_tcb_capture(tcp);
}
#endif
printleader(tcp);
tprintf("%s(", tcp_sysent(tcp)->sys_name);
int res = raw(tcp) ? printargs(tcp) : tcp_sysent(tcp)->sys_func(tcp);
tprintf("%s(", tcp->s_ent->sys_name);
int res = raw(tcp) ? printargs(tcp) : tcp->s_ent->sys_func(tcp);
fflush(tcp->outf);
return res;
}
@ -667,7 +670,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
if ((Tflag || cflag) && !filtered(tcp))
clock_gettime(CLOCK_MONOTONIC, pts);
if (tcp_sysent(tcp)->sys_flags & MEMORY_MAPPING_CHANGE)
if (tcp->s_ent->sys_flags & MEMORY_MAPPING_CHANGE)
mmap_notify_report(tcp);
if (filtered(tcp))
@ -685,26 +688,6 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
return get_syscall_result(tcp);
}
void
print_syscall_resume(struct tcb *tcp)
{
/* If not in -ff mode, and printing_tcp != tcp,
* then the log currently does not end with output
* of _our syscall entry_, but with something else.
* We need to say which syscall's return is this.
*
* Forced reprinting via TCB_REPRINT is used only by
* "strace -ff -oLOG test/threaded_execve" corner case.
* It's the only case when -ff mode needs reprinting.
*/
if ((followfork < 2 && printing_tcp != tcp)
|| (tcp->flags & TCB_REPRINT)) {
tcp->flags &= ~TCB_REPRINT;
printleader(tcp);
tprintf("<... %s resumed>", tcp_sysent(tcp)->sys_name);
}
}
int
syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
{
@ -718,7 +701,20 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
}
}
print_syscall_resume(tcp);
/* If not in -ff mode, and printing_tcp != tcp,
* then the log currently does not end with output
* of _our syscall entry_, but with something else.
* We need to say which syscall's return is this.
*
* Forced reprinting via TCB_REPRINT is used only by
* "strace -ff -oLOG test/threaded_execve" corner case.
* It's the only case when -ff mode needs reprinting.
*/
if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) {
tcp->flags &= ~TCB_REPRINT;
printleader(tcp);
tprintf("<... %s resumed> ", tcp->s_ent->sys_name);
}
printing_tcp = tcp;
tcp->s_prev_ent = NULL;
@ -749,7 +745,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
if (tcp->sys_func_rval & RVAL_DECODED)
sys_res = tcp->sys_func_rval;
else
sys_res = tcp_sysent(tcp)->sys_func(tcp);
sys_res = tcp->s_ent->sys_func(tcp);
}
tprints(") ");
@ -1236,14 +1232,6 @@ get_syscall_regs(struct tcb *tcp)
return get_regs(tcp);
}
const struct_sysent stub_sysent = {
.nargs = MAX_ARGS,
.sys_flags = MEMORY_MAPPING_CHANGE,
.sen = SEN_printargs,
.sys_func = printargs,
.sys_name = "????",
};
/*
* Returns:
* 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
@ -1255,10 +1243,6 @@ const struct_sysent stub_sysent = {
int
get_scno(struct tcb *tcp)
{
tcp->scno = -1;
tcp->s_ent = NULL;
tcp->qual_flg = QUAL_RAW | DEFAULT_QUAL_FLAGS;
if (get_syscall_regs(tcp) < 0)
return -1;
@ -1286,7 +1270,9 @@ get_scno(struct tcb *tcp)
struct sysent_buf *s = xcalloc(1, sizeof(*s));
s->tcp = tcp;
s->ent = stub_sysent;
s->ent.nargs = MAX_ARGS;
s->ent.sen = SEN_printargs;
s->ent.sys_func = printargs;
s->ent.sys_name = s->buf;
xsprintf(s->buf, "syscall_%#" PRI_klx, shuffle_scno(tcp->scno));
@ -1317,7 +1303,7 @@ get_syscall_args(struct tcb *tcp)
for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
tcp->u_arg[i] = ptrace_sci.entry.args[i];
#if SUPPORTED_PERSONALITIES > 1
if (tcp_sysent(tcp)->sys_flags & COMPAT_SYSCALL_TYPES) {
if (tcp->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
tcp->u_arg[i] = (uint32_t) tcp->u_arg[i];
}
@ -1344,7 +1330,7 @@ get_syscall_result(struct tcb *tcp)
if (get_syscall_result_regs(tcp) < 0)
return -1;
get_error(tcp,
(!(tcp_sysent(tcp)->sys_flags & SYSCALL_NEVER_FAILS)
(!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
|| syscall_tampered(tcp))
&& !syscall_tampered_nofail(tcp));
@ -1390,7 +1376,7 @@ set_error(struct tcb *tcp, unsigned long new_error)
if (ptrace_syscall_info_is_valid())
tcp->u_rval = -1;
else
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
get_error(tcp, !(tcp->s_ent->sys_flags &
SYSCALL_NEVER_FAILS));
}
}
@ -1414,7 +1400,7 @@ set_success(struct tcb *tcp, kernel_long_t new_rval)
if (ptrace_syscall_info_is_valid())
tcp->u_error = 0;
else
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
get_error(tcp, !(tcp->s_ent->sys_flags &
SYSCALL_NEVER_FAILS));
}
}

1
test/.gitignore vendored
View File

@ -1,6 +1,7 @@
childthread
clone
leaderkill
many_looping_threads
mmap_offset_decode
mtd
seccomp

View File

@ -3,7 +3,8 @@ CFLAGS += -Wall
PROGS = \
sig skodic clone leaderkill childthread \
sigkill_rain wait_must_be_interruptible threaded_execve \
mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap
mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap \
many_looping_threads
all: $(PROGS)
@ -11,6 +12,8 @@ leaderkill: LDFLAGS += -pthread
childthread: LDFLAGS += -pthread
many_looping_threads: LDFLAGS += -pthread
clean distclean:
rm -f *.o core $(PROGS) *.gdb

View File

@ -0,0 +1,49 @@
// This testcase, when run with large number of threads
// under stace -f, may never finish because strace does not
// ensure any fairness in thread scheduling:
// it restarts threads as they stop. If daughter threads crowd out
// the "mother" and _they_ get continually restarted by strace,
// the end of spawning loop will never be reached.
//
// Also, it is a testcase which triggers the
// "strace: Exit of unknown pid 32457 seen"
// message when on testcase exit, strace sees deaths of newly-attached
// threads _before_ their first syscall stop.
//
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
static int thd_no;
static void *sub_thd(void *c)
{
dprintf(1, "sub-thread %d created\n", ++thd_no);
for (;;)
getuid();
return NULL;
}
int main(int argc, char *argv[])
{
int i;
pthread_t *thd;
int num_threads = 1;
if (argv[1])
num_threads = atoi(argv[1]);
thd = malloc(num_threads * sizeof(thd[0]));
dprintf(1, "test start, num_threads:%d...\n", num_threads);
for (i = 0; i < num_threads; i++) {
pthread_create(&thd[i], NULL, sub_thd, NULL);
dprintf(1, "after pthread_create\n");
}
/* Exit. This kills all threads */
return 0;
}

1
tests/.gitignore vendored
View File

@ -220,7 +220,6 @@ list_sigaction_signum
llseek
localtime
lookup_dcookie
looping_threads
lseek
lstat
lstat64

View File

@ -106,11 +106,9 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
ioctl_perf-success \
ioctl_rtc-v \
is_linux_mips_n64 \
kill_child \
ksysent \
list_sigaction_signum \
localtime \
looping_threads \
mmsg-silent \
mmsg_name-v \
msg_control-v \
@ -174,7 +172,6 @@ fstat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
fstatat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
ftruncate64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
localtime_LDADD = $(clock_LIBS) $(LDADD)
looping_threads_LDADD = -lpthread $(LDADD)
lstat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
mmap64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
mmap64_Xabbrev_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
@ -308,10 +305,8 @@ MISC_TESTS = \
get_regs.test \
inject-nf.test \
interactive_block.test \
kill_child.test \
ksysent.test \
localtime.test \
looping_threads.test \
opipe.test \
options-syntax.test \
pc.test \

View File

@ -1,46 +0,0 @@
/**
* Check for a corner case that previously lead to segfault due to an attempt
* to access unitialised tcp->s_ent.
*
* 13994 ????( <unfinished ...>
* ...
* 13994 <... ???? resumed>) = ?
*/
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "tests.h"
#define ITERS 10000
#define SC_ITERS 10000
int main(void)
{
unsigned int i;
for (i = 0; i < ITERS; i++) {
int pid = fork();
if (pid < 0)
perror_msg_and_fail("fork");
if (!pid) {
unsigned int j;
for (j = 0; j < SC_ITERS; j++)
getuid();
pause();
return 0;
}
kill(pid, SIGKILL);
wait(NULL);
}
return 0;
}

View File

@ -1,17 +0,0 @@
#!/bin/sh
#
# Check whether repeated killing of just forked processes crashes strace.
. "${srcdir=.}/init.sh"
run_prog "../$NAME"
# Run strace until the known corner case is observed
while :; do
run_strace -f -qq -esignal=none $args
# Printing of "<... SYSCALL resumed>" in strace.c:print_event_exit
# previously led to segfault if the syscall number hadn't been obtained
# on syscall entering.
grep -qE '^[0-9]+ <\.\.\. \?\?\?\? resumed>\) += \?$' "$LOG" && exit 0
done

View File

@ -1,110 +0,0 @@
/*
* Check tracing of looping threads.
*
* Copyright (c) 2009-2018 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*/
#include "tests.h"
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static void *
thread(void *arg)
{
for (;;)
getuid();
return arg;
}
int
main(int ac, const char *av[])
{
assert(ac == 3);
int timeout = atoi(av[1]);
assert(timeout > 0);
int num_threads = atoi(av[2]);
assert(num_threads > 0);
/* Create a new process group. */
if (setpgid(0, 0))
perror_msg_and_fail("setpgid");
/*
* When the main process terminates, the process group becomes orphaned.
* If any member of the orphaned process group is stopped, then
* a SIGHUP signal followed by a SIGCONT signal is sent to each process
* in the orphaned process group.
* Create a process in a stopped state to activate this behaviour.
*/
pid_t stopped = fork();
if (stopped < 0)
perror_msg_and_fail("fork");
if (!stopped) {
raise(SIGSTOP);
_exit(0);
}
const sigset_t set = {};
const struct sigaction act = { .sa_handler = SIG_DFL };
if (sigaction(SIGALRM, &act, NULL))
perror_msg_and_fail("sigaction");
if (sigprocmask(SIG_SETMASK, &set, NULL))
perror_msg_and_fail("sigprocmask");
alarm(timeout);
/*
* Create all threads in a subprocess, this guarantees that
* their tracer will not be their parent.
*/
pid_t pid = fork();
if (pid < 0)
perror_msg_and_fail("fork");
if (!pid) {
for (int i = 0; i < num_threads; i++) {
pthread_t t;
if ((errno = pthread_create(&t, NULL, thread, NULL)))
perror_msg_and_fail("pthread_create #%d", i);
}
/* This terminates all threads. */
_exit(0);
}
int s;
if (waitpid(pid, &s, 0) != pid)
perror_msg_and_fail("waitpid");
assert(WIFEXITED(s));
return WEXITSTATUS(s);
}

View File

@ -1,37 +0,0 @@
#!/bin/sh
#
# Check tracing of looping threads.
#
# Copyright (c) 2009-2018 The strace developers.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. 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.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
. "${srcdir=.}/init.sh"
check_prog nproc
timeout="$(($TIMEOUT_DURATION/10))"
nproc="$((64+$timeout+$(nproc)))"
run_prog "../$NAME" "$timeout" "$nproc"
run_strace -f -qq -enone -esignal=none $args

View File

@ -141,7 +141,7 @@ thread(void *arg)
}
printf("%-5d +++ superseded by execve in pid %u +++\n"
"%-5d <... execve resumed>) = 0\n",
"%-5d <... execve resumed> ) = 0\n",
leader, tid,
leader);

6
util.c
View File

@ -1161,7 +1161,7 @@ print_array_ex(struct tcb *const tcp,
int
printargs(struct tcb *tcp)
{
const int n = n_args(tcp);
const int n = tcp->s_ent->nargs;
int i;
for (i = 0; i < n; ++i)
tprintf("%s%#" PRI_klx, i ? ", " : "", tcp->u_arg[i]);
@ -1171,7 +1171,7 @@ printargs(struct tcb *tcp)
int
printargs_u(struct tcb *tcp)
{
const int n = n_args(tcp);
const int n = tcp->s_ent->nargs;
int i;
for (i = 0; i < n; ++i)
tprintf("%s%u", i ? ", " : "",
@ -1182,7 +1182,7 @@ printargs_u(struct tcb *tcp)
int
printargs_d(struct tcb *tcp)
{
const int n = n_args(tcp);
const int n = tcp->s_ent->nargs;
int i;
for (i = 0; i < n; ++i)
tprintf("%s%d", i ? ", " : "",