Search code examples
macospopendlopenlibcryptolibev

SIGINT getting generated in a program using libev


Hi I am dealing with a problem on Mac OS 13.1, while using libev, opening a DLL (libcrypto.3.dylib) and then doing popen to run a shell command and read the ouptut to a string.

The scenario in the program is illustrated in the sample as follows.

  1. The main thread in the process creates a child thread and waits for SIGINT to terminate the program.
  2. The child thread starts an event loop using libev, one of the event handlers is a timer, which does dlopen of libcrypto.3.dylib and then calls popen to run the command "uname -o 2>/dev/null".
  3. At this stage signal SIGINT, gets generated, this terminates the wait on the main thread and the whole program gets terminated after that.

If the dlopen and popen are done without libev in a child thread. It works fine. In fact, even if the loop is initialized and not actually run, the signal gets generated and delivered.

Not able to understand the cause of SIGINT. If I try with another library (a very trivial one), it seems to work properly.

The code for the simualation is given below.

ev.cpp The main program

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <dlfcn.h>

extern "C" {
#include "ev.h"
}

pthread_cond_t      cond;
pthread_mutex_t     mutex;

int run_func()
{
    //void * dll = dlopen("/Volumes/NEW_DISK/user/sudheerp/usr/local/lib/libevlcrypto.so", RTLD_GLOBAL | RTLD_NOW);
    //printf("[%s:%d]: dll = [%p]\n", __FILE__, __LINE__, dll);
    //void * sym = dlsym(dll, "open_libevlcrypto");
    //printf("[%s:%d]: sym = [%p]\n", __FILE__, __LINE__, sym);

    //void * dll = dlopen("/Volumes/NEW_DISK/user/sudheerp/temp/io.open/clib.so", RTLD_GLOBAL | RTLD_NOW);
    //printf("[%s:%d]: dll = [%p]\n", __FILE__, __LINE__, dll);
    //void * sym = dlsym(dll, "open_libevlcrypto");
    //printf("[%s:%d]: sym = [%p]\n", __FILE__, __LINE__, sym);
    
    //void * dll = dlopen("/Volumes/NEW_DISK/user/sudheerp/temp/io.open/lib.so", RTLD_GLOBAL | RTLD_NOW);
    //printf("[%s:%d]: dll = [%p]\n", __FILE__, __LINE__, dll);
    //void * sym = dlsym(dll, "open_libevlcrypto");
    //printf("[%s:%d]: sym = [%p]\n", __FILE__, __LINE__, sym);
    

    void * dll = dlopen("/opt/local/libexec/openssl3/lib/libcrypto.3.dylib", RTLD_GLOBAL | RTLD_NOW);
    printf("[%s:%d]: dll = [%p]\n", __FILE__, __LINE__, dll);

    {
        FILE * fp = popen("uname -o 2>/dev/null","r");
        char out_s[256];

        fgets(out_s, 255, fp);

        for(int i = 0; i<strlen(out_s); i++) {
            if (out_s[i] == '\n' || out_s[i] == '\r') {
                out_s[i] = 0;
                break;
            }
        }
        printf("%s\n", out_s);

        pclose(fp);
    }

    printf("[%s:%d] first uname complete\n", __FILE__, __LINE__);


    {
        FILE * fp = popen("uname -o 2>/dev/null","r");
        char out_s[256];

        fgets(out_s, 255, fp);

        for(int i = 0; i<strlen(out_s); i++) {
            if (out_s[i] == '\n' || out_s[i] == '\r') {
                out_s[i] = 0;
                break;
            }
        }
        printf("%s\n", out_s);

        pclose(fp);
    }

    printf("[%s:%d] second uname complete\n", __FILE__, __LINE__);

    return 0;
}

ev_io stdin_watcher;
ev_timer timeout_watcher;

static void stdin_cb (EV_P_ ev_io *w, int revents)
{
    puts ("stdin ready");
    ev_io_stop (EV_A_ w);

    ev_break (EV_A_ EVBREAK_ALL);
}

static void timeout_cb (EV_P_ ev_timer *w, int revents)
{
    puts ("timeout");
    run_func();
}

struct ev_loop *loop = EV_DEFAULT;

