From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1791) id 2F1F8384B0C1; Tue, 21 Apr 2020 18:12:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2F1F8384B0C1 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Adhemerval Zanella To: glibc-cvs@sourceware.org Subject: [glibc] signal: Only handle on NSIG signals on signal functions (BZ #25657) X-Act-Checkin: glibc X-Git-Author: Adhemerval Zanella X-Git-Refname: refs/heads/master X-Git-Oldrev: 2f6fa80147f0cf74c0d411a0e07c5655deb436b3 X-Git-Newrev: 566e10aa7292bacd74d229ca6f2cd9e8c8ba8748 Message-Id: <20200421181222.2F1F8384B0C1@sourceware.org> Date: Tue, 21 Apr 2020 18:12:22 +0000 (GMT) X-BeenThere: glibc-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Glibc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Apr 2020 18:12:22 -0000 https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=566e10aa7292bacd74d229ca6f2cd9e8c8ba8748 commit 566e10aa7292bacd74d229ca6f2cd9e8c8ba8748 Author: Adhemerval Zanella Date: Thu Mar 12 09:50:19 2020 -0300 signal: Only handle on NSIG signals on signal functions (BZ #25657) The upper bits of the sigset_t s not fully initialized in the signal mask calls that return information from kernel (sigprocmask, sigpending, and pthread_sigmask), since the exported sigset_t size (1024 bits) is larger than Linux support one (64 or 128 bits). It might make sigisemptyset/sigorset/sigandset fail if the mask is filled prior the call. This patch changes the internal signal function to handle up to supported Linux signal number (_NSIG), the remaining bits are untouched. Checked on x86_64-linux-gnu and i686-linux-gnu. Diff: --- nptl/Makefile | 2 +- nptl/pthread_sigmask.c | 7 +- nptl/tst-signal8.c | 62 ++++++++++++++ signal/Makefile | 1 + signal/sigsetops.c | 12 +-- signal/tst-sigisemptyset.c | 95 +++++++++++++++++++++ sysdeps/unix/sysv/linux/sigpending.c | 6 +- sysdeps/unix/sysv/linux/sigsetops.h | 155 +++++++++++++++++------------------ 8 files changed, 246 insertions(+), 94 deletions(-) diff --git a/nptl/Makefile b/nptl/Makefile index 4816fa254e..33fa03807e 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -293,7 +293,7 @@ tests = tst-attr2 tst-attr3 tst-default-attr \ tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 tst-cleanup4 \ tst-flock1 tst-flock2 \ tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \ - tst-signal6 \ + tst-signal6 tst-signal8 \ tst-exec1 tst-exec2 tst-exec3 tst-exec4 tst-exec5 \ tst-exit1 tst-exit2 tst-exit3 \ tst-stdio1 tst-stdio2 \ diff --git a/nptl/pthread_sigmask.c b/nptl/pthread_sigmask.c index c6c6e83c08..d266d296c5 100644 --- a/nptl/pthread_sigmask.c +++ b/nptl/pthread_sigmask.c @@ -29,12 +29,11 @@ __pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask) /* The only thing we have to make sure here is that SIGCANCEL and SIGSETXID is not blocked. */ if (newmask != NULL - && (__builtin_expect (__sigismember (newmask, SIGCANCEL), 0) - || __builtin_expect (__sigismember (newmask, SIGSETXID), 0))) + && (__glibc_unlikely (__sigismember (newmask, SIGCANCEL)) + || __glibc_unlikely (__sigismember (newmask, SIGSETXID)))) { local_newmask = *newmask; - __sigdelset (&local_newmask, SIGCANCEL); - __sigdelset (&local_newmask, SIGSETXID); + __clear_internal_signals (&local_newmask); newmask = &local_newmask; } diff --git a/nptl/tst-signal8.c b/nptl/tst-signal8.c new file mode 100644 index 0000000000..9da7e5ef07 --- /dev/null +++ b/nptl/tst-signal8.c @@ -0,0 +1,62 @@ +/* Tests for sigisemptyset and pthread_sigmask. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +static void * +tf (void *arg) +{ + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (pthread_sigmask (SIG_BLOCK, 0, &set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + { + sigset_t set; + sigfillset (&set); + TEST_COMPARE (pthread_sigmask (SIG_BLOCK, 0, &set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + return NULL; +} + +static int +do_test (void) +{ + /* Ensure current SIG_BLOCK mask empty. */ + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (sigprocmask (SIG_BLOCK, &set, 0), 0); + } + + { + pthread_t thr = xpthread_create (NULL, tf, NULL); + xpthread_join (thr); + } + + return 0; +} + +#include diff --git a/signal/Makefile b/signal/Makefile index 37de438bba..f3c19e2992 100644 --- a/signal/Makefile +++ b/signal/Makefile @@ -49,6 +49,7 @@ tests := tst-signal tst-sigset tst-sigsimple tst-raise tst-sigset2 \ tst-sigwait-eintr tst-sigaction \ tst-minsigstksz-1 tst-minsigstksz-2 tst-minsigstksz-3 \ tst-minsigstksz-3a tst-minsigstksz-4 \ + tst-sigisemptyset include ../Rules diff --git a/signal/sigsetops.c b/signal/sigsetops.c index eb89e6780e..1165377876 100644 --- a/signal/sigsetops.c +++ b/signal/sigsetops.c @@ -26,28 +26,28 @@ int attribute_compat_text_section -(__sigismember) (const __sigset_t *set, int sig) +__sigismember_compat (const __sigset_t *set, int sig) { return __sigismember (set, sig); } -compat_symbol (libc, __sigismember, __sigismember, GLIBC_2_0); +compat_symbol (libc, __sigismember_compat, __sigismember, GLIBC_2_0); int attribute_compat_text_section -(__sigaddset) (__sigset_t *set, int sig) +__sigaddset_compat (__sigset_t *set, int sig) { __sigaddset (set, sig); return 0; } -compat_symbol (libc, __sigaddset, __sigaddset, GLIBC_2_0); +compat_symbol (libc, __sigaddset_compat, __sigaddset, GLIBC_2_0); int attribute_compat_text_section -(__sigdelset) (__sigset_t *set, int sig) +__sigdelset_compat (__sigset_t *set, int sig) { __sigdelset (set, sig); return 0; } -compat_symbol (libc, __sigdelset, __sigdelset, GLIBC_2_0); +compat_symbol (libc, __sigdelset_compat, __sigdelset, GLIBC_2_0); #endif diff --git a/signal/tst-sigisemptyset.c b/signal/tst-sigisemptyset.c new file mode 100644 index 0000000000..9ed95496f2 --- /dev/null +++ b/signal/tst-sigisemptyset.c @@ -0,0 +1,95 @@ +/* Tests for sigisemptyset/sigorset/sigandset. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +static int +do_test (void) +{ + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + { + sigset_t set; + sigfillset (&set); + TEST_COMPARE (sigisemptyset (&set), 0); + } + + { + sigset_t setfill, setempty, set; + sigfillset (&setfill); + sigemptyset (&setempty); + + sigorset (&set, &setfill, &setempty); + TEST_COMPARE (sigisemptyset (&set), 0); + + sigandset (&set, &setfill, &setempty); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + /* Ensure current SIG_BLOCK mask empty. */ + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (sigprocmask (SIG_BLOCK, &set, 0), 0); + } + + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (sigprocmask (SIG_BLOCK, 0, &set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + { + sigset_t set; + sigfillset (&set); + TEST_COMPARE (sigprocmask (SIG_BLOCK, 0, &set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + /* Block all signals. */ + { + sigset_t set; + sigfillset (&set); + TEST_COMPARE (sigprocmask (SIG_BLOCK, &set, 0), 0); + } + + { + sigset_t set; + sigemptyset (&set); + TEST_COMPARE (sigpending (&set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + { + sigset_t set; + sigfillset (&set); + TEST_COMPARE (sigpending (&set), 0); + TEST_COMPARE (sigisemptyset (&set), 1); + } + + return 0; +} + +#include diff --git a/sysdeps/unix/sysv/linux/sigpending.c b/sysdeps/unix/sysv/linux/sigpending.c index f6bedb5182..458a3cf99e 100644 --- a/sysdeps/unix/sysv/linux/sigpending.c +++ b/sysdeps/unix/sysv/linux/sigpending.c @@ -15,13 +15,9 @@ License along with the GNU C Library; if not, see . */ -#include #include -#include - #include -#include - +#include /* Change the set of blocked signals to SET, wait until a signal arrives, and restore the set of blocked signals. */ diff --git a/sysdeps/unix/sysv/linux/sigsetops.h b/sysdeps/unix/sysv/linux/sigsetops.h index cb97c98b10..db8f378cf0 100644 --- a/sysdeps/unix/sysv/linux/sigsetops.h +++ b/sysdeps/unix/sysv/linux/sigsetops.h @@ -26,83 +26,82 @@ (((unsigned long int) 1) << (((sig) - 1) % (8 * sizeof (unsigned long int)))) /* Return the word index for SIG. */ -# define __sigword(sig) (((sig) - 1) / (8 * sizeof (unsigned long int))) - -# define __sigemptyset(set) \ - (__extension__ ({ \ - int __cnt = _SIGSET_NWORDS; \ - sigset_t *__set = (set); \ - while (--__cnt >= 0) \ - __set->__val[__cnt] = 0; \ - (void)0; \ - })) - -# define __sigfillset(set) \ - (__extension__ ({ \ - int __cnt = _SIGSET_NWORDS; \ - sigset_t *__set = (set); \ - while (--__cnt >= 0) \ - __set->__val[__cnt] = ~0UL; \ - (void)0; \ - })) - -# define __sigisemptyset(set) \ - (__extension__ ({ \ - int __cnt = _SIGSET_NWORDS; \ - const sigset_t *__set = (set); \ - int __ret = __set->__val[--__cnt]; \ - while (!__ret && --__cnt >= 0) \ - __ret = __set->__val[__cnt]; \ - __ret == 0; \ - })) - -# define __sigandset(dest, left, right) \ - (__extension__ ({ \ - int __cnt = _SIGSET_NWORDS; \ - sigset_t *__dest = (dest); \ - const sigset_t *__left = (left); \ - const sigset_t *__right = (right); \ - while (--__cnt >= 0) \ - __dest->__val[__cnt] = (__left->__val[__cnt] \ - & __right->__val[__cnt]); \ - (void)0; \ - })) - -# define __sigorset(dest, left, right) \ - (__extension__ ({ \ - int __cnt = _SIGSET_NWORDS; \ - sigset_t *__dest = (dest); \ - const sigset_t *__left = (left); \ - const sigset_t *__right = (right); \ - while (--__cnt >= 0) \ - __dest->__val[__cnt] = (__left->__val[__cnt] \ - | __right->__val[__cnt]); \ - (void)0; \ - })) - -/* These macros needn't check for a bogus signal number; - error checking is done in the non-__ versions. */ -# define __sigismember(set, sig) \ - (__extension__ ({ \ - unsigned long int __mask = __sigmask (sig); \ - unsigned long int __word = __sigword (sig); \ - (set)->__val[__word] & __mask ? 1 : 0; \ - })) - -# define __sigaddset(set, sig) \ - (__extension__ ({ \ - unsigned long int __mask = __sigmask (sig); \ - unsigned long int __word = __sigword (sig); \ - (set)->__val[__word] |= __mask; \ - (void)0; \ - })) - -# define __sigdelset(set, sig) \ - (__extension__ ({ \ - unsigned long int __mask = __sigmask (sig); \ - unsigned long int __word = __sigword (sig); \ - (set)->__val[__word] &= ~__mask; \ - (void)0; \ - })) +static inline unsigned long int +__sigword (int sig) +{ + return (sig - 1) / (8 * sizeof (unsigned long int)); +} + +/* Linux sig* functions only handle up to __NSIG_WORDS words instead of + full _SIGSET_NWORDS sigset size. The signal numbers are 1-based, and + bit 0 of a signal mask is for signal 1. */ + +# define __NSIG_WORDS (_NSIG / (8 * sizeof (unsigned long int ))) + +static inline void +__sigemptyset (sigset_t *set) +{ + int cnt = __NSIG_WORDS; + while (--cnt >= 0) + set->__val[cnt] = 0; +} + +static inline void +__sigfillset (sigset_t *set) +{ + int cnt = __NSIG_WORDS; + while (--cnt >= 0) + set->__val[cnt] = ~0UL; +} + +static inline int +__sigisemptyset (const sigset_t *set) +{ + int cnt = __NSIG_WORDS; + int ret = set->__val[--cnt]; + while (ret == 0 && --cnt >= 0) + ret = set->__val[cnt]; + return ret == 0; +} + +static inline void +__sigandset (sigset_t *dest, const sigset_t *left, const sigset_t *right) +{ + int cnt = __NSIG_WORDS; + while (--cnt >= 0) + dest->__val[cnt] = left->__val[cnt] & right->__val[cnt]; +} + +static inline void +__sigorset (sigset_t *dest, const sigset_t *left, const sigset_t *right) +{ + int cnt = __NSIG_WORDS; + while (--cnt >= 0) + dest->__val[cnt] = left->__val[cnt] | right->__val[cnt]; +} + +static inline int +__sigismember (const sigset_t *set, int sig) +{ + unsigned long int mask = __sigmask (sig); + unsigned long int word = __sigword (sig); + return set->__val[word] & mask ? 1 : 0; +} + +static inline void +__sigaddset (sigset_t *set, int sig) +{ + unsigned long int mask = __sigmask (sig); + unsigned long int word = __sigword (sig); + set->__val[word] |= mask; +} + +static inline void +__sigdelset (sigset_t *set, int sig) +{ + unsigned long int mask = __sigmask (sig); + unsigned long int word = __sigword (sig); + set->__val[word] &= ~mask; +} #endif /* bits/sigsetops.h */