summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Krapivensky <krapivenskiy.va@phystech.edu>2017-09-14 16:09:29 +0300
committerVictor Krapivensky <krapivenskiy.va@phystech.edu>2017-09-20 19:25:06 +0300
commit69e55ce1187311c48897e9aea1d935c92cf6471a (patch)
tree28e5c003f868a2bd78ae82a5dcb7d2cc287652ca
parent9c69fa1fe823d1ac08ee3baecb5afb9ac85d7748 (diff)
downloadstrace-69e55ce1187311c48897e9aea1d935c92cf6471a.tar.gz
tests: check Lua scripting supportVictorKrapivensky/luajit
* tests/.gitignore: Add lua. * tests/Makefile.am (check_PROGRAMS): Likewise. (LUAJIT_TESTS): New variable. (TESTS): Add LUAJIT_TESTS. (EXTRA_DIST): Add lua.sh, lua-basics.test, lua-qual.test, lua-tampering.test. * tests/lua-basics.test: New file. * tests/lua-qual.test: Likewise. * tests/lua-tampering.test: Likewise. * tests/lua.c: Likewise. * tests/lua.sh: Likewise.
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am13
-rwxr-xr-xtests/lua-basics.test301
-rwxr-xr-xtests/lua-qual.test58
-rwxr-xr-xtests/lua-tampering.test199
-rw-r--r--tests/lua.c115
-rw-r--r--tests/lua.sh11
7 files changed, 697 insertions, 1 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 982a9fcda..3556011cc 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -166,6 +166,7 @@ lookup_dcookie
lseek
lstat
lstat64
+lua
madvise
mbind
membarrier
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b4ba22a64..fbd367dbd 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -106,6 +106,7 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
ioctl_rtc-v \
is_linux_mips_n64 \
ksysent \
+ lua \
mmsg-silent \
mmsg_name-v \
msg_control-v \
@@ -185,6 +186,12 @@ else
LIBUNWIND_TESTS =
endif
+if USE_LUAJIT
+LUAJIT_TESTS = lua-basics.test lua-qual.test lua-tampering.test
+else
+LUAJIT_TESTS =
+endif
+
DECODER_TESTS = \
brk.test \
btrfs-v.test \
@@ -292,7 +299,7 @@ MISC_TESTS = \
threads-execve.test \
# end of MISC_TESTS
-TESTS = $(GEN_TESTS) $(DECODER_TESTS) $(MISC_TESTS) $(LIBUNWIND_TESTS)
+TESTS = $(GEN_TESTS) $(DECODER_TESTS) $(MISC_TESTS) $(LIBUNWIND_TESTS) $(LUAJIT_TESTS)
XFAIL_TESTS_ =
XFAIL_TESTS_m32 = $(LIBUNWIND_TESTS)
@@ -329,6 +336,10 @@ EXTRA_DIST = \
ipc_msgbuf.expected \
ksysent.sed \
lstatx.c \
+ lua.sh \
+ lua-basics.test \
+ lua-qual.test \
+ lua-tampering.test \
match.awk \
net.expected \
netlink_sock_diag-v.sh \
diff --git a/tests/lua-basics.test b/tests/lua-basics.test
new file mode 100755
index 000000000..9fa4ba7d6
--- /dev/null
+++ b/tests/lua-basics.test
@@ -0,0 +1,301 @@
+#!/bin/sh
+
+. "${srcdir=.}/lua.sh"
+
+run_with_script()
+{
+ run_strace_with_script -e trace=readv,writev ../lua "$@" > "$EXP"
+ match_diff "$LOG" "$EXP"
+}
+
+DATA=0123abcdefghijklnmop
+
+run_with_script $DATA $DATA <<EOF
+EOF
+
+run_with_script $DATA $DATA <<EOF
+-- check that callbacks are run in order they were registered
+ntotal = 10
+ncur = 0
+for i = 0, ntotal - 1 do
+ strace.at_exit(function()
+ assert(ncur == i)
+ ncur = ncur + 1
+ if ncur == ntotal then
+ assert(io.open('at-exit-marker', 'w'))
+ end
+ end)
+end
+EOF
+if ! [ -f at-exit-marker ]; then
+ fail_ "'at-exit-marker' does not exist"
+fi
+
+run_with_script $DATA $DATA <<EOF
+for i = 1, 10 do assert(strace.next_sc() == nil) end
+EOF
+
+run_with_script $DATA $DATA <<EOF
+ffi = require 'ffi'
+function check()
+ assert(not strace.C.inject_signo(1))
+ assert(not strace.C.inject_retval(0))
+ assert(strace.C.umove(0, 1, ffi.new('char [1]')) == -1)
+ assert(strace.C.umove_str(0, 1, ffi.new('char [1]')) < 0)
+ assert(strace.C.upoke(0, 1, 'x') == -1)
+ assert(not strace.C.path_match(ffi.new('const char *[1]', {'aaa'}), 1))
+end
+
+check()
+assert(strace.next_sc() == nil)
+check()
+EOF
+
+when_decls()
+{
+ echo "----- start of 'when_decls $*' -----"
+ case "$1" in
+ entering)
+ echo "\
+hooks_per_syscall = 1
+function make_state_checker()
+ return function(tcp)
+ assert(tcp ~= nil)
+ assert(strace.entering(tcp))
+ assert(not strace.exiting(tcp))
+ end
+end"
+ case "$2" in
+ s) echo "when_obj = 'entering'" ;;
+ t) echo "when_obj = {true, false}" ;;
+ esac
+ ;;
+ exiting)
+ echo "\
+hooks_per_syscall = 1
+function make_state_checker()
+ return function(tcp)
+ assert(tcp ~= nil)
+ assert(strace.exiting(tcp))
+ assert(not strace.entering(tcp))
+ end
+end"
+ case "$2" in
+ s) echo "when_obj = 'exiting'" ;;
+ t) echo "when_obj = {false, true}" ;;
+ esac
+ ;;
+ both)
+ echo "\
+hooks_per_syscall = 2
+function make_state_checker()
+ local expect_entry = true
+ return function(tcp)
+ assert(tcp ~= nil)
+ if expect_entry then
+ assert(strace.entering(tcp))
+ assert(not strace.exiting(tcp))
+ else
+ assert(strace.exiting(tcp))
+ assert(not strace.entering(tcp))
+ end
+ expect_entry = not expect_entry
+ end
+end"
+ case "$2" in
+ s) echo "when_obj = 'both'" ;;
+ t) echo "when_obj = {true, true}" ;;
+ esac
+ ;;
+ esac
+ echo "----- end of 'when_decls $*' -----"
+}
+
+make_hook_decl="\
+----- start of 'make_hook_decl' -----
+function make_hook(no_at_exit_hook, state_checker)
+ state_checker = state_checker or make_state_checker()
+ local nwritev, nreadv = 0, 0
+ local function check_nreadv()
+ assert(nreadv == hooks_per_syscall)
+ end
+ local function hook(tcp, kind)
+ state_checker(tcp)
+ local name = strace.get_sc_name(tcp.scno, tcp.currpers)
+ if kind == 'writev' or kind == 'readv' then
+ assert(name == kind)
+ elseif kind == '|' then
+ assert(name == 'writev' or name == 'readv')
+ else
+ assert(kind == '*', 'unknown \"kind\" value')
+ end
+ if name == 'writev' then
+ assert(nreadv == 0)
+ nwritev = nwritev + 1
+ elseif name == 'readv' then
+ assert(nwritev == hooks_per_syscall)
+ nreadv = nreadv + 1
+ end
+ end
+ if no_at_exit_hook then
+ return hook, check_nreadv
+ else
+ strace.at_exit(check_nreadv)
+ return hook
+ end
+end
+----- end of 'make_hook_decl' -----"
+
+for when in entering exiting both; do
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when t)
+$make_hook_decl
+strace.C.monitor_all(when_obj[1], when_obj[2])
+check_state = make_state_checker()
+tcp = strace.next_sc()
+check_state(tcp)
+assert(strace.get_sc_name(tcp.scno, tcp.currpers) == 'execve')
+if strace.entering(tcp) then
+ assert(strace.read_path(tcp.u_arg[0]) == '../lua')
+ assert(strace.path_match '../lua')
+ assert(strace.path_match{'aaa', '../lua', 'bbb'})
+ assert(not strace.path_match{'aaa', 'bbb'})
+ assert(not strace.path_match 'aaa')
+end
+hook, final_check = make_hook(true, check_state)
+while true do
+ tcp = strace.next_sc()
+ if tcp == nil then
+ break
+ end
+ hook(tcp, '*')
+end
+final_check()
+for i = 1, 10 do assert(strace.next_sc() == nil) end
+EOF
+
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when t)
+check_state = make_state_checker()
+for _, scname in ipairs{'writev', 'readv'} do
+ for p = 0, strace.npersonalities - 1 do
+ local scno = strace.get_scno(scname, p)
+ if scno then
+ assert(not not strace.C.monitor(scno, p, when_obj[1],
+ when_obj[2]))
+ end
+ end
+ for i = 1, hooks_per_syscall do
+ tcp = strace.next_sc()
+ check_state(tcp)
+ assert(strace.get_sc_name(tcp.scno, tcp.currpers) == scname)
+ end
+end
+for i = 1, 10 do assert(strace.next_sc() == nil) end
+EOF
+
+ for objtype in s t; do
+ for n in 1 5; do
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when $objtype)
+$make_hook_decl
+for i = 1, $n do
+ local hook = make_hook()
+ strace.hook('writev', when_obj, function(tcp) hook(tcp, 'writev') end)
+ strace.hook('readv', when_obj, function(tcp) hook(tcp, 'readv') end)
+end
+EOF
+ done
+
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when $objtype)
+$make_hook_decl
+hook = make_hook()
+strace.hook({'readv', 'writev'}, when_obj, function(tcp) hook(tcp, '|') end)
+EOF
+
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when $objtype)
+$make_hook_decl
+hook = make_hook()
+for p = 0, strace.npersonalities - 1 do
+ local t = {}
+ t[#t + 1] = strace.get_scno('writev', p)
+ t[#t + 1] = strace.get_scno('readv', p)
+ strace.hook_scno(t, p, when_obj, function(tcp) hook(tcp, '|') end)
+end
+EOF
+
+ run_with_script $DATA $DATA <<EOF
+$(when_decls $when $objtype)
+$make_hook_decl
+hook = make_hook()
+strace.hook_class({'%desc', '%network'}, when_obj,
+ function(tcp) hook(tcp, '*') end)
+EOF
+
+ done
+done
+
+run_with_script $DATA $DATA <<EOF
+ffi = require 'ffi'
+int = ffi.typeof('int')
+cstr = ffi.typeof('const char *')
+read_scno = assert(strace.get_scno('read', 0))
+
+assert(strace.get_sc_name( -1 , 0) == nil)
+assert(strace.get_sc_name(int(-1), 0) == nil)
+assert(strace.get_sc_name( strace.C.nsysent_vec[0] , 0) == nil)
+assert(strace.get_sc_name(int(strace.C.nsysent_vec[0]), 0) == nil)
+assert(strace.get_sc_name(int(read_scno), 0) == 'read')
+
+assert(strace.get_scno('' , 0) == nil)
+assert(strace.get_scno( 'some nonsense' , 0) == nil)
+assert(strace.get_scno(cstr('some nonsense'), 0) == nil)
+assert(strace.get_scno(cstr('read'), 0) == read_scno)
+
+assert(strace.get_err_name( 1 , 0) == 'EPERM')
+assert(strace.get_err_name(int(1), 0) == 'EPERM')
+assert(strace.get_err_name( -1 , 0) == nil)
+assert(strace.get_err_name(int(-1), 0) == nil)
+assert(strace.get_err_name( strace.C.nerrnoent_vec[0] , 0) == nil)
+assert(strace.get_err_name(int(strace.C.nerrnoent_vec[0]), 0) == nil)
+
+assert(strace.get_errno( '' , 0) == nil)
+assert(strace.get_errno(cstr(''), 0) == nil)
+assert(strace.get_errno('some nonsense', 0) == nil)
+assert(strace.get_errno( 'EPERM' , 0) == 1)
+assert(strace.get_errno(cstr('EPERM'), 0) == 1)
+
+assert(strace.get_sig_name( -1 , 0) == nil)
+assert(strace.get_sig_name(int(-1), 0) == nil)
+assert(strace.get_sig_name( 9 , 0) == 'SIGKILL')
+assert(strace.get_sig_name(int(9), 0) == 'SIGKILL')
+
+assert(strace.get_signo('', 0) == nil)
+assert(strace.get_signo( 'some nonsense' , 0) == nil)
+assert(strace.get_signo(cstr('some nonsense'), 0) == nil)
+assert(strace.get_signo( 'SIGKILL' , 0) == 9)
+assert(strace.get_signo(cstr('SIGKILL'), 0) == 9)
+
+assert(strace.get_class_flagbit('some nonsense') == nil)
+
+assert(strace.get_sc_flags(read_scno, 0) > 0)
+assert(strace.get_sc_flags(-1, 0) == nil)
+
+function check_ioctl_for(index, pers)
+ local entry = strace.C.ioctlent_vec[pers][index]
+ local s = ffi.string(entry.symbol)
+ assert(strace.get_ioctl_name( entry.code , pers) == s)
+ assert(strace.get_ioctl_name(tonumber(entry.code), pers) == s)
+ assert(strace.get_ioctl_code(entry.symbol, pers) == entry.code)
+ assert(strace.get_ioctl_code(s, pers) == entry.code)
+end
+check_ioctl_for(0, 0)
+n = strace.C.nioctlent_vec[0]
+check_ioctl_for(n / 2, 0)
+check_ioctl_for(n - 1, 0)
+assert(strace.get_ioctl_name(
+ strace.C.ioctlent_vec[0][n - 1].code + 1, 0) == nil)
+assert(strace.get_ioctl_code('some nonsense', 0) == nil)
+EOF
diff --git a/tests/lua-qual.test b/tests/lua-qual.test
new file mode 100755
index 000000000..5556d02bc
--- /dev/null
+++ b/tests/lua-qual.test
@@ -0,0 +1,58 @@
+#!/bin/sh
+set -e
+
+. "${srcdir=.}/lua.sh"
+
+run_prog ../umovestr
+pattern_abbrev_verbose='execve("\.\./umovestr", \["\.\./umovestr"\], 0x[[:xdigit:]]* /\* [[:digit:]]* vars \*/) = 0'
+pattern_nonabbrev_verbose='execve("\.\./umovestr", \["\.\./umovestr"\], \[".*\"\(\.\.\.\)\?\]) = 0'
+pattern_nonverbose='execve("\.\./umovestr", 0x[[:xdigit:]]*, 0x[[:xdigit:]]*) = 0'
+pattern_raw='execve(0x[[:xdigit:]]*, 0x[[:xdigit:]]*, 0x[[:xdigit:]]*) = 0'
+
+check_output_mismatch()
+{
+ local pattern
+ pattern="$1"; shift
+ run_strace_with_script "$@" ../umovestr
+ LC_ALL=C grep -x "$pattern" "$LOG" > /dev/null || {
+ printf '%s\n%s\n' \
+ 'Failed patterns of expected output:' "$pattern"
+ dump_log_and_fail_with "output mismatch"
+ }
+}
+
+gen_script()
+{
+ local not=
+ if [ "$1" = '!' ]; then
+ not=not
+ shift
+ fi
+ echo "\
+strace.C.monitor_all(true, false)
+while true do
+ tcp = strace.next_sc()
+ if tcp == nil then
+ break
+ end
+ strace.$1($not strace.get_sc_name(tcp.scno, tcp.currpers) == 'execve')
+end"
+}
+
+gen_script trace | check_output_mismatch "$pattern_abbrev_verbose"
+
+LC_ALL=C grep -v -x "$pattern_abbrev_verbose" "$LOG" |
+LC_ALL=C grep '^[[:alnum:]_]*(' > /dev/null &&
+ dump_log_and_fail_with "unexpected output"
+
+gen_script \! abbrev | check_output_mismatch "$pattern_nonabbrev_verbose"
+gen_script \! verbose | check_output_mismatch "$pattern_nonverbose"
+gen_script raw | check_output_mismatch "$pattern_raw"
+
+check_output_mismatch "$pattern_abbrev_verbose" -e trace=none <<EOF
+strace.hook_class('%process', 'entering', function(tcp) strace.trace() end)
+EOF
+LC_ALL=C grep '^chdir' "$LOG" > /dev/null &&
+ dump_log_and_fail_with "unexpected output"
+
+exit 0
diff --git a/tests/lua-tampering.test b/tests/lua-tampering.test
new file mode 100755
index 000000000..6808d2ad7
--- /dev/null
+++ b/tests/lua-tampering.test
@@ -0,0 +1,199 @@
+#!/bin/sh
+
+. "${srcdir=.}/lua.sh"
+
+run_with_script_and_opts()
+{
+ local opts; opts="$1"
+ shift
+ run_strace_with_script $opts -e trace=readv,writev ../lua "$@" > "$EXP"
+ match_diff "$LOG" "$EXP"
+}
+
+run_with_script()
+{
+ run_with_script_and_opts '' "$@"
+}
+
+DATA=0123abcdefghijklnmop
+
+check_prog awk
+
+iovec_decls="\
+----- start of 'iovec_decls' -----
+ffi.cdef[[
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+]]
+function decode_iovec(addr, pers)
+ local kulong = ffi.typeof('kernel_ulong_t')
+ if strace.C.pers_wordsize[pers] < ffi.sizeof(kulong) then
+ local v = strace.read_obj(addr, 'unsigned int [2]')
+ return kulong(v[0]), kulong(v[1])
+ else
+ local v = assert(strace.read_obj(addr, 'struct iovec'))
+ return strace.ptr_to_kulong(v.iov_base), kulong(v.iov_len)
+ end
+end
+----- end of 'iovec_decls' -----"
+
+run_with_script $DATA $DATA <<EOF
+ffi = require 'ffi'
+$iovec_decls
+function check_vecs(tcp, realsz)
+ assert(tcp.u_arg[2] == 1)
+ local v_base, v_len = decode_iovec(tcp.u_arg[1], tcp.currpers)
+ local n = realsz or v_len
+ assert(n == ${#DATA})
+ local buf = assert(strace.read_obj(v_base, 'char [?]', n))
+ assert(ffi.string(buf, n) == '$DATA')
+end
+strace.hook('writev', 'entering', check_vecs)
+strace.hook('readv', 'exiting', function(tcp)
+ assert(tcp.u_rval ~= -1)
+ check_vecs(tcp, tcp.u_rval)
+end)
+EOF
+
+for s in '' A AB ABC ABCD ABCDEFGH ABCDEFGHIJK; do
+ run_with_script $DATA "$s" <<EOF
+ffi = require 'ffi'
+$iovec_decls
+s = '$s'
+strace.hook('readv', 'entering', function(tcp)
+ assert(tcp.u_arg[2] == 1)
+ local v_base, v_len = decode_iovec(tcp.u_arg[1], tcp.currpers)
+ assert(v_len >= #s)
+ assert(strace.C.upoke(v_base, #s, s) == 0)
+ assert(strace.inject_retval(#s))
+end)
+EOF
+done
+
+for off in 1 2 3; do
+ for str in A ABC ABCD ABCDEFGH; do
+ expected=$(echo | awk -v d="$DATA" -v s="$str" -v off="$off" \
+ "{print substr(d,1,off) s substr(d,1+off+length(s))}")
+ run_with_script $DATA $expected <<EOF
+ffi = require 'ffi'
+$iovec_decls
+ins_str, ins_off = '$str', $off
+strace.hook('readv', 'exiting', function(tcp)
+ assert(tcp.u_arg[2] == 1)
+ local v_base, v_len = decode_iovec(tcp.u_arg[1], tcp.currpers)
+ assert(v_len >= #ins_str + ins_off)
+ assert(strace.write_obj(v_base + ins_off,
+ ffi.new('char [?]', #ins_str, ins_str)))
+end)
+EOF
+ done
+done
+
+run_with_script -EPIPE $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_error('EPIPE')
+ first = false
+ end
+end)
+EOF
+
+# check overwriting command-line fault injection options
+run_with_script_and_opts '-e inject=writev:error=ENOSYS:when=1' -EPIPE $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_error('EPIPE')
+ first = false
+ end
+end)
+EOF
+
+run_with_script -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_signal('SIGUSR1')
+ first = false
+ end
+end)
+EOF
+
+# check overwriting command-line signal injection options
+run_with_script_and_opts '-e inject=writev:signal=SIGUSR2:when=1' -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_signal('SIGUSR1')
+ first = false
+ end
+end)
+EOF
+
+run_with_script -EPIPE -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_signal('SIGUSR1')
+ strace.inject_error('EPIPE')
+ first = false
+ end
+end)
+EOF
+
+run_with_script -EPIPE -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_error('EPIPE')
+ strace.inject_signal('SIGUSR1')
+ first = false
+ end
+end)
+EOF
+
+# check playing nice with command-line injection options
+run_with_script_and_opts '-e inject=writev:signal=SIGUSR1:when=1' -EPIPE -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_error('EPIPE')
+ first = false
+ end
+end)
+EOF
+
+run_with_script_and_opts '-e inject=writev:error=EPIPE:when=1' -EPIPE -SIGUSR1 $DATA $DATA <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_signal('SIGUSR1')
+ first = false
+ end
+end)
+EOF
+
+expected=tESt
+run_with_script -EPIPE -SIGUSR1 $DATA $expected <<EOF
+first = true
+strace.hook('writev', 'entering', function(tcp)
+ if first then
+ strace.inject_error('EPIPE')
+ strace.inject_signal('SIGUSR1')
+ first = false
+ end
+end)
+ffi = require 'ffi'
+$iovec_decls
+s = '$expected'
+strace.hook('readv', 'entering', function(tcp)
+ assert(strace.inject_retval(#s))
+ assert(tcp.u_arg[2] == 1)
+ local v_base, v_len = decode_iovec(tcp.u_arg[1], tcp.currpers)
+ assert(v_len >= #s)
+ assert(strace.C.upoke(v_base, #s, s) == 0)
+end)
+EOF
diff --git a/tests/lua.c b/tests/lua.c
new file mode 100644
index 000000000..96102749c
--- /dev/null
+++ b/tests/lua.c
@@ -0,0 +1,115 @@
+#include "tests.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <errno.h>
+
+static volatile int got_sig = 0;
+
+static void
+handler(int sig)
+{
+ got_sig = 1;
+}
+
+static void
+expect_sigusr1_once(void)
+{
+ static bool first = true;
+ if (first) {
+ assert(got_sig);
+ got_sig = 0;
+ tprintf("--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_KERNEL} "
+ "---\n");
+ first = false;
+ } else
+ assert(!got_sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ tprintf("%s", "");
+
+ const struct sigaction act = { .sa_handler = handler };
+ if (sigaction(SIGUSR1, &act, NULL))
+ perror_msg_and_fail("sigaction");
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL))
+ perror_msg_and_fail("sigprocmask");
+
+ bool expect_sigusr1 = false;
+ bool expect_epipe = false;
+
+ int curarg;
+ for (curarg = 1; curarg < argc; ++curarg) {
+ if (strcmp(argv[curarg], "-EPIPE") == 0)
+ expect_epipe = true;
+ else if (strcmp(argv[curarg], "-SIGUSR1") == 0)
+ expect_sigusr1 = true;
+ else
+ break;
+ }
+ assert(argc - curarg == 2);
+ char *towrite = argv[curarg++];
+ size_t ntowrite = strlen(towrite);
+ char *toread = argv[curarg++];
+ size_t ntoread = strlen(toread);
+
+ int fds[2];
+ if (pipe(fds) < 0)
+ perror_msg_and_fail("pipe");
+
+ if (expect_epipe) {
+ assert(writev(fds[1], (const struct iovec [1]) {{
+ .iov_base = towrite,
+ .iov_len = ntowrite,
+ }}, 1) == -1 && errno == EPIPE);
+ tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%zu}], 1) = "
+ "-1 EPIPE (%s) (INJECTED)\n",
+ fds[1], towrite, ntowrite, strerror(EPIPE));
+ if (expect_sigusr1)
+ expect_sigusr1_once();
+ }
+
+ assert(writev(fds[1], (const struct iovec [1]) {{
+ .iov_base = towrite,
+ .iov_len = ntowrite,
+ }}, 1) == (ssize_t) ntowrite);
+ tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%zu}], 1) = %zu\n",
+ fds[1], towrite, ntowrite, ntowrite);
+ if (expect_sigusr1)
+ expect_sigusr1_once();
+
+ if (close(fds[1]) < 0)
+ perror_msg_and_fail("close");
+
+ char *buf = malloc(ntoread + 1);
+ if (!buf)
+ perror_msg_and_fail("malloc");
+
+ assert(readv(fds[0], (const struct iovec [1]) {{
+ .iov_base = buf,
+ .iov_len = ntoread + 1,
+ }}, 1) == (ssize_t) ntoread);
+ if (ntoread && memcmp(buf, toread, ntoread) != 0) {
+ buf[ntoread] = '\0';
+ error_msg_and_fail("expected to read '%s', got '%s'",
+ toread, buf);
+ return 1;
+ }
+ tprintf("readv(%d, [{iov_base=\"%s\", iov_len=%zu}], 1) = %zu%s\n",
+ fds[0], toread, ntoread + 1, ntoread,
+ ntoread == ntowrite ? "" : " (INJECTED)");
+
+ tprintf("+++ exited with 0 +++\n");
+ return 0;
+}
diff --git a/tests/lua.sh b/tests/lua.sh
new file mode 100644
index 000000000..6be797d34
--- /dev/null
+++ b/tests/lua.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+SCRIPTFILE=lua-script.lua
+
+run_strace_with_script()
+{
+ cat > "$SCRIPTFILE" || fail_ "cannot write $SCRIPTFILE"
+ run_strace -l "$SCRIPTFILE" "$@"
+}