void* thread_main (void*p)
{
    ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);
    ev_io_start (loop, &stdin_watcher);

    ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 2);
    ev_timer_start (loop, &timeout_watcher);

    ev_run (loop, 0);

    return (void*)0;
}

void stop_the_loop()
{
    ev_io_stop(loop, &stdin_watcher);
    ev_timer_stop(loop, &timeout_watcher);
}

int main()
{
    pthread_t t;

    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);

    pthread_create(&t, NULL, thread_main, (void*)0);
    sigset_t sset;
    sigemptyset(&sset);
    sigaddset(&sset, SIGINT);
    sigaddset(&sset, SIGQUIT);
    sigaddset(&sset, SIGTERM);
    sigprocmask(SIG_BLOCK, &sset, NULL);

    int sig;
    sigwait(&sset, &sig);
    printf("[%s:%d]:Got signal [%d]\n", __FILE__, __LINE__, sig);
    stop_the_loop();

    pthread_join(t,NULL);

    return 0;
}

Makefile

all: signals ev sev lib.o lib.so clib.so

signals: signals.cpp
    clang++ -Wno-c++17-compat-mangling -I/Volumes/NEW_DISK/user/sudheerp/usr/local/include/evpoco signals.cpp -L. -lev -o signals

ev: ev.o
    clang -o ev ev.o -L. -lev -L/Volumes/NEW_DISK/user/sudheerp/usr/local/lib -llua 

sev: sev.o
    clang -o sev sev.o -L. -lev -L/Volumes/NEW_DISK/user/sudheerp/usr/local/lib -llua 

clib.o: clib.c
    clang -c -arch x86_64 -arch arm64 -Wno-c++17-compat-mangling -I/opt/local/libexec/openssl3/include clib.c -o clib.o

clib.so: clib.o
    clang -O2 -g -arch x86_64 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk -bundle -bind_at_load -o clib.so clib.o /opt/local/libexec/openssl3/lib/libcrypto.dylib

lib.o: lib.c
    clang -c -Wno-c++17-compat-mangling -arch x86_64 -arch arm64 lib.c -o lib.o

lib.so: lib.o
    clang -O2 -g -arch x86_64 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk -bundle -bind_at_load -o lib.so lib.o

run.o: run.c
    clang -c -Wno-c++17-compat-mangling -I../libev-4.33 run.c -o run.o

ev.o: ev.cpp
    clang++ -c -Wno-c++17-compat-mangling -I../libev-4.33 -I/Volumes/NEW_DISK/user/sudheerp/usr/local/include/evpoco ev.cpp -o ev.o

sev.o: sev.cpp
    clang++ -c -Wno-c++17-compat-mangling -I../libev-4.33 -I${HOME}/platform/lua-5.3.5/src -I/Volumes/NEW_DISK/user/sudheerp/usr/local/include/evpoco sev.cpp -o sev.o


clean:
    rm -rf ev.o signals.o ev signals run.o sev.o sev lib.o lib.so clib.o clib.so

lib.c the source code for dummy lib.so

#include <string.h>
#include <stddef.h>
#include <assert.h>
#include <arpa/inet.h>

static int hmac_fdigest(void *L)
{
    return 0;
}


static int generate_hash(void *L, const char * algo)
{
    return 0;
}

static int generate_hash_from_string_sha1(void *L)
{
    return generate_hash(L, "SHA1");
}

static int generate_hash_from_string_sha256(void *L)
{
    return generate_hash(L, "SHA256");
}

static int generate_hash_from_string_sha384(void *L)
{
    return generate_hash(L, "SHA384");
}

static int generate_hash_from_string_sha512(void *L)
{
    return generate_hash(L, "SHA512");
}

typedef struct {
    char * name;
    void * ptr;
} Reg;


static const Reg _lib[] = {
    { NULL, NULL }
};

struct cipher_text_s {
    unsigned char * buffer;
    size_t len;
};

const static char *_cipher_text_name = "cipher_text";
static int cipher_text__gc(void *L)
{
    return 0;
}

static int cipher_text__tostring(void *L)
{
    return 1;
}

const static char *_cipher_key_name = "cipher_key";
static int cipher_key__gc(void *L)
{
    return 0;
}

static int cipher_key__tostring(void *L)
{
    return 1;
}

