poke-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: fix invalid use of siglongjmp due to threads


From: Jose E. Marchesi
Subject: Re: fix invalid use of siglongjmp due to threads
Date: Sun, 28 Jun 2020 16:43:30 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

Hi Bruno.

Interesting problem.
OK for master.
Thanks!
    
    poke is a multi-threaded program. I see in gdb 3 threads from libgc:
    
    (gdb) info threads 
      Id   Target Id         Frame 
    * 1    Thread 0x7ffff7fc4700 (LWP 16429) "poke" 0x00007ffff727051d in read 
() at ../sysdeps/unix/syscall-template.S:84
      2    Thread 0x7ffff5ae2700 (LWP 16433) "poke" 
pthread_cond_wait@@GLIBC_2.3.2 ()
        at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
      3    Thread 0x7ffff52e1700 (LWP 16434) "poke" 
pthread_cond_wait@@GLIBC_2.3.2 ()
        at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
      4    Thread 0x7ffff4ae0700 (LWP 16435) "poke" 
pthread_cond_wait@@GLIBC_2.3.2 ()
        at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
      5    Thread 0x7ffff42df700 (LWP 16436) "poke" 0x00007ffff6f935d3 in 
select () at ../sysdeps/unix/syscall-template.S:84
    (gdb) where
    #0  0x00007ffff727051d in read () at ../sysdeps/unix/syscall-template.S:84
    #1  0x00007ffff7948a2d in rl_getc () from 
/lib/x86_64-linux-gnu/libreadline.so.6
    #2  0x00000000004053db in poke_getc (stream=0x7ffff725a8e0 
<_IO_2_1_stdin_>) at pk-repl.c:142
    #3  0x00007ffff79492ad in rl_read_key () from 
/lib/x86_64-linux-gnu/libreadline.so.6
    #4  0x00007ffff7932dd2 in readline_internal_char () from 
/lib/x86_64-linux-gnu/libreadline.so.6
    #5  0x00007ffff7933545 in readline () from 
/lib/x86_64-linux-gnu/libreadline.so.6
    #6  0x0000000000405878 in pk_repl () at pk-repl.c:313
    #7  0x0000000000404d0c in main (argc=2, argv=0x7fffffffd808) at poke.c:683
    (gdb) thread 2
    [Switching to thread 2 (Thread 0x7ffff5ae2700 (LWP 16433))]
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    185     ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S: Datei oder 
Verzeichnis nicht gefunden.
    (gdb) where   
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    #1  0x00007ffff7b87937 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #2  0x00007ffff7b7dc3a in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #3  0x00007ffff7b85ebc in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #4  0x00007ffff72676ba in start_thread (arg=0x7ffff5ae2700) at 
pthread_create.c:333
    #5  0x00007ffff6f9d41d in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:109
    (gdb) thread 3
    [Switching to thread 3 (Thread 0x7ffff52e1700 (LWP 16434))]
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    185     in ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
    (gdb) where   
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    #1  0x00007ffff7b87937 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #2  0x00007ffff7b7dc3a in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #3  0x00007ffff7b85ebc in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #4  0x00007ffff72676ba in start_thread (arg=0x7ffff52e1700) at 
pthread_create.c:333
    #5  0x00007ffff6f9d41d in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:109
    (gdb) thread 4
    [Switching to thread 4 (Thread 0x7ffff4ae0700 (LWP 16435))]
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    185     in ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
    (gdb) where   
    #0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
    #1  0x00007ffff7b87937 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #2  0x00007ffff7b7dc3a in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #3  0x00007ffff7b85ebc in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
    #4  0x00007ffff72676ba in start_thread (arg=0x7ffff4ae0700) at 
pthread_create.c:333
    #5  0x00007ffff6f9d41d in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:109
    
    And even if we soon won't use libgc any more, other libraries can create
    threads at their own discretion. (E.g. macOS libc does this.)
    
    But in a multithreaded program, any of the threads might get to execute any
    signal handler. (Since you don't control the creation of the other threads,
    and don't even have a way to enumerate them, you can't call pthread_sigmask
    on them.)
    
    In such a situation, siglongjmp may cause two threads to executed with the 
same
    stack area. At moment the two stack pointers get close to each other, it's 
