Compare commits

...

8 Commits

Author SHA1 Message Date
Eugene Syromyatnikov
cff0b9e5ca tests: add kill_child test
This tests repeatedly creates and kills children, so some corner
cases in handling of not-quite-existing processes can be observed.

Previously, strace was crashing in the following situation:

    13994 ????( <unfinished ...>
    ...
    13994 <... ???? resumed>) = ?

as tcp->s_ent wasn't initialised on syscall entering and
strace.c:print_event_exit segfaulted when tried to access
tcp->s_ent->sys_name.

* tests/kill_child.c: New file.
* tests/kill_child.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add kill_child.
(MISC_TESTS): Add kill_child.test.
2019-02-01 12:40:58 +01:00
Eugene Syromyatnikov
5cf7f16bad syscall.c: set MEMORY_MAPPING_CHANGE in stub sysent
As unknown syscalls may incur unknown side effects.

* syscall.c (stub_sysent): Set sys_flags to MEMORY_MAPPING_CHANGE.
2019-02-01 12:40:58 +01:00
Eugene Syromyatnikov
762abe0ce3 Use accessors for tcp->s_ent, return a stub struct if it is NULL
Since code paths are non-trivial, it's an attempt to future-proof and
prevent improper access of tcp->s_ent fields.

* defs.h (struct tcb): Update the description of s_ent field.
(stub_sysent): New declaration.
(tcp_sysent, n_args): New macro functions.
* ipc.c (SYS_FUNC(ipc)): Use n_args() instead of tcp->s_ent->nargs.
* linux/alpha/get_syscall_args.c (get_syscall_args): Likewise.
* linux/bfin/get_syscall_args.c (get_syscall_args): Likewise.
* linux/hppa/get_syscall_args.c (get_syscall_args): Likewise.
* linux/ia64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/microblaze/get_syscall_args.c (get_syscall_args): Likewise.
* linux/mips/get_syscall_args.c (get_syscall_args): Likewise.
* linux/sh/get_syscall_args.c (get_syscall_args): Likewise.
* linux/sh64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/x86_64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/xtensa/get_syscall_args.c (get_syscall_args): Likewise.
* prctl.c (print_prctl_args): Likewise.
* signal.c (SYS_FUNC(sgetmask)): Likewise.
* util.c (printargs, printargs_u, printargs_d): Likewise.
* syscall.c (decode_ipc_subcall, decode_syscall_subcall: Likewise.
(dumpio, tamper_with_syscall_exiting, syscall_entering_decode,
syscall_entering_decode, syscall_entering_trace, syscall_entering_trace,
syscall_exiting_decode, print_syscall_resume, syscall_exiting_trace,
get_syscall_result): Use tcp_sysent instead of tcp->s_ent().
(stub_sysent): New stub sysent.
(get_scno): Reset scno, s_ent, qual_flg; initialise s->ent from
stub_sysent.
* pathtrace.c (pathtrace_match_set): Use tcp_sysent instead of
tcp->s_ent().
2019-02-01 12:40:58 +01:00
Eugene Syromyatnikov
8c015ef905 syscall.c: remove arch check for SEN_execv usage
SEN_* decalrations are always present and can be used regardless
of architecture.

* syscall.c (syscall_entering_trace): Remove "#if defined SPARC ||
defined SPARC64" guard.
2019-02-01 12:40:58 +01:00
Eugene Syromyatnikov
fc6c6578dc Merge "<... resumed>" printing
Apparently, it was slightly different with no apparent reason. Use
a single routine to print this message now.

* defs.h (print_syscall_resume): New function declaration.
* strace.c (print_event_exit): Replace open-coding of "<... resumed>"
message printing with a print_syscall_resume() call.
* syscall.c (syscall_exiting_trace): Likewise.
(print_syscall_resume): New function.
* tests/threads-execve.c: Update expected output.
2019-02-01 12:40:57 +01:00
Eugene Syromyatnikov
756ccfa00b Implement queueing of threads before dispatching them
It is possible that some tracees call a lot of cheap syscalls too fast,
and that can lead to starvation to the point some tracees are not served
for indefinite amount of time.  In order to solve that unfairness, try
to collect all the pending tracees first along with the relevant
information and only then dispatch the events.

* defs.h: Include "list.h".
(struct tcb): Add wait_data_idx and wait_list fields.
* strace.c (struct tcb_wait_data): Add "msg" field.
(tcb_wait_tab): New static variable.
(expand_tcbtab): Resize tcb_wait_tab along with tcbtab, provide
an additional slot for extra event.
(droptcb): Remove tcp from wait_list.
(maybe_switch_tcbs): Get old pid from
tcb_wait_tab[tcp->wait_data_idx].msg.
(next_event): Add pending_tcps, extra_tcp, wait_nohang, elem, and
wait_tab_pos variables; check for elements in pending_tcps and skip
waiting if the list is not empty; check for extra_tcp and skip waiting
along with swapping wait_data_idx with wait_extra_data_idx;
after the initial wait, call wait4() in loop with WNOHANG flag set;
fetch siginfo on signal and eventmsg on PTRACE_EVENT_EXEC;
return the first tcp in pending_tcps list.
* tests/Makefile.am (XFAIL_TEST): Remove looping_threads.test.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=478419
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=526740
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=851457
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1609318
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1610774
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
Co-Authored-by: Denys Vlasenko <dvlasenk@redhat.com>
Co-Authored-by: Andreas Schwab <aschwab@redhat.com>
Co-Authored-by: Jeff Law <law@redhat.com>
Co-Authored-by: DJ Delorie <dj@redhat.com>
2019-02-01 12:40:57 +01:00
Eugene Syromyatnikov
1bff0fadad Add a generic list implementation
Similar to the one used in the Linux kernel.

* macros.h (cast_ptr, containerof): New macros.
* list.h: New file.
* Makefile.am (strace_SOURCES): Add it.
2019-02-01 12:40:29 +01:00
c1838de552 tests: check tracing of looping threads
* test/many_looping_threads.c: Remove.
* test/.gitignore: Remove many_looping_threads.
* test/Makefile (PROGS): Likewise.
(many_looping_threads): Remove.
* tests/looping_threads.c: New file.
* tests/looping_threads.test: New test.
* tests/.gitignore: Add looping_threads.
* tests/Makefile.am (check_PROGRAMS): Likewise.
(looping_threads_LDADD): New variable.
(MISC_TESTS, XFAIL_TESTS): Add looping_threads.test.
2019-01-29 15:39:18 +01:00
31 changed files with 822 additions and 246 deletions

View File

@ -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
View File

@ -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
View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

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 < 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) {

View File

@ -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;

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 (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]),

View File

@ -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;

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 < 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;

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->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])

View File

@ -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
View 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 */

View File

@ -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)
{

View File

@ -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;

View File

@ -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]);
}

View File

@ -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
View File

@ -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

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 = 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
View File

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

View File

@ -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

View File

@ -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
View File

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

View File

@ -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
View 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
View 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
View 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
View 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

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 = 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 ? ", " : "",