Compare commits
8 Commits
master
...
esyr/loopi
Author | SHA1 | Date | |
---|---|---|---|
|
cff0b9e5ca | ||
|
5cf7f16bad | ||
|
762abe0ce3 | ||
|
8c015ef905 | ||
|
fc6c6578dc | ||
|
756ccfa00b | ||
|
1bff0fadad | ||
c1838de552 |
@ -168,6 +168,7 @@ 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
20
defs.h
@ -37,6 +37,7 @@
|
||||
# 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"
|
||||
@ -206,7 +207,9 @@ 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 dummy struct for bad scno */
|
||||
const struct_sysent *s_ent; /* sysent[scno] or a stub struct for bad
|
||||
* scno. Use tcp_sysent() macro for access.
|
||||
*/
|
||||
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 */
|
||||
@ -216,6 +219,15 @@ 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
|
||||
@ -285,6 +297,10 @@ 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[];
|
||||
@ -427,6 +443,8 @@ 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
2
ipc.c
@ -21,7 +21,7 @@ SYS_FUNC(ipc)
|
||||
printxval_u(ipccalls, call, NULL);
|
||||
|
||||
unsigned int i;
|
||||
for (i = 1; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 1; i < n_args(tcp); ++i)
|
||||
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
|
||||
|
||||
return RVAL_DECODED;
|
||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, REG_A0+i, &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
@ -14,7 +14,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, argreg[i], &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, PT_GR26-4*i, &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
@ -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 < tcp->s_ent->nargs; ++i) {
|
||||
for (i = 0; i < n_args(tcp); ++i) {
|
||||
if (umove(tcp,
|
||||
(unsigned long) ia64_rse_skip_regs(out0, i),
|
||||
&tcp->u_arg[i]) < 0) {
|
||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, (5 + i) * 4, &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
@ -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 (tcp->s_ent->nargs > 4
|
||||
if (n_args(tcp) > 4
|
||||
&& umoven(tcp, mips_REG_SP + 4 * sizeof(tcp->u_arg[0]),
|
||||
(tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]),
|
||||
(n_args(tcp) - 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,
|
||||
(tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]));
|
||||
(n_args(tcp) - 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 (tcp->s_ent->nargs == MAX_ARGS) {
|
||||
if (n_args(tcp) == MAX_ARGS) {
|
||||
if (umoven(tcp,
|
||||
mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
|
||||
sizeof(tcp->u_arg[0]),
|
||||
|
@ -19,7 +19,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, syscall_regs[i], &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
@ -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 < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, REG_GENERAL(syscall_regs[i]),
|
||||
&tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
|
@ -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->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
|
||||
if (tcp_sysent(tcp)->sys_flags & COMPAT_SYSCALL_TYPES) {
|
||||
/*
|
||||
* X32 compat syscall: zero-extend from 32 bits.
|
||||
* Use truncate_klong_to_current_wordsize(tcp->u_arg[N])
|
||||
|
@ -20,7 +20,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = 0; i < n_args(tcp); ++i)
|
||||
if (upeek(tcp, xtensaregs[i], &tcp->u_arg[i]) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
295
list.h
Normal file
295
list.h
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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 */
|
9
macros.h
9
macros.h
@ -37,6 +37,15 @@
|
||||
(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)
|
||||
{
|
||||
|
@ -167,7 +167,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
{
|
||||
const struct_sysent *s;
|
||||
|
||||
s = tcp->s_ent;
|
||||
s = tcp_sysent(tcp);
|
||||
|
||||
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
|
||||
return false;
|
||||
|
2
prctl.c
2
prctl.c
@ -61,7 +61,7 @@ print_prctl_args(struct tcb *tcp, const unsigned int first)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = first; i < tcp->s_ent->nargs; ++i)
|
||||
for (i = first; i < n_args(tcp); ++i)
|
||||
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
|
||||
}
|
||||
|
||||
|
4
signal.c
4
signal.c
@ -405,10 +405,10 @@ SYS_FUNC(sgetmask)
|
||||
SYS_FUNC(sigsuspend)
|
||||
{
|
||||
#ifdef MIPS
|
||||
print_sigset_addr_len(tcp, tcp->u_arg[tcp->s_ent->nargs - 1],
|
||||
print_sigset_addr_len(tcp, tcp->u_arg[n_args(tcp) - 1],
|
||||
current_wordsize);
|
||||
#else
|
||||
tprint_old_sigmask_val("", tcp->u_arg[tcp->s_ent->nargs - 1]);
|
||||
tprint_old_sigmask_val("", tcp->u_arg[n_args(tcp) - 1]);
|
||||
#endif
|
||||
|
||||
return RVAL_DECODED;
|
||||
|
342
strace.c
342
strace.c
@ -143,10 +143,17 @@ 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;
|
||||
|
||||
@ -724,6 +731,9 @@ 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 *
|
||||
@ -827,6 +837,8 @@ droptcb(struct tcb *tcp)
|
||||
if (printing_tcp == tcp)
|
||||
printing_tcp = NULL;
|
||||
|
||||
list_remove(&tcp->wait_list);
|
||||
|
||||
memset(tcp, 0, sizeof(*tcp));
|
||||
}
|
||||
|
||||
@ -2042,10 +2054,8 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
|
||||
{
|
||||
FILE *fp;
|
||||
struct tcb *execve_thread;
|
||||
long old_pid = 0;
|
||||
long old_pid = tcb_wait_tab[tcp->wait_data_idx].msg;
|
||||
|
||||
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;
|
||||
@ -2181,12 +2191,7 @@ print_event_exit(struct tcb *tcp)
|
||||
set_current_tcp(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);
|
||||
}
|
||||
print_syscall_resume(tcp);
|
||||
|
||||
if (!(tcp->sys_func_rval & RVAL_DECODED)) {
|
||||
/*
|
||||
@ -2206,17 +2211,27 @@ 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()); }
|
||||
@ -2258,8 +2273,10 @@ next_event(void)
|
||||
* then the system call will be interrupted and
|
||||
* the expiration will be handled by the signal handler.
|
||||
*/
|
||||
pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
|
||||
const int wait_errno = errno;
|
||||
int status;
|
||||
struct rusage ru;
|
||||
int pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
|
||||
int wait_errno = errno;
|
||||
|
||||
/*
|
||||
* The window of opportunity to handle expirations
|
||||
@ -2275,135 +2292,194 @@ next_event(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pid < 0) {
|
||||
if (wait_errno == EINTR) {
|
||||
wd->te = TE_NEXT;
|
||||
return wd;
|
||||
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 (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)");
|
||||
}
|
||||
|
||||
wd->status = status;
|
||||
if (!pid)
|
||||
break;
|
||||
|
||||
if (pid == popen_pid) {
|
||||
if (!WIFSTOPPED(status))
|
||||
popen_pid = 0;
|
||||
wd->te = TE_NEXT;
|
||||
return wd;
|
||||
}
|
||||
if (pid == popen_pid) {
|
||||
if (!WIFSTOPPED(status))
|
||||
popen_pid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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) {
|
||||
wd->te = TE_NEXT;
|
||||
return wd;
|
||||
tcp = maybe_allocate_tcb(pid, status);
|
||||
if (!tcp)
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
return tcb_wait_tab + tcp->wait_data_idx;
|
||||
}
|
||||
|
||||
static int
|
||||
|
86
syscall.c
86
syscall.c
@ -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 = tcp->s_ent->nargs;
|
||||
const unsigned int n = n_args(tcp);
|
||||
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->s_ent->sen) {
|
||||
switch (tcp_sysent(tcp)->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->s_ent->sen) {
|
||||
switch (tcp_sysent(tcp)->sen) {
|
||||
case SEN_read:
|
||||
case SEN_pread:
|
||||
case SEN_recv:
|
||||
@ -548,10 +548,9 @@ 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(", scno_good == 1 ? tcp->s_ent->sys_name : "????");
|
||||
tprintf("%s(", tcp_sysent(tcp)->sys_name);
|
||||
/*
|
||||
* " <unavailable>" will be added later by the code which
|
||||
* detects ptrace errors.
|
||||
@ -563,7 +562,7 @@ syscall_entering_decode(struct tcb *tcp)
|
||||
|| defined SYS_socket_subcall \
|
||||
|| defined SYS_syscall_subcall
|
||||
for (;;) {
|
||||
switch (tcp->s_ent->sen) {
|
||||
switch (tcp_sysent(tcp)->sen) {
|
||||
# ifdef SYS_ipc_subcall
|
||||
case SEN_ipc:
|
||||
decode_ipc_subcall(tcp);
|
||||
@ -577,7 +576,7 @@ syscall_entering_decode(struct tcb *tcp)
|
||||
# ifdef SYS_syscall_subcall
|
||||
case SEN_syscall:
|
||||
decode_syscall_subcall(tcp);
|
||||
if (tcp->s_ent->sen != SEN_syscall)
|
||||
if (tcp_sysent(tcp)->sen != SEN_syscall)
|
||||
continue;
|
||||
break;
|
||||
# endif
|
||||
@ -599,12 +598,10 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
||||
*/
|
||||
tcp->qual_flg &= ~QUAL_INJECT;
|
||||
|
||||
switch (tcp->s_ent->sen) {
|
||||
switch (tcp_sysent(tcp)->sen) {
|
||||
case SEN_execve:
|
||||
case SEN_execveat:
|
||||
#if defined SPARC || defined SPARC64
|
||||
case SEN_execv:
|
||||
#endif
|
||||
/*
|
||||
* First exec* syscall makes the log visible.
|
||||
*/
|
||||
@ -633,14 +630,14 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
||||
|
||||
#ifdef ENABLE_STACKTRACE
|
||||
if (stack_trace_enabled) {
|
||||
if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
|
||||
if (tcp_sysent(tcp)->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
|
||||
unwind_tcb_capture(tcp);
|
||||
}
|
||||
#endif
|
||||
|
||||
printleader(tcp);
|
||||
tprintf("%s(", tcp->s_ent->sys_name);
|
||||
int res = raw(tcp) ? printargs(tcp) : tcp->s_ent->sys_func(tcp);
|
||||
tprintf("%s(", tcp_sysent(tcp)->sys_name);
|
||||
int res = raw(tcp) ? printargs(tcp) : tcp_sysent(tcp)->sys_func(tcp);
|
||||
fflush(tcp->outf);
|
||||
return res;
|
||||
}
|
||||
@ -670,7 +667,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
|
||||
if ((Tflag || cflag) && !filtered(tcp))
|
||||
clock_gettime(CLOCK_MONOTONIC, pts);
|
||||
|
||||
if (tcp->s_ent->sys_flags & MEMORY_MAPPING_CHANGE)
|
||||
if (tcp_sysent(tcp)->sys_flags & MEMORY_MAPPING_CHANGE)
|
||||
mmap_notify_report(tcp);
|
||||
|
||||
if (filtered(tcp))
|
||||
@ -688,6 +685,26 @@ 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)
|
||||
{
|
||||
@ -701,20 +718,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
print_syscall_resume(tcp);
|
||||
printing_tcp = tcp;
|
||||
|
||||
tcp->s_prev_ent = NULL;
|
||||
@ -745,7 +749,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->s_ent->sys_func(tcp);
|
||||
sys_res = tcp_sysent(tcp)->sys_func(tcp);
|
||||
}
|
||||
|
||||
tprints(") ");
|
||||
@ -1232,6 +1236,14 @@ 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
|
||||
@ -1243,6 +1255,10 @@ get_syscall_regs(struct tcb *tcp)
|
||||
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;
|
||||
|
||||
@ -1270,9 +1286,7 @@ get_scno(struct tcb *tcp)
|
||||
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
||||
|
||||
s->tcp = tcp;
|
||||
s->ent.nargs = MAX_ARGS;
|
||||
s->ent.sen = SEN_printargs;
|
||||
s->ent.sys_func = printargs;
|
||||
s->ent = stub_sysent;
|
||||
s->ent.sys_name = s->buf;
|
||||
xsprintf(s->buf, "syscall_%#" PRI_klx, shuffle_scno(tcp->scno));
|
||||
|
||||
@ -1303,7 +1317,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->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
|
||||
if (tcp_sysent(tcp)->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];
|
||||
}
|
||||
@ -1330,7 +1344,7 @@ get_syscall_result(struct tcb *tcp)
|
||||
if (get_syscall_result_regs(tcp) < 0)
|
||||
return -1;
|
||||
get_error(tcp,
|
||||
(!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
|
||||
(!(tcp_sysent(tcp)->sys_flags & SYSCALL_NEVER_FAILS)
|
||||
|| syscall_tampered(tcp))
|
||||
&& !syscall_tampered_nofail(tcp));
|
||||
|
||||
@ -1376,7 +1390,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->s_ent->sys_flags &
|
||||
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
|
||||
SYSCALL_NEVER_FAILS));
|
||||
}
|
||||
}
|
||||
@ -1400,7 +1414,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->s_ent->sys_flags &
|
||||
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
|
||||
SYSCALL_NEVER_FAILS));
|
||||
}
|
||||
}
|
||||
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@ -1,7 +1,6 @@
|
||||
childthread
|
||||
clone
|
||||
leaderkill
|
||||
many_looping_threads
|
||||
mmap_offset_decode
|
||||
mtd
|
||||
seccomp
|
||||
|
@ -3,8 +3,7 @@ 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 \
|
||||
many_looping_threads
|
||||
mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
@ -12,8 +11,6 @@ leaderkill: LDFLAGS += -pthread
|
||||
|
||||
childthread: LDFLAGS += -pthread
|
||||
|
||||
many_looping_threads: LDFLAGS += -pthread
|
||||
|
||||
clean distclean:
|
||||
rm -f *.o core $(PROGS) *.gdb
|
||||
|
||||
|
@ -1,49 +0,0 @@
|
||||
// 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
1
tests/.gitignore
vendored
@ -220,6 +220,7 @@ list_sigaction_signum
|
||||
llseek
|
||||
localtime
|
||||
lookup_dcookie
|
||||
looping_threads
|
||||
lseek
|
||||
lstat
|
||||
lstat64
|
||||
|
@ -106,9 +106,11 @@ 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 \
|
||||
@ -172,6 +174,7 @@ 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
|
||||
@ -305,8 +308,10 @@ 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 \
|
||||
|
46
tests/kill_child.c
Normal file
46
tests/kill_child.c
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
17
tests/kill_child.test
Executable file
17
tests/kill_child.test
Executable file
@ -0,0 +1,17 @@
|
||||
#!/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
|
110
tests/looping_threads.c
Normal file
110
tests/looping_threads.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
37
tests/looping_threads.test
Executable file
37
tests/looping_threads.test
Executable file
@ -0,0 +1,37 @@
|
||||
#!/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
|
@ -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
6
util.c
@ -1161,7 +1161,7 @@ print_array_ex(struct tcb *const tcp,
|
||||
int
|
||||
printargs(struct tcb *tcp)
|
||||
{
|
||||
const int n = tcp->s_ent->nargs;
|
||||
const int n = n_args(tcp);
|
||||
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 = tcp->s_ent->nargs;
|
||||
const int n = n_args(tcp);
|
||||
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 = tcp->s_ent->nargs;
|
||||
const int n = n_args(tcp);
|
||||
int i;
|
||||
for (i = 0; i < n; ++i)
|
||||
tprintf("%s%d", i ? ", " : "",
|
||||
|
Loading…
Reference in New Issue
Block a user