static int generate_symmetric_key(void *L)
{
    return 1;
}

static int generate_aes_key(void *L)
{
    return 1;
}

const static char *_rsa_key_name = "rsa_key";
static int rsa_key__gc(void *L)
{
    return 0;
}

static int rsa_key__tostring(void *L)
{
    return 1;
}

static int get_rsa_public_key(void *L)
{
    return 1;
}

static int load_rsa_public_key(void *L)
{
    return 1;
}

static int get_rsa_private_key(void *L)
{
    return 1;
}

static int load_rsa_private_key(void *L)
{
    return 1;
}

static int generate_rsa_key_pair(void *L)
{
    return 1;
}

static void * deserialize_symmetric_key(void *L, unsigned char * data)
{
    return NULL;;
}

struct serialized_cipher_key_s {
    unsigned char * buffer;
    size_t buffer_size;
};

static void serialize_symmetric_key(void *L, void * key, struct serialized_cipher_key_s * s_cipher_key_p)
{
    return ;
}

static int rsa_encrypt_symm_key(void *L)
{
    return 3;
}

static int rsa_decrypt_udata_enc_symm_key(void *L)
{
    return 1;
}

static int rsa_decrypt_enc_symm_key(void *L)
{
    return 1;
}

static int rsa_decrypt_b64_enc_symm_key(void *L)
{
    return 1;
}

static int encrypt_text(void *L)
{
    return 3;
}

static int decrypt_cipher_text(void *L)
{
    return 1;
}

static int decrypt_b64_cipher_text(void *L)
{
    return 1;
}

static int decrypt_udata_cipher_text(void *L)
{
    return 1;
}

int open_libevlcrypto(void *L)
{
    static const Reg crypto_methods[] = {
         {"s_sha1_hash", generate_hash_from_string_sha1}
        ,{"s_sha256_hash", generate_hash_from_string_sha256}
        ,{"s_sha384_hash", generate_hash_from_string_sha384}
        ,{"s_sha512_hash", generate_hash_from_string_sha512}
        ,{"hmac_digest", hmac_fdigest}

        ,{"generate_symmetric_key", generate_symmetric_key}
        ,{"generate_aes_key", generate_aes_key}
        ,{"generate_rsa_key_pair", generate_rsa_key_pair}

        ,{"get_rsa_public_key", get_rsa_public_key}
        ,{"get_rsa_private_key", get_rsa_private_key}

        ,{"load_rsa_public_key", load_rsa_public_key}
        ,{"load_rsa_private_key", load_rsa_private_key}


        ,{"rsa_encrypt_symm_key", rsa_encrypt_symm_key}

        ,{"rsa_decrypt_enc_symm_key", rsa_decrypt_enc_symm_key}
        ,{"rsa_decrypt_udata_enc_symm_key", rsa_decrypt_udata_enc_symm_key}
        ,{"rsa_decrypt_b64_enc_symm_key", rsa_decrypt_b64_enc_symm_key}


        ,{"encrypt_text", encrypt_text}

        ,{"decrypt_cipher_text", decrypt_cipher_text}
        ,{"decrypt_udata_cipher_text", decrypt_udata_cipher_text}
        ,{"decrypt_b64_cipher_text", decrypt_b64_cipher_text}
        ,{NULL, NULL}
    };

    return 1;    
}


#include <string.h>
#include <stddef.h>
#include <assert.h>
#include <arpa/inet.h>

static int hmac_fdigest(void *L)
{
    return 0;
}


static int generate_hash(void *L, const char * algo)
{
    return 0;
}

static int generate_hash_from_string_sha1(void *L)
{
    return generate_hash(L, "SHA1");
}

static int generate_hash_from_string_sha256(void *L)
{
    return generate_hash(L, "SHA256");
}

static int generate_hash_from_string_sha384(void *L)
{
    return generate_hash(L, "SHA384");
}

static int generate_hash_from_string_sha512(void *L)
{
    return generate_hash(L, "SHA512");
}

typedef struct {
    char * name;
    void * ptr;
} Reg;


static const Reg _lib[] = {
    { NULL, NULL }
};

struct cipher_text_s {
    unsigned char * buffer;
    size_t len;
};

