lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <3907bc2a-0645-8d93-6ee5-3f99874e7022@gmail.com>
Date:   Tue, 23 May 2017 12:35:41 +0200
From:   "Michael Kerrisk (man-pages)" <mtk.manpages@...il.com>
To:     Stas Sergeev <stsp@...t.ru>
Cc:     mtk.manpages@...il.com, linux-man <linux-man@...r.kernel.org>,
        Andy Lutomirski <luto@...capital.net>,
        Oleg Nesterov <oleg@...hat.com>,
        lkml <linux-kernel@...r.kernel.org>
Subject: Re: Documenting sigaltstack SS_AUTODISRM

Hello Stas,

On 05/23/2017 01:36 AM, Stas Sergeev wrote:
> 22.05.2017 23:38, Michael Kerrisk (man-pages) пишет:
>> Stas,
>>
>> I have attempted to document the SS_AUTODISARM feature that you added
>> in Linux 4.7.
>>
>> Could you please take a look at the SS_AUTODISARM pieces in the
>> sigaltstack() man page below? There is also one FIXME that I would
>> like help with.
>>
>> It seems to me that the API has become rather odd now. It is no longer
>> possible to simply check whether code is executing on an alternative
>> stack by using
>>
>>      sigaltstack(NULL, &old_ss);
>>      if (old_ss.ss_flags & SS_ONSTACK)

> You mean, if SS_AUTODISARM was previously used, right?

Yes, that's what I meant.

> Because I don't think we broke the existing code, or did we?