time
    for Guru meditation.
    
    This proposed patch fixes it.
    
    The marker _GL_ASYNC_SAFE is purely for documentation purposes. See
    gnulib-common.m4 for its (important) meaning.
    
    Bruno
    
    >From 03c1cfdfdbee84ddd1c09588c9290742ce61c283 Mon Sep 17 00:00:00 2001
    From: Bruno Haible <bruno@clisp.org>
    Date: Sat, 27 Jun 2020 17:25:10 +0200
    Subject: [PATCH] Fix invalid use of siglongjmp due to threads.
    
    * poke/pk-repl.c: Include <pthread.h>.
    (ctrlc_thread): New variable.
    (poke_sigint_handler): Mark as _GL_ASYNC_SAFE (purely a comment). When 
invoked
    in a different thread, redirect the signal to the ctrlc_thread.
    (pk_repl): Initialize ctrlc_thread.
    ---
     poke/pk-repl.c | 44 ++++++++++++++++++++++++++++++++++++--------
     1 file changed, 36 insertions(+), 8 deletions(-)
    
    diff --git a/poke/pk-repl.c b/poke/pk-repl.c
    index 38a53a5..c6dce90 100644
    --- a/poke/pk-repl.c
    +++ b/poke/pk-repl.c
    @@ -19,6 +19,7 @@
     #include <config.h>
     
     #include <signal.h>
    +#include <pthread.h>
     #include <unistd.h>
     #include <setjmp.h>
     #include <stdlib.h>
    @@ -37,9 +38,12 @@
     #include "pk-utils.h"
     #include "pk-map.h"
     
    +/* The thread that contains the non-local entry point for reentering
    +   the REPL.  */
    +static pthread_t volatile ctrlc_thread;
     /* The non-local entry point for reentering the REPL.  */
     static sigjmp_buf /*volatile*/ ctrlc_buf;
    -/* When nonzero, ctrlc_buf contains a valid value.  */
    +/* When nonzero, ctrlc_thread and ctrlc_buf contain valid values.  */
     static int volatile ctrlc_buf_valid;
     
     /* This function is called repeatedly by the readline library, when
    @@ -204,17 +208,36 @@ banner (void)
     
     }
     
    -static void
    +static _GL_ASYNC_SAFE void
     poke_sigint_handler (int sig)
     {
       if (ctrlc_buf_valid)
         {
           /* Abort the current command, and return to the REPL.  */
    -      rl_free_line_state ();
    -      rl_cleanup_after_signal ();
    -      rl_line_buffer[rl_point = rl_end = rl_mark = 0] = 0;
    -      printf ("\n");
    -      siglongjmp (ctrlc_buf, 1);
    +      if (!pthread_equal (pthread_self (), ctrlc_thread))
    +        {
    +          /* We are not in the thread that established the jmp_buf for
    +             returning to the REPL.  Redirect the signal to that thread,
    +             and decline responsibility for future deliveries of this
    +             signal.  */
    +          sigset_t mask;
    +          if (sigemptyset (&mask) == 0
    +              && sigaddset (&mask, SIGINT) == 0)
    +            pthread_sigmask (SIG_BLOCK, &mask, NULL);
    +
    +          pthread_kill (ctrlc_thread, SIGINT);
    +        }
    +      else
    +        {
    +          /* We are in the thread that established the jmp_buf for 
returning
    +             to the REPL.  Put the readline library into a sane state, then
    +             unwind the stack.  */
    +          rl_free_line_state ();
    +          rl_cleanup_after_signal ();
    +          rl_line_buffer[rl_point = rl_end = rl_mark = 0] = 0;
    +          printf ("\n");
    +          siglongjmp (ctrlc_buf, 1);
    +        }
         }
       else
         {
    @@ -289,7 +312,11 @@ pk_repl (void)
     {
       banner ();
     
    -  /* Arrange for the current line to be cancelled on SIGINT.  */
    +  /* Arrange for the current line to be cancelled on SIGINT.
    +     Since some library code is also interested in SIGINT
    +     (GNU libtextstyle, via gnulib module fatal-signal), it is better
    +     to install it once, rather than to install and uninstall it once
    +     in each round of the REP loop below.  */
       ctrlc_buf_valid = 0;
       struct sigaction sa;
       sa.sa_handler = poke_sigint_handler;
    @@ -319,6 +346,7 @@ pk_repl (void)
       rl_filename_quote_characters = " ";
       rl_filename_quoting_function = escape_metacharacters;
     
    +  ctrlc_thread = pthread_self ();
       sigsetjmp (ctrlc_buf, 1);
       ctrlc_buf_valid = 1;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]