Search code examples
c++linuxopensslsigpipe

C++ & OpenSSL: SIGPIPE when writing in closed pipe


I'm coding a C++ SSL Server for TCP Connections on Linux. When the program uses SSL_write() to write into a closed pipe, a SIGPIPE-Exception gets thrown which causes the program to shut down. I know that this is normal behaviour. But the program should not always die when the peer not closes the connection correctly.

I have already googled a lot and tried pretty much everything I found, but it seems like nothing is working for me. signal(SIGPIPE,SIG_IGN) does not work - the exception still gets thrown (Same for signal(SIGPIPE, SomeKindOfHandler).

The gdb output:

Program received signal SIGPIPE, Broken pipe.
0x00007ffff6b23ccd in write () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) where
#0  0x00007ffff6b23ccd in write () from /lib/x86_64-linux-gnu/libpthread.so.0
#1  0x00007ffff7883835 in ?? () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2  0x00007ffff7881687 in BIO_write () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#3  0x00007ffff7b9d3e0 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#4  0x00007ffff7b9db04 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#5  0x000000000042266a in NetInterface::SendToSubscribers(bool) () at ../Bether/NetInterface.h:181
#6  0x0000000000425834 in main () at ../Bether/main.cpp:111

About the Code:

I'm using a thread which is waiting for new connections and accepting them. The thread then puts the connection information (BIO & SSL) into a static map inside the NetInterface class. Every 5 seconds NetInterface::sendTOSubscribers() is executed from main(). This function accesses the static map and sends data to every connection in there. This function is also where the SIGPIPE comes from. I have used signal(SIGPIPE,SIG_IGN) in main() (obviously before the 5-seconds loop) and in NetInterface::SendToSubscribers(), but it is not working anywhere.

Thanks for your help!


Solution

  • You have to call function sigaction to change this behavior either to ignore SIGPIPE or handle it in a specific way with your own signal handler. Please don't use function signal, it's obsolete.

    http://man7.org/linux/man-pages/man2/sigaction.2.html

    One way to do it (I haven't compiled this code but should be something like this):

    void sigpipe_handler(int signal)
    {
       ...
    }
    
    int main() 
    {
        struct sigaction sh;
        struct sigaction osh;
    
        sh.sa_handler = &sigpipe_handler; //Can set to SIG_IGN
        // Restart interrupted system calls
        sh.sa_flags = SA_RESTART;
    
        // Block every signal during the handler
        sigemptyset(&sh.sa_mask);
    
        if (sigaction(SIGPIPE, &sh, &osh) < 0)
        {
            return -1;
        }
    
        ...
    }
    

    If the program is multithreaded, it is a little different as you have less control on which thread will receive the signal. That depends on the type of signal. For SIGPIPE, it will be sent to the pthread that generated the signal. Nevertheless, sigaction should work OK.

    It is possible to set the mask in the main thread and all subsequently created pthreads will inherit the signal mask. Otherwise, the signal mask can be set in each thread.

    sigset_t blockedSignal; 
    sigemptyset(&blockedSignal);    
    sigaddset(&blockedSignal, SIGPIPE); 
    pthread_sigmask(SIG_BLOCK, &blockedSignal, NULL);
    

    However, if you block the signal, it will be pending for the process and as soon as it is possible it will be delivered. For this case, use sigtimedwait at the end of the thread. sigaction set at the main thread or in the thread that generated SIGPIPE should work as well.