Probably not, but it seems to me that there is some small
possibility that library code that makes use of sigaltstack()
to test whether a signal is being handled on an alternate signal 
stack, unaware that the main program employed SS_AUTODISARM,
could be confused/broken. I've no idea how likely this scenario 
is though. (I imagine it's rather unlikely.)

> I can vaguely recall that I was submitting the patches
> that were returning SS_ONSTACK even when SS_AUTODISARM
> was used, but they were considered too complex.
> This is possible to implement, but the agreement was
> that it is not a big deal.
> 
>>         ss.ss_flags
>>                This field contains either 0, or the following flag:
> Is this correct?
> AFAIK it can be SS_DISABLE too, 

It's correct in context. Just above in the man page its says:

       To  establish  a new alternate signal stack, the fields of this
       structure are set as follows:

The discussion of SS_DISABLE to disable the SS is lower in the page.

> and posix seems to allow any
> other value for enable, which can be (on linux) SS_ONSTACK,
> not only 0.

Yes, long ago someone got confused, as I've noted in a recently added
BUGS section in the page:

    BUGS
       In the lead up to the development  of  the  Linux  2.4  kernel,
       someone   got   confused  and  allowed  the  kernel  to  accept
       SS_ONSTACK in ss.ss_flags, which results behavior that  is  the
       same  as  when  ss_flags  is  0.  On other implementations, and
       according to POSIX.1, SS_ONSTACK appears  only  as  a  reported
       flag in old_ss.ss_flags.  There is no need ever to specify this
       flag in ss.ss_flags.

> And SS_AUTODISARM can be ORed with the value.
> 
>>                ┌─────────────────────────────────────────────────────┐
>>                │FIXME                                                │
>>                ├─────────────────────────────────────────────────────┤
>>                │Was it intended that one  can  set  up  a  different │
>>                │alternative signal stack in this scenario? (In pass‐ │
>>                │ing,  if  one  does  this,   the   sigaltstack(NULL, │
>>                │&old_ss)  now returns old_ss.ss_flags==SS_AUTODISARM │
>>                │rather  than  old_ss.ss_flags==SS_DISABLE.  The  API │
>>                │design here seems confusing...                       │
>>                └─────────────────────────────────────────────────────┘
> My memory may be wrong here, but I think setting
> up another alt stack was not supposed because the
> previous settings would be restored upon sighandler
> return. AFAIK I was trying to make up a proposal to
> get such attempts explicitly blocked rather than
> silently ignored, but again the simplicity was chosen.

So, I've done only limited experimentation here, but this is what 
I see in one experiment:

[[
* Set up two handlers for SIGX and SIGY, both using SA_ONSTACK.
* Establish alternate SS (1) using SS_AUTODISARM
  
  [SIGA is delivered]

* Handler for SIGA is called and handler is executed on alternate SS 1.
* The handler establishes a new alternate SS (2) with SS_AUTODISARM.

  [SIGB is delivered]

* Handler for SIGB is called and handler is executed on alternate SS 2.
* Handler for SIGB returns

  [SIGB is delivered]

* Handler for SIGB is called and handler is executed on alternate SS 2.
* Handler for SIGB returns
* Handler of SIGA returns

  [SIGA is delivered]

* Handler for SIGA is called and handler is executed on alternate SS 1.
]]

Summary: setting up another alternate signal stack seems to "work".
API history is littered with stories where users found out that
something unforeseen "worked", and so they used it. The question
is: what can go wrong if people do try using this "feature"?

>>         SS_AUTODISARM
>>                The  alternate  signal  stack  has  been  marked  to  be
>>                autodisarmed as described above.
> Initially this flag was supposed to be ORed with
> the old values. Your descrition is correct, but if
> more bit flags are added, this may became a
> problem, as you are always treating it as a separate
> value, not a bit flag.

Thanks for the confirmation.

At the end of this mail is a test program that I used to experiment
with this stuff. Here's a sample run that demonstrates the scenario
described above:

[[
$ ./t_sigaltstack_SS_AUTODISARM d 1
Autodisarm:                          YES
Try to establish new SS while on SS: YES
Initial SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]

Top of standard stack is near 0x7ffc2b16382c
Signal stack allocated at 0x69a000-0x6ae000
About to change SS: sp = 0x69a000; size = 81920; flags = [ SS_AUTODISARM ]
SS after change:    sp = 0x69a000; size = 81920; flags = [ SS_AUTODISARM ]

Send me a SIGQUIT (^\) or a SIGTSTP (^Z)
^\

Caught signal 3 (Quit)
Top of handler stack near       0x6adaa8
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]

Now try establishing a new signal stack while still executing on this one (1)
Signal stack allocated at 0x1baf000-0x1bc3000
Modifying SS to: sp = 0x1baf000; size = 81920; flags = [ SS_AUTODISARM ]
SS after update: sp = 0x1baf000; size = 81920; flags = [ SS_AUTODISARM ]

sleep(2) before displaying SS (send a signal now, if desired)
^Z

Caught signal 20 (Stopped)
Top of handler stack near      0x1bc2aa8
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]

sleep(2) before displaying SS (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
sleep(2) before return from handler (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
Returning from handler for signal 20
current SS: sp = 0x1baf000; size = 81920; flags = [ SS_AUTODISARM ]
sleep(2) before return from handler (send a signal now, if desired)
^Z

Caught signal 20 (Stopped)
Top of handler stack near      0x1bc2aa8
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]

sleep(2) before displaying SS (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
sleep(2) before return from handler (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
Returning from handler for signal 20
current SS: sp = 0x1baf000; size = 81920; flags = [ SS_AUTODISARM ]
Returning from handler for signal 3
===============================

Back in main
current SS: sp = 0x69a000; size = 81920; flags = [ SS_AUTODISARM ]
^\

Caught signal 3 (Quit)
Top of handler stack near       0x6adaa8
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]

sleep(2) before displaying SS (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
sleep(2) before return from handler (send a signal now, if desired)
current SS: sp = (nil); size = 0; flags = [ SS_DISABLE ]
Returning from handler for signal 3
===============================

Back in main
current SS: sp = 0x69a000; size = 81920; flags = [ SS_AUTODISARM ]
^C
]]
Cheers,

Michael

/*#* t_sigaltstack_SS_AUTODISARM.c

   COPYRIGHT-NOTICE
*/
#define _GNU_SOURCE
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define errExit(msg)    do { fprintf(stderr, "[FAILED] "); \
                             perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

#define errExitEN(en, msg) \
                        do { errno = en; perror(msg); \
                        exit(EXIT_FAILURE); } while (0)

#define SS_AUTODISARM   (1U << 31)

static int tryNewStack = 0;

static void
showSS(char *prefix, stack_t * osp)
{
    printf("%ssp = %p; size = %ld; flags = ", prefix,
           osp->ss_sp, (long) osp->ss_size);
    if (osp->ss_flags != 0) {
        printf("[ ");
        if (osp->ss_flags & SS_ONSTACK)
            printf("SS_ONSTACK ");
        if (osp->ss_flags & SS_DISABLE)
            printf("SS_DISABLE ");
        if (osp->ss_flags & SS_AUTODISARM)
            printf("SS_AUTODISARM ");
        printf("]");
    }
    printf("\n");
}

static void
showCurrentSS(char *msg)
{
    stack_t os;

    if (sigaltstack(NULL, &os) == -1)
        errExit("sigaltstack");
    showSS((msg != NULL) ? msg : "current SS: ", &os);
}

static const size_t stackSize = 10 * SIGSTKSZ;

static void
allocateSS(void **ss_sp)
{
    int s;
    void *p;

    s = posix_memalign(ss_sp, 4096, stackSize);
    if (s != 0)
        errExitEN(s, "posix_memalign");
    printf("Signal stack allocated at %p-%p\n",
           *ss_sp, (char *) *ss_sp + stackSize);
    for (s = 0; s < 256; s++)
        posix_memalign(&p, 4096, stackSize);
}

static void
handler(int sig)
{
    int x;

    printf("\n\nCaught signal %d (%s)\n", sig, strsignal(sig));
    printf("Top of handler stack near     %10p\n", (void *) &x);
    fflush(NULL);

    showCurrentSS(NULL);

    if (tryNewStack > 0) {
        stack_t sigstack, os;
        int s;

        printf("\nNow try establishing a new signal stack "
               "while still executing on this one (%d)\n", tryNewStack);

        tryNewStack--;

        allocateSS(&sigstack.ss_sp);
        sigstack.ss_size = stackSize;
        sigstack.ss_flags = SS_AUTODISARM;
        showSS("Modifying SS to: ", &sigstack);

        s = sigaltstack(&sigstack, NULL);
        if (s == -1) {
            fprintf(stderr, "[FAILED] ");
            perror("sigaltstack");
        }
        if (sigaltstack(NULL, &os) == -1)
            errExit("sigaltstack");
        else
            showCurrentSS("SS after update: ");
    }
    printf("\nsleep(2) before displaying SS "
            "(send a signal now, if desired)\n");
    sleep(2);
    showCurrentSS(NULL);
    printf("sleep(2) before return from handler"
            "(send a signal now, if desired)\n");
    sleep(2);
    showCurrentSS(NULL);
    printf("Returning from handler for signal %d\n", sig);
}

int
main(int argc, char *argv[])
{
    stack_t sigstack;
    struct sigaction sa;
    int j;
    int autodisarm;

    autodisarm = 0;
    if (argc > 1) {
        if (strchr(argv[1], 'd') != NULL)
            autodisarm = 1;
    }

    tryNewStack = (argc > 2) ? atoi(argv[2]) : 0;

    printf("Autodisarm:                          %s\n",
           autodisarm ? "YES" : "NO");
    printf("Try to establish new SS while on SS: %s\n",
           tryNewStack ? "YES" : "NO");

    showCurrentSS("Initial SS: ");

    printf("\nTop of standard stack is near %10p\n", (void *) &j);

    /* Allocate alternate stack and inform kernel of its existence */

    allocateSS(&sigstack.ss_sp);
    sigstack.ss_size = stackSize;
    sigstack.ss_flags = autodisarm ? SS_AUTODISARM : 0;
    showSS("About to change SS: ", &sigstack);
    if (sigaltstack(&sigstack, NULL) == -1)
        errExit("sigaltstack");

    showCurrentSS("SS after change:    ");

    /* Establish handlers for SIGQUIT and SIGTSTP */

    sa.sa_handler = handler;    
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK | SA_NODEFER;
        /* SA_NODEFER to allow experiments with interrupted handlers */

    if (sigaction(SIGQUIT, &sa, NULL) == -1)
        errExit("sigaction");
    if (sigaction(SIGTSTP, &sa, NULL) == -1)
        errExit("sigaction");

    printf("\nSend me a SIGQUIT (^\\) or a SIGTSTP (^Z)\n");

    for (;;) {
        pause();
        printf("===============================\n");
        printf("\nBack in main\n");

        showCurrentSS(NULL);
    }
}



-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