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/asm_stat.h \
|
||||||
linux/x32/asm_stat.h \
|
linux/x32/asm_stat.h \
|
||||||
linux/x86_64/asm_stat.h \
|
linux/x86_64/asm_stat.h \
|
||||||
|
list.h \
|
||||||
listen.c \
|
listen.c \
|
||||||
lookup_dcookie.c \
|
lookup_dcookie.c \
|
||||||
loop.c \
|
loop.c \
|
||||||
|
20
defs.h
20
defs.h
@ -37,6 +37,7 @@
|
|||||||
# include "error_prints.h"
|
# include "error_prints.h"
|
||||||
# include "gcc_compat.h"
|
# include "gcc_compat.h"
|
||||||
# include "kernel_types.h"
|
# include "kernel_types.h"
|
||||||
|
# include "list.h"
|
||||||
# include "macros.h"
|
# include "macros.h"
|
||||||
# include "mpers_type.h"
|
# include "mpers_type.h"
|
||||||
# include "string_to_uint.h"
|
# include "string_to_uint.h"
|
||||||
@ -206,7 +207,9 @@ struct tcb {
|
|||||||
const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */
|
const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */
|
||||||
void *_priv_data; /* Private data for syscall decoding functions */
|
void *_priv_data; /* Private data for syscall decoding functions */
|
||||||
void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
|
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 */
|
const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" msg */
|
||||||
struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
|
struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
|
||||||
struct timespec stime; /* System time usage as of last process wait */
|
struct timespec stime; /* System time usage as of last process wait */
|
||||||
@ -216,6 +219,15 @@ struct tcb {
|
|||||||
|
|
||||||
struct mmap_cache_t *mmap_cache;
|
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
|
# ifdef HAVE_LINUX_KVM_H
|
||||||
struct vcpu_info *vcpu_info_list;
|
struct vcpu_info *vcpu_info_list;
|
||||||
# endif
|
# endif
|
||||||
@ -285,6 +297,10 @@ struct tcb {
|
|||||||
# define syscall_delayed(tcp) ((tcp)->flags & TCB_DELAYED)
|
# define syscall_delayed(tcp) ((tcp)->flags & TCB_DELAYED)
|
||||||
# define syscall_tampered_nofail(tcp) ((tcp)->flags & TCB_TAMPERED_NO_FAIL)
|
# 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"
|
# include "xlat.h"
|
||||||
|
|
||||||
extern const struct xlat addrfams[];
|
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 bool get_stack_pointer(struct tcb *, kernel_ulong_t *);
|
||||||
extern void print_instruction_pointer(struct tcb *);
|
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_decode(struct tcb *);
|
||||||
extern int syscall_entering_trace(struct tcb *, unsigned int *);
|
extern int syscall_entering_trace(struct tcb *, unsigned int *);
|
||||||
extern void syscall_entering_finish(struct tcb *, 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);
|
printxval_u(ipccalls, call, NULL);
|
||||||
|
|
||||||
unsigned int i;
|
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]);
|
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
|
||||||
|
|
||||||
return RVAL_DECODED;
|
return RVAL_DECODED;
|
||||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
{
|
{
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, REG_A0+i, &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -14,7 +14,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
};
|
};
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, argreg[i], &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
{
|
{
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, PT_GR26-4*i, &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
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 long *out0 = ia64_rse_skip_regs(rbs_end, -sof + sol);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < tcp->s_ent->nargs; ++i) {
|
for (i = 0; i < n_args(tcp); ++i) {
|
||||||
if (umove(tcp,
|
if (umove(tcp,
|
||||||
(unsigned long) ia64_rse_skip_regs(out0, i),
|
(unsigned long) ia64_rse_skip_regs(out0, i),
|
||||||
&tcp->u_arg[i]) < 0) {
|
&tcp->u_arg[i]) < 0) {
|
||||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
{
|
{
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, (5 + i) * 4, &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
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[1] = mips_REG_A1;
|
||||||
tcp->u_arg[2] = mips_REG_A2;
|
tcp->u_arg[2] = mips_REG_A2;
|
||||||
tcp->u_arg[3] = mips_REG_A3;
|
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]),
|
&& 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) {
|
&tcp->u_arg[4]) < 0) {
|
||||||
/*
|
/*
|
||||||
* Let's proceed with the first 4 arguments
|
* Let's proceed with the first 4 arguments
|
||||||
* instead of reporting the failure.
|
* instead of reporting the failure.
|
||||||
*/
|
*/
|
||||||
memset(&tcp->u_arg[4], 0,
|
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
|
#else
|
||||||
# error unsupported mips abi
|
# error unsupported mips abi
|
||||||
@ -54,7 +54,7 @@ decode_syscall_subcall(struct tcb *tcp)
|
|||||||
* and sync_file_range) requires additional code,
|
* and sync_file_range) requires additional code,
|
||||||
* see linux/mips/get_syscall_args.c
|
* see linux/mips/get_syscall_args.c
|
||||||
*/
|
*/
|
||||||
if (tcp->s_ent->nargs == MAX_ARGS) {
|
if (n_args(tcp) == MAX_ARGS) {
|
||||||
if (umoven(tcp,
|
if (umoven(tcp,
|
||||||
mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
|
mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
|
||||||
sizeof(tcp->u_arg[0]),
|
sizeof(tcp->u_arg[0]),
|
||||||
|
@ -19,7 +19,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
};
|
};
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, syscall_regs[i], &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
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 };
|
static const int syscall_regs[MAX_ARGS] = { 2, 3, 4, 5, 6, 7 };
|
||||||
unsigned int i;
|
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]),
|
if (upeek(tcp, REG_GENERAL(syscall_regs[i]),
|
||||||
&tcp->u_arg[i]) < 0)
|
&tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -11,7 +11,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
{
|
{
|
||||||
if (x86_io.iov_len != sizeof(i386_regs)) {
|
if (x86_io.iov_len != sizeof(i386_regs)) {
|
||||||
/* x86-64 or x32 ABI */
|
/* 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.
|
* X32 compat syscall: zero-extend from 32 bits.
|
||||||
* Use truncate_klong_to_current_wordsize(tcp->u_arg[N])
|
* Use truncate_klong_to_current_wordsize(tcp->u_arg[N])
|
||||||
|
@ -20,7 +20,7 @@ arch_get_syscall_args(struct tcb *tcp)
|
|||||||
};
|
};
|
||||||
unsigned int i;
|
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)
|
if (upeek(tcp, xtensaregs[i], &tcp->u_arg[i]) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
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_))
|
(offsetof(type_, member_) + sizeof(((type_ *)0)->member_))
|
||||||
# endif
|
# 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
|
static inline bool
|
||||||
is_filled(const char *ptr, char fill, size_t size)
|
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;
|
const struct_sysent *s;
|
||||||
|
|
||||||
s = tcp->s_ent;
|
s = tcp_sysent(tcp);
|
||||||
|
|
||||||
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
|
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
|
||||||
return false;
|
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;
|
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]);
|
tprintf(", %#" PRI_klx, tcp->u_arg[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
signal.c
4
signal.c
@ -405,10 +405,10 @@ SYS_FUNC(sgetmask)
|
|||||||
SYS_FUNC(sigsuspend)
|
SYS_FUNC(sigsuspend)
|
||||||
{
|
{
|
||||||
#ifdef MIPS
|
#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);
|
current_wordsize);
|
||||||
#else
|
#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
|
#endif
|
||||||
|
|
||||||
return RVAL_DECODED;
|
return RVAL_DECODED;
|
||||||
|
184
strace.c
184
strace.c
@ -143,10 +143,17 @@ static struct tcb *current_tcp;
|
|||||||
struct tcb_wait_data {
|
struct tcb_wait_data {
|
||||||
enum trace_event te; /**< Event passed to dispatch_event() */
|
enum trace_event te; /**< Event passed to dispatch_event() */
|
||||||
int status; /**< status, returned by wait4() */
|
int status; /**< status, returned by wait4() */
|
||||||
|
unsigned long msg; /**< Value returned by PTRACE_GETEVENTMSG */
|
||||||
siginfo_t si; /**< siginfo, returned by PTRACE_GETSIGINFO */
|
siginfo_t si; /**< siginfo, returned by PTRACE_GETSIGINFO */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct tcb **tcbtab;
|
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 unsigned int nprocs;
|
||||||
static size_t tcbtabsize;
|
static size_t tcbtabsize;
|
||||||
|
|
||||||
@ -724,6 +731,9 @@ expand_tcbtab(void)
|
|||||||
for (tcb_ptr = tcbtab + old_tcbtabsize;
|
for (tcb_ptr = tcbtab + old_tcbtabsize;
|
||||||
tcb_ptr < tcbtab + tcbtabsize; tcb_ptr++, newtcbs++)
|
tcb_ptr < tcbtab + tcbtabsize; tcb_ptr++, newtcbs++)
|
||||||
*tcb_ptr = newtcbs;
|
*tcb_ptr = newtcbs;
|
||||||
|
|
||||||
|
tcb_wait_tab = xreallocarray(tcb_wait_tab, sizeof(*tcb_wait_tab),
|
||||||
|
tcbtabsize + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tcb *
|
static struct tcb *
|
||||||
@ -827,6 +837,8 @@ droptcb(struct tcb *tcp)
|
|||||||
if (printing_tcp == tcp)
|
if (printing_tcp == tcp)
|
||||||
printing_tcp = NULL;
|
printing_tcp = NULL;
|
||||||
|
|
||||||
|
list_remove(&tcp->wait_list);
|
||||||
|
|
||||||
memset(tcp, 0, sizeof(*tcp));
|
memset(tcp, 0, sizeof(*tcp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2042,10 +2054,8 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
|
|||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct tcb *execve_thread;
|
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 */
|
/* Avoid truncation in pid2tcb() param passing */
|
||||||
if (old_pid <= 0 || old_pid == pid)
|
if (old_pid <= 0 || old_pid == pid)
|
||||||
return tcp;
|
return tcp;
|
||||||
@ -2181,12 +2191,7 @@ print_event_exit(struct tcb *tcp)
|
|||||||
set_current_tcp(tcp);
|
set_current_tcp(tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((followfork < 2 && printing_tcp != tcp)
|
print_syscall_resume(tcp);
|
||||||
|| (tcp->flags & TCB_REPRINT)) {
|
|
||||||
tcp->flags &= ~TCB_REPRINT;
|
|
||||||
printleader(tcp);
|
|
||||||
tprintf("<... %s resumed>", tcp->s_ent->sys_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(tcp->sys_func_rval & RVAL_DECODED)) {
|
if (!(tcp->sys_func_rval & RVAL_DECODED)) {
|
||||||
/*
|
/*
|
||||||
@ -2206,17 +2211,27 @@ print_event_exit(struct tcb *tcp)
|
|||||||
static const struct tcb_wait_data *
|
static const struct tcb_wait_data *
|
||||||
next_event(void)
|
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)
|
if (interrupted)
|
||||||
return NULL;
|
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:
|
* Used to exit simply when nprocs hits zero, but in this testcase:
|
||||||
* int main(void) { _exit(!!fork()); }
|
* int main(void) { _exit(!!fork()); }
|
||||||
@ -2258,8 +2273,10 @@ next_event(void)
|
|||||||
* then the system call will be interrupted and
|
* then the system call will be interrupted and
|
||||||
* the expiration will be handled by the signal handler.
|
* the expiration will be handled by the signal handler.
|
||||||
*/
|
*/
|
||||||
pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
|
int status;
|
||||||
const int wait_errno = errno;
|
struct rusage ru;
|
||||||
|
int pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
|
||||||
|
int wait_errno = errno;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The window of opportunity to handle expirations
|
* The window of opportunity to handle expirations
|
||||||
@ -2275,11 +2292,17 @@ next_event(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t wait_tab_pos = 0;
|
||||||
|
bool wait_nohang = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct tcb_wait_data *wd;
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
if (wait_errno == EINTR) {
|
if (wait_errno == EINTR)
|
||||||
wd->te = TE_NEXT;
|
break;
|
||||||
return wd;
|
if (wait_nohang)
|
||||||
}
|
break;
|
||||||
if (nprocs == 0 && wait_errno == ECHILD)
|
if (nprocs == 0 && wait_errno == ECHILD)
|
||||||
return NULL;
|
return NULL;
|
||||||
/*
|
/*
|
||||||
@ -2290,13 +2313,13 @@ next_event(void)
|
|||||||
perror_msg_and_die("wait4(__WALL)");
|
perror_msg_and_die("wait4(__WALL)");
|
||||||
}
|
}
|
||||||
|
|
||||||
wd->status = status;
|
if (!pid)
|
||||||
|
break;
|
||||||
|
|
||||||
if (pid == popen_pid) {
|
if (pid == popen_pid) {
|
||||||
if (!WIFSTOPPED(status))
|
if (!WIFSTOPPED(status))
|
||||||
popen_pid = 0;
|
popen_pid = 0;
|
||||||
wd->te = TE_NEXT;
|
break;
|
||||||
return wd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_flag)
|
if (debug_flag)
|
||||||
@ -2307,16 +2330,9 @@ next_event(void)
|
|||||||
|
|
||||||
if (!tcp) {
|
if (!tcp) {
|
||||||
tcp = maybe_allocate_tcb(pid, status);
|
tcp = maybe_allocate_tcb(pid, status);
|
||||||
if (!tcp) {
|
if (!tcp)
|
||||||
wd->te = TE_NEXT;
|
break;
|
||||||
return wd;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
clear_regs(tcp);
|
|
||||||
|
|
||||||
/* Set current output file */
|
|
||||||
set_current_tcp(tcp);
|
|
||||||
|
|
||||||
if (cflag) {
|
if (cflag) {
|
||||||
struct timespec stime = {
|
struct timespec stime = {
|
||||||
@ -2327,26 +2343,26 @@ next_event(void)
|
|||||||
tcp->stime = 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)) {
|
if (WIFSIGNALED(status)) {
|
||||||
wd->te = TE_SIGNALLED;
|
wd->te = TE_SIGNALLED;
|
||||||
return wd;
|
} else if (WIFEXITED(status)) {
|
||||||
}
|
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
wd->te = TE_EXITED;
|
wd->te = TE_EXITED;
|
||||||
return wd;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As WCONTINUED flag has not been specified to wait4,
|
* As WCONTINUED flag has not been specified to wait4,
|
||||||
* it cannot be WIFCONTINUED(status), so the only case
|
* it cannot be WIFCONTINUED(status), so the only case
|
||||||
* that remains is WIFSTOPPED(status).
|
* 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 sig = WSTOPSIG(status);
|
||||||
const unsigned int event = (unsigned int) status >> 16;
|
const unsigned int event = (unsigned int) status >> 16;
|
||||||
|
|
||||||
@ -2359,22 +2375,28 @@ next_event(void)
|
|||||||
* than SIGSTOP if we happened to attach
|
* than SIGSTOP if we happened to attach
|
||||||
* just before the process takes a signal.
|
* just before the process takes a signal.
|
||||||
*/
|
*/
|
||||||
if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
|
if (sig == SIGSTOP &&
|
||||||
debug_func_msg("ignored SIGSTOP on pid %d", tcp->pid);
|
(tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
|
||||||
|
debug_func_msg("ignored SIGSTOP on "
|
||||||
|
"pid %d", tcp->pid);
|
||||||
tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
|
tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
|
||||||
wd->te = TE_RESTART;
|
wd->te = TE_RESTART;
|
||||||
} else if (sig == syscall_trap_sig) {
|
} else if (sig == syscall_trap_sig) {
|
||||||
wd->te = TE_SYSCALL_STOP;
|
wd->te = TE_SYSCALL_STOP;
|
||||||
} else {
|
} else {
|
||||||
memset(&wd->si, 0, sizeof(wd->si));
|
|
||||||
/*
|
/*
|
||||||
* True if tracee is stopped by signal
|
* True if tracee is stopped by signal
|
||||||
* (as opposed to "tracee received signal").
|
* (as opposed to "tracee received
|
||||||
* TODO: shouldn't we check for errno == EINVAL too?
|
* signal").
|
||||||
|
* TODO: shouldn't we check for
|
||||||
|
* errno == EINVAL too?
|
||||||
* We can get ESRCH instead, you know...
|
* We can get ESRCH instead, you know...
|
||||||
*/
|
*/
|
||||||
bool stopped = ptrace(PTRACE_GETSIGINFO, pid, 0, &wd->si) < 0;
|
bool stopped = ptrace(PTRACE_GETSIGINFO,
|
||||||
wd->te = stopped ? TE_GROUP_STOP : TE_SIGNAL_DELIVERY_STOP;
|
pid, 0, &wd->si) < 0;
|
||||||
|
|
||||||
|
wd->te = stopped ? TE_GROUP_STOP
|
||||||
|
: TE_SIGNAL_DELIVERY_STOP;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PTRACE_EVENT_STOP:
|
case PTRACE_EVENT_STOP:
|
||||||
@ -2394,6 +2416,15 @@ next_event(void)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PTRACE_EVENT_EXEC:
|
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;
|
wd->te = TE_STOP_BEFORE_EXECVE;
|
||||||
break;
|
break;
|
||||||
case PTRACE_EVENT_EXIT:
|
case PTRACE_EVENT_EXIT:
|
||||||
@ -2402,8 +2433,53 @@ next_event(void)
|
|||||||
default:
|
default:
|
||||||
wd->te = TE_RESTART;
|
wd->te = TE_RESTART;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return wd;
|
if (tcp->wait_list.next) {
|
||||||
|
wait_extra_data_idx = wait_tab_pos;
|
||||||
|
extra_tcp = tcp;
|
||||||
|
debug_func_msg("queued extra pid %d", tcp->pid);
|
||||||
|
} else {
|
||||||
|
tcp->wait_data_idx = wait_tab_pos;
|
||||||
|
list_append(&pending_tcps, &tcp->wait_list);
|
||||||
|
debug_func_msg("queued pid %d", tcp->pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
wd->status = status;
|
||||||
|
wait_tab_pos++;
|
||||||
|
|
||||||
|
if (extra_tcp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pid = wait4(-1, &status, __WALL | WNOHANG, (cflag ? &ru : NULL));
|
||||||
|
wait_errno = errno;
|
||||||
|
wait_nohang = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_event_get_tcp:
|
||||||
|
elem = list_remove_head(&pending_tcps);
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
memset(tcb_wait_tab, 0, sizeof(*tcb_wait_tab));
|
||||||
|
tcb_wait_tab->te = TE_NEXT;
|
||||||
|
|
||||||
|
return tcb_wait_tab;
|
||||||
|
} else {
|
||||||
|
tcp = list_elem(elem, struct tcb, wait_list);
|
||||||
|
debug_func_msg("dequeued pid %d", tcp->pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
next_event_exit:
|
||||||
|
/* Is this the very first time we see this tracee stopped? */
|
||||||
|
if (tcp->flags & TCB_STARTUP)
|
||||||
|
startup_tcb(tcp);
|
||||||
|
|
||||||
|
clear_regs(tcp);
|
||||||
|
|
||||||
|
/* Set current output file */
|
||||||
|
set_current_tcp(tcp);
|
||||||
|
|
||||||
|
return tcb_wait_tab + tcp->wait_data_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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->qual_flg = qual_flags(tcp->scno);
|
||||||
tcp->s_ent = &sysent[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;
|
unsigned int i;
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
tcp->u_arg[i] = tcp->u_arg[i + 1];
|
tcp->u_arg[i] = tcp->u_arg[i + 1];
|
||||||
@ -361,7 +361,7 @@ dumpio(struct tcb *tcp)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (is_number_in_set(fd, write_set)) {
|
if (is_number_in_set(fd, write_set)) {
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp_sysent(tcp)->sen) {
|
||||||
case SEN_write:
|
case SEN_write:
|
||||||
case SEN_pwrite:
|
case SEN_pwrite:
|
||||||
case SEN_send:
|
case SEN_send:
|
||||||
@ -388,7 +388,7 @@ dumpio(struct tcb *tcp)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (is_number_in_set(fd, read_set)) {
|
if (is_number_in_set(fd, read_set)) {
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp_sysent(tcp)->sen) {
|
||||||
case SEN_read:
|
case SEN_read:
|
||||||
case SEN_pread:
|
case SEN_pread:
|
||||||
case SEN_recv:
|
case SEN_recv:
|
||||||
@ -548,10 +548,9 @@ syscall_entering_decode(struct tcb *tcp)
|
|||||||
int res = get_scno(tcp);
|
int res = get_scno(tcp);
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
return res;
|
return res;
|
||||||
int scno_good = res;
|
|
||||||
if (res != 1 || (res = get_syscall_args(tcp)) != 1) {
|
if (res != 1 || (res = get_syscall_args(tcp)) != 1) {
|
||||||
printleader(tcp);
|
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
|
* " <unavailable>" will be added later by the code which
|
||||||
* detects ptrace errors.
|
* detects ptrace errors.
|
||||||
@ -563,7 +562,7 @@ syscall_entering_decode(struct tcb *tcp)
|
|||||||
|| defined SYS_socket_subcall \
|
|| defined SYS_socket_subcall \
|
||||||
|| defined SYS_syscall_subcall
|
|| defined SYS_syscall_subcall
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp_sysent(tcp)->sen) {
|
||||||
# ifdef SYS_ipc_subcall
|
# ifdef SYS_ipc_subcall
|
||||||
case SEN_ipc:
|
case SEN_ipc:
|
||||||
decode_ipc_subcall(tcp);
|
decode_ipc_subcall(tcp);
|
||||||
@ -577,7 +576,7 @@ syscall_entering_decode(struct tcb *tcp)
|
|||||||
# ifdef SYS_syscall_subcall
|
# ifdef SYS_syscall_subcall
|
||||||
case SEN_syscall:
|
case SEN_syscall:
|
||||||
decode_syscall_subcall(tcp);
|
decode_syscall_subcall(tcp);
|
||||||
if (tcp->s_ent->sen != SEN_syscall)
|
if (tcp_sysent(tcp)->sen != SEN_syscall)
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
# endif
|
# endif
|
||||||
@ -599,12 +598,10 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
|||||||
*/
|
*/
|
||||||
tcp->qual_flg &= ~QUAL_INJECT;
|
tcp->qual_flg &= ~QUAL_INJECT;
|
||||||
|
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp_sysent(tcp)->sen) {
|
||||||
case SEN_execve:
|
case SEN_execve:
|
||||||
case SEN_execveat:
|
case SEN_execveat:
|
||||||
#if defined SPARC || defined SPARC64
|
|
||||||
case SEN_execv:
|
case SEN_execv:
|
||||||
#endif
|
|
||||||
/*
|
/*
|
||||||
* First exec* syscall makes the log visible.
|
* First exec* syscall makes the log visible.
|
||||||
*/
|
*/
|
||||||
@ -633,14 +630,14 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
|||||||
|
|
||||||
#ifdef ENABLE_STACKTRACE
|
#ifdef ENABLE_STACKTRACE
|
||||||
if (stack_trace_enabled) {
|
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);
|
unwind_tcb_capture(tcp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
printleader(tcp);
|
printleader(tcp);
|
||||||
tprintf("%s(", tcp->s_ent->sys_name);
|
tprintf("%s(", tcp_sysent(tcp)->sys_name);
|
||||||
int res = raw(tcp) ? printargs(tcp) : tcp->s_ent->sys_func(tcp);
|
int res = raw(tcp) ? printargs(tcp) : tcp_sysent(tcp)->sys_func(tcp);
|
||||||
fflush(tcp->outf);
|
fflush(tcp->outf);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -670,7 +667,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
|
|||||||
if ((Tflag || cflag) && !filtered(tcp))
|
if ((Tflag || cflag) && !filtered(tcp))
|
||||||
clock_gettime(CLOCK_MONOTONIC, pts);
|
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);
|
mmap_notify_report(tcp);
|
||||||
|
|
||||||
if (filtered(tcp))
|
if (filtered(tcp))
|
||||||
@ -688,6 +685,26 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
|
|||||||
return get_syscall_result(tcp);
|
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
|
int
|
||||||
syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
|
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,
|
print_syscall_resume(tcp);
|
||||||
* then the log currently does not end with output
|
|
||||||
* of _our syscall entry_, but with something else.
|
|
||||||
* We need to say which syscall's return is this.
|
|
||||||
*
|
|
||||||
* Forced reprinting via TCB_REPRINT is used only by
|
|
||||||
* "strace -ff -oLOG test/threaded_execve" corner case.
|
|
||||||
* It's the only case when -ff mode needs reprinting.
|
|
||||||
*/
|
|
||||||
if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) {
|
|
||||||
tcp->flags &= ~TCB_REPRINT;
|
|
||||||
printleader(tcp);
|
|
||||||
tprintf("<... %s resumed> ", tcp->s_ent->sys_name);
|
|
||||||
}
|
|
||||||
printing_tcp = tcp;
|
printing_tcp = tcp;
|
||||||
|
|
||||||
tcp->s_prev_ent = NULL;
|
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)
|
if (tcp->sys_func_rval & RVAL_DECODED)
|
||||||
sys_res = tcp->sys_func_rval;
|
sys_res = tcp->sys_func_rval;
|
||||||
else
|
else
|
||||||
sys_res = tcp->s_ent->sys_func(tcp);
|
sys_res = tcp_sysent(tcp)->sys_func(tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
tprints(") ");
|
tprints(") ");
|
||||||
@ -1232,6 +1236,14 @@ get_syscall_regs(struct tcb *tcp)
|
|||||||
return get_regs(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:
|
* Returns:
|
||||||
* 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
|
* 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
|
||||||
@ -1243,6 +1255,10 @@ get_syscall_regs(struct tcb *tcp)
|
|||||||
int
|
int
|
||||||
get_scno(struct tcb *tcp)
|
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)
|
if (get_syscall_regs(tcp) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -1270,9 +1286,7 @@ get_scno(struct tcb *tcp)
|
|||||||
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
||||||
|
|
||||||
s->tcp = tcp;
|
s->tcp = tcp;
|
||||||
s->ent.nargs = MAX_ARGS;
|
s->ent = stub_sysent;
|
||||||
s->ent.sen = SEN_printargs;
|
|
||||||
s->ent.sys_func = printargs;
|
|
||||||
s->ent.sys_name = s->buf;
|
s->ent.sys_name = s->buf;
|
||||||
xsprintf(s->buf, "syscall_%#" PRI_klx, shuffle_scno(tcp->scno));
|
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)
|
for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
|
||||||
tcp->u_arg[i] = ptrace_sci.entry.args[i];
|
tcp->u_arg[i] = ptrace_sci.entry.args[i];
|
||||||
#if SUPPORTED_PERSONALITIES > 1
|
#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)
|
for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
|
||||||
tcp->u_arg[i] = (uint32_t) 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)
|
if (get_syscall_result_regs(tcp) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
get_error(tcp,
|
get_error(tcp,
|
||||||
(!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
|
(!(tcp_sysent(tcp)->sys_flags & SYSCALL_NEVER_FAILS)
|
||||||
|| syscall_tampered(tcp))
|
|| syscall_tampered(tcp))
|
||||||
&& !syscall_tampered_nofail(tcp));
|
&& !syscall_tampered_nofail(tcp));
|
||||||
|
|
||||||
@ -1376,7 +1390,7 @@ set_error(struct tcb *tcp, unsigned long new_error)
|
|||||||
if (ptrace_syscall_info_is_valid())
|
if (ptrace_syscall_info_is_valid())
|
||||||
tcp->u_rval = -1;
|
tcp->u_rval = -1;
|
||||||
else
|
else
|
||||||
get_error(tcp, !(tcp->s_ent->sys_flags &
|
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
|
||||||
SYSCALL_NEVER_FAILS));
|
SYSCALL_NEVER_FAILS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1400,7 +1414,7 @@ set_success(struct tcb *tcp, kernel_long_t new_rval)
|
|||||||
if (ptrace_syscall_info_is_valid())
|
if (ptrace_syscall_info_is_valid())
|
||||||
tcp->u_error = 0;
|
tcp->u_error = 0;
|
||||||
else
|
else
|
||||||
get_error(tcp, !(tcp->s_ent->sys_flags &
|
get_error(tcp, !(tcp_sysent(tcp)->sys_flags &
|
||||||
SYSCALL_NEVER_FAILS));
|
SYSCALL_NEVER_FAILS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@ -1,7 +1,6 @@
|
|||||||
childthread
|
childthread
|
||||||
clone
|
clone
|
||||||
leaderkill
|
leaderkill
|
||||||
many_looping_threads
|
|
||||||
mmap_offset_decode
|
mmap_offset_decode
|
||||||
mtd
|
mtd
|
||||||
seccomp
|
seccomp
|
||||||
|
@ -3,8 +3,7 @@ CFLAGS += -Wall
|
|||||||
PROGS = \
|
PROGS = \
|
||||||
sig skodic clone leaderkill childthread \
|
sig skodic clone leaderkill childthread \
|
||||||
sigkill_rain wait_must_be_interruptible threaded_execve \
|
sigkill_rain wait_must_be_interruptible threaded_execve \
|
||||||
mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap \
|
mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap
|
||||||
many_looping_threads
|
|
||||||
|
|
||||||
all: $(PROGS)
|
all: $(PROGS)
|
||||||
|
|
||||||
@ -12,8 +11,6 @@ leaderkill: LDFLAGS += -pthread
|
|||||||
|
|
||||||
childthread: LDFLAGS += -pthread
|
childthread: LDFLAGS += -pthread
|
||||||
|
|
||||||
many_looping_threads: LDFLAGS += -pthread
|
|
||||||
|
|
||||||
clean distclean:
|
clean distclean:
|
||||||
rm -f *.o core $(PROGS) *.gdb
|
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
|
llseek
|
||||||
localtime
|
localtime
|
||||||
lookup_dcookie
|
lookup_dcookie
|
||||||
|
looping_threads
|
||||||
lseek
|
lseek
|
||||||
lstat
|
lstat
|
||||||
lstat64
|
lstat64
|
||||||
|
@ -106,9 +106,11 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
|
|||||||
ioctl_perf-success \
|
ioctl_perf-success \
|
||||||
ioctl_rtc-v \
|
ioctl_rtc-v \
|
||||||
is_linux_mips_n64 \
|
is_linux_mips_n64 \
|
||||||
|
kill_child \
|
||||||
ksysent \
|
ksysent \
|
||||||
list_sigaction_signum \
|
list_sigaction_signum \
|
||||||
localtime \
|
localtime \
|
||||||
|
looping_threads \
|
||||||
mmsg-silent \
|
mmsg-silent \
|
||||||
mmsg_name-v \
|
mmsg_name-v \
|
||||||
msg_control-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
|
fstatat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
ftruncate64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
ftruncate64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
localtime_LDADD = $(clock_LIBS) $(LDADD)
|
localtime_LDADD = $(clock_LIBS) $(LDADD)
|
||||||
|
looping_threads_LDADD = -lpthread $(LDADD)
|
||||||
lstat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
lstat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
mmap64_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
|
mmap64_Xabbrev_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
@ -305,8 +308,10 @@ MISC_TESTS = \
|
|||||||
get_regs.test \
|
get_regs.test \
|
||||||
inject-nf.test \
|
inject-nf.test \
|
||||||
interactive_block.test \
|
interactive_block.test \
|
||||||
|
kill_child.test \
|
||||||
ksysent.test \
|
ksysent.test \
|
||||||
localtime.test \
|
localtime.test \
|
||||||
|
looping_threads.test \
|
||||||
opipe.test \
|
opipe.test \
|
||||||
options-syntax.test \
|
options-syntax.test \
|
||||||
pc.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
|
6
util.c
6
util.c
@ -1161,7 +1161,7 @@ print_array_ex(struct tcb *const tcp,
|
|||||||
int
|
int
|
||||||
printargs(struct tcb *tcp)
|
printargs(struct tcb *tcp)
|
||||||
{
|
{
|
||||||
const int n = tcp->s_ent->nargs;
|
const int n = n_args(tcp);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
tprintf("%s%#" PRI_klx, i ? ", " : "", tcp->u_arg[i]);
|
tprintf("%s%#" PRI_klx, i ? ", " : "", tcp->u_arg[i]);
|
||||||
@ -1171,7 +1171,7 @@ printargs(struct tcb *tcp)
|
|||||||
int
|
int
|
||||||
printargs_u(struct tcb *tcp)
|
printargs_u(struct tcb *tcp)
|
||||||
{
|
{
|
||||||
const int n = tcp->s_ent->nargs;
|
const int n = n_args(tcp);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
tprintf("%s%u", i ? ", " : "",
|
tprintf("%s%u", i ? ", " : "",
|
||||||
@ -1182,7 +1182,7 @@ printargs_u(struct tcb *tcp)
|
|||||||
int
|
int
|
||||||
printargs_d(struct tcb *tcp)
|
printargs_d(struct tcb *tcp)
|
||||||
{
|
{
|
||||||
const int n = tcp->s_ent->nargs;
|
const int n = n_args(tcp);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
tprintf("%s%d", i ? ", " : "",
|
tprintf("%s%d", i ? ", " : "",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user