From 11656206fff75205a8abab60363eb2f682ff678c Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 18 Feb 2016 00:08:30 +0000 Subject: [PATCH] Fix corner cases of rt_sigpending syscall decoder * signal (print_sigset_addr_len_limit): New function, cloned from print_sigset_addr_len with added minimal length argument. Treat length less than minimal length or greater than NSIG/8 as invalid. Do not align length to 4-byte boundary. Align destination buffer to 4-byte boundary and initialize it with zeroes so that subsequent call to sprintsigmask_n will not access uninitialized data. (print_sigset_addr_len): Turn into a wrapper around print_sigset_addr_len_limit with current_wordsize as a minimal length argument. (SYS_FUNC(rt_sigpending)): Call print_sigset_addr_len_limit instead of print_sigset_addr_len with 1 as a minimal length argument. * tests/rt_sigpending.c: New file. * tests/rt_sigpending.test: New test. * tests/.gitignore: Add rt_sigpending. * tests/Makefile.am (check_PROGRAMS): Likewise. (TESTS): Add rt_sigpending.test. --- signal.c | 26 ++++---- tests/.gitignore | 1 + tests/Makefile.am | 2 + tests/rt_sigpending.c | 125 +++++++++++++++++++++++++++++++++++++++ tests/rt_sigpending.test | 11 ++++ 5 files changed, 153 insertions(+), 12 deletions(-) create mode 100644 tests/rt_sigpending.c create mode 100755 tests/rt_sigpending.test diff --git a/signal.c b/signal.c index 45509dce..c13308d9 100644 --- a/signal.c +++ b/signal.c @@ -223,28 +223,29 @@ printsignal(int nr) tprints(signame(nr)); } -void -print_sigset_addr_len(struct tcb *tcp, long addr, long len) +static void +print_sigset_addr_len_limit(struct tcb *tcp, long addr, long len, long min_len) { - char mask[NSIG / 8]; - - /* Here len is usually equals NSIG / 8 or current_wordsize. + /* + * Here len is usually equal to NSIG / 8 or current_wordsize. * But we code this defensively: */ - if (len < 0) { + if (len < min_len || len > NSIG / 8) { printaddr(addr); return; } - if (len >= NSIG / 8) - len = NSIG / 8; - else - len = (len + 3) & ~3; - + int mask[NSIG / 8 / sizeof(int)] = {}; if (umoven_or_printaddr(tcp, addr, len, mask)) return; tprints(sprintsigmask_n("", mask, len)); } +void +print_sigset_addr_len(struct tcb *tcp, long addr, long len) +{ + print_sigset_addr_len_limit(tcp, addr, len, current_wordsize); +} + SYS_FUNC(sigsetmask) { if (entering(tcp)) { @@ -613,7 +614,8 @@ SYS_FUNC(rt_sigpending) * This allows non-rt sigpending() syscall * to reuse rt_sigpending() code in kernel. */ - print_sigset_addr_len(tcp, tcp->u_arg[0], tcp->u_arg[1]); + print_sigset_addr_len_limit(tcp, tcp->u_arg[0], + tcp->u_arg[1], 1); tprintf(", %lu", tcp->u_arg[1]); } return 0; diff --git a/tests/.gitignore b/tests/.gitignore index 5671978e..dc8b1330 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -81,6 +81,7 @@ readlinkat readv recvmsg restart_syscall +rt_sigpending rt_sigqueueinfo sched_xetaffinity sched_xetattr diff --git a/tests/Makefile.am b/tests/Makefile.am index d8a9bc2a..db23808d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -129,6 +129,7 @@ check_PROGRAMS = \ readv \ recvmsg \ restart_syscall \ + rt_sigpending \ rt_sigqueueinfo \ sched_xetaffinity \ sched_xetattr \ @@ -279,6 +280,7 @@ TESTS = \ readlinkat.test \ readv.test \ recvmsg.test \ + rt_sigpending.test \ rt_sigqueueinfo.test \ sched_xetaffinity.test \ sched_xetattr.test \ diff --git a/tests/rt_sigpending.c b/tests/rt_sigpending.c new file mode 100644 index 00000000..16dfa7e2 --- /dev/null +++ b/tests/rt_sigpending.c @@ -0,0 +1,125 @@ +/* + * This file is part of rt_sigpending strace test. + * + * Copyright (c) 2016 Dmitry V. Levin + * 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 + +#ifdef __NR_rt_sigpending + +# include +# include +# include +# include +# include + +static long +k_sigpending(void *const set, const unsigned long size) +{ + return syscall(__NR_rt_sigpending, set, size); +} + +static void +iterate(const char *const text, unsigned int size, void *set) +{ + for (;;) { + if (k_sigpending(set, size)) + perror_msg_and_fail("rt_sigpending"); + if (size) { +#if WORDS_BIGENDIAN + if (size < sizeof(long)) + tprintf("rt_sigpending(%s, %u) = 0\n", + "[]", size); + else +#endif + tprintf("rt_sigpending(%s, %u) = 0\n", + text, size); + } else { + tprintf("rt_sigpending(%p, %u) = 0\n", set, size); + break; + } + size >>= 1; + set += size; + } +} + +int +main(void) +{ + tprintf("%s", ""); + + const unsigned int big_size = 1024 / 8; + void *k_set = tail_alloc(big_size); + sigset_t *const libc_set = tail_alloc(sizeof(sigset_t)); + + sigemptyset(libc_set); + if (sigprocmask(SIG_SETMASK, libc_set, NULL)) + perror_msg_and_fail("sigprocmask"); + + memset(k_set, 0, big_size); + unsigned int set_size = big_size; + for (; set_size; set_size >>= 1, k_set += set_size) { + if (!k_sigpending(k_set, set_size)) + break; + tprintf("rt_sigpending(%p, %u) = -1 EINVAL (%m)\n", + k_set, set_size); + } + if (!set_size) + perror_msg_and_fail("rt_sigpending"); + tprintf("rt_sigpending(%s, %u) = 0\n", "[]", set_size); + + iterate("[]", set_size >> 1, k_set + (set_size >> 1)); + + void *const efault = k_set + (set_size >> 1); + assert(k_sigpending(efault, set_size) == -1); + tprintf("rt_sigpending(%p, %u) = -1 EFAULT (%m)\n", + efault, set_size); + + sigaddset(libc_set, SIGHUP); + if (sigprocmask(SIG_SETMASK, libc_set, NULL)) + perror_msg_and_fail("sigprocmask"); + raise(SIGHUP); + + iterate("[HUP]", set_size, k_set); + + sigaddset(libc_set, SIGINT); + if (sigprocmask(SIG_SETMASK, libc_set, NULL)) + perror_msg_and_fail("sigprocmask"); + raise(SIGINT); + + iterate("[HUP INT]", set_size, k_set); + + tprintf("+++ exited with 0 +++\n"); + return 0; +} + +#else + +SKIP_MAIN_UNDEFINED("__NR_rt_sigpending") + +#endif diff --git a/tests/rt_sigpending.test b/tests/rt_sigpending.test new file mode 100755 index 00000000..5218563a --- /dev/null +++ b/tests/rt_sigpending.test @@ -0,0 +1,11 @@ +#!/bin/sh + +# Check rt_sigpending syscall decoding. + +. "${srcdir=.}/init.sh" + +run_prog > /dev/null +OUT="$LOG.out" +run_strace -a20 -ert_sigpending $args > "$OUT" +match_diff "$LOG" "$OUT" +rm -f "$OUT" -- 2.40.0