const static char *_cipher_text_name = "cipher_text";
static int cipher_text__gc(void *L)
{
    return 0;
}

static int cipher_text__tostring(void *L)
{
    return 1;
}

const static char *_cipher_key_name = "cipher_key";
static int cipher_key__gc(void *L)
{
    return 0;
}

static int cipher_key__tostring(void *L)
{
    return 1;
}

static int generate_symmetric_key(void *L)
{
    return 1;
}

static int generate_aes_key(void *L)
{
    return 1;
}

const static char *_rsa_key_name = "rsa_key";
static int rsa_key__gc(void *L)
{
    return 0;
}

static int rsa_key__tostring(void *L)
{
    return 1;
}

static int get_rsa_public_key(void *L)
{
    return 1;
}

static int load_rsa_public_key(void *L)
{
    return 1;
}

static int get_rsa_private_key(void *L)
{
    return 1;
}

static int load_rsa_private_key(void *L)
{
    return 1;
}

static int generate_rsa_key_pair(void *L)
{
    return 1;
}

static void * deserialize_symmetric_key(void *L, unsigned char * data)
{
    return NULL;;
}

struct serialized_cipher_key_s {
    unsigned char * buffer;
    size_t buffer_size;
};

static void serialize_symmetric_key(void *L, void * key, struct serialized_cipher_key_s * s_cipher_key_p)
{
    return ;
}

static int rsa_encrypt_symm_key(void *L)
{
    return 3;
}

static int rsa_decrypt_udata_enc_symm_key(void *L)
{
    return 1;
}

static int rsa_decrypt_enc_symm_key(void *L)
{
    return 1;
}

static int rsa_decrypt_b64_enc_symm_key(void *L)
{
    return 1;
}

static int encrypt_text(void *L)
{
    return 3;
}

static int decrypt_cipher_text(void *L)
{
    return 1;
}

static int decrypt_b64_cipher_text(void *L)
{
    return 1;
}

static int decrypt_udata_cipher_text(void *L)
{
    return 1;
}

int open_libevlcrypto(void *L)
{
    static const Reg crypto_methods[] = {
         {"s_sha1_hash", generate_hash_from_string_sha1}
        ,{"s_sha256_hash", generate_hash_from_string_sha256}
        ,{"s_sha384_hash", generate_hash_from_string_sha384}
        ,{"s_sha512_hash", generate_hash_from_string_sha512}
        ,{"hmac_digest", hmac_fdigest}

        ,{"generate_symmetric_key", generate_symmetric_key}
        ,{"generate_aes_key", generate_aes_key}
        ,{"generate_rsa_key_pair", generate_rsa_key_pair}

        ,{"get_rsa_public_key", get_rsa_public_key}
        ,{"get_rsa_private_key", get_rsa_private_key}

        ,{"load_rsa_public_key", load_rsa_public_key}
        ,{"load_rsa_private_key", load_rsa_private_key}


        ,{"rsa_encrypt_symm_key", rsa_encrypt_symm_key}

        ,{"rsa_decrypt_enc_symm_key", rsa_decrypt_enc_symm_key}
        ,{"rsa_decrypt_udata_enc_symm_key", rsa_decrypt_udata_enc_symm_key}
        ,{"rsa_decrypt_b64_enc_symm_key", rsa_decrypt_b64_enc_symm_key}


        ,{"encrypt_text", encrypt_text}

        ,{"decrypt_cipher_text", decrypt_cipher_text}
        ,{"decrypt_udata_cipher_text", decrypt_udata_cipher_text}
        ,{"decrypt_b64_cipher_text", decrypt_b64_cipher_text}
        ,{NULL, NULL}
    };

    return 1;    
}

Solution

  • This is happening because the default loop of libev installs a handle for SIGCHLD.

    struct ev_loop *loop = EV_DEFAULT;
    

    With this whenever a child process terminates the handler gets triggered. (This happens each time io.popen completes)

    Further, the signal can get delivered in any of the threads of the multi-threaded program "ev"

    Sometimes the signal gets delivered in the main thread, which is waiting for SIGINT to arrive in order to terminate the whole program. When this happens the waiting process is interrupted (or SIGINT is delivered) and thus the program is terminated, even though there is no other source of interruption.