wolkykim/libasyncd

segfault on ad_server_free()

tarkin000 opened this issue · 4 comments

Hello,
to make a simple testcase, modify helloworld_http_server.c:

int my_http_get_handler(short event, ad_conn_t *conn, void *userdata) {
    struct ad_http_s *http;
    if (ad_http_get_status(conn) == AD_HTTP_REQ_DONE) {
        // get the http struct pointer
        http = ad_conn_get_extra(conn);
        // check if the request matches our shutdown command
        if (strcmp("/SHUTDOWN",http->request.path) == 0) {
          ad_http_set_response_header(conn,"Content-Length","13");
          ad_http_response(conn, 200, "text/plain", "Shutting down", 13);
          ad_server_stop(conn->server);
        } else {
          ad_http_response(conn, 200, "text/html", "Hello World", 11);
        }
        return ad_http_is_keepalive_request(conn) ? AD_DONE : AD_CLOSE;
    }
    return AD_OK;
}

and in main:

int main(int argc, char **argv) {
    int rc;
    ad_log_level(AD_LOG_DEBUG);
    ad_server_t *server = ad_server_new();
    ad_server_set_option(server, "server.port", "8888");
    // set a timeout, so the client actually renders the content
    ad_server_set_option(server, "server.timeout", "2");
    ad_server_register_hook(server, ad_http_handler, NULL); // HTTP Parser is also a hook.
    ad_server_register_hook_on_method(server, "GET", my_http_get_handler, NULL);
    ad_server_register_hook(server, my_http_default_handler, NULL);
    rc = ad_server_start(server);
    ad_server_free(server);
    return rc;
}

Start the server. Issue a test request for '/'. Get 'Hello World'.
Issue a request for '/SHUTDOWN'. Get 'Shutting down'.
Here's the last few lines of the log output:

[DEBUG] Connection closed. [conn_cb(),ad_server.c:751]
[DEBUG] Existing loop. [notify_cb(),ad_server.c:503]
[DEBUG] Loop finished [server_loop(),ad_server.c:512]
[DEBUG] Closing server. [close_server(),ad_server.c:519]
[INFO] Server closed.
[DEBUG] Server terminated. [ad_server_free(),ad_server.c:330]
Segmentation fault

uname -a: Linux sgmtech 3.2.0-4-686-pae #1 SMP Debian 3.2.65-1+deb7u2 i686 GNU/Linux
distro : Debian 7, stock kernel.
Libevent version: 2.0.19-stable

Here's a backtrace from gdb, using libevent 2.0.21-stable that I built from source:

Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0  0x00000000 in ?? ()
#1  0xb7f7e35f in event_del (ev=0x805b780) at event.c:2186
#2  0xb7f81dde in event_base_free (base=0x805b610) at event.c:760
#3  0x0804b0e7 in ad_server_free (server=server@entry=0x805a008) at ad_server.c:305
#4  0x0804a78e in main (argc=1, argv=0xbffff2f4) at helloworld_http_server.c:67

I built from source to rule out any Debian patches.
I will test one more time with latest libevent stable, (2.0.22), and report back.
I can't tell if it's a libasyncd or a libevent issue.

Ok, on libevent 2.0.22-stable, the error occurs on line 2204:

EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);

...which generates the following code (after deciphering macros):

if (ev->evbase->th_base_lock)
  _evthread_lock_fns.lock(0, ev->evbase->th_base_lock)

I think this is due to locking not having been intialized....
...sure enough, in libasyncd/src/ad_server.c, line 133,
evthread_use_threads() is commented out.
Re-enabling this call doesn't entirely solve the problem; it just moves it:

Program received signal SIGSEGV, Segmentation fault.
event_base_free (base=0x805b610) at event.c:759
759                             if (!(ev->ev_flags & EVLIST_INTERNAL)) {

This last error is an error with libevent, and not libasyncd.
I would suggest re-enabling the call to evthread_use_threads in ad_server.c

Nevermind, I hadn't realized that free_on_stop was set by default.
All errors I observed were the crazy result of trying to free the server twice!
Sorry.

Sorry for the late response, I was out of the internet on the weekend. I'm glad toI see you've resolved the issue.