21ab4e
From 893f1428fb7153ea504ba7e85b729be05cb185b8 Mon Sep 17 00:00:00 2001
21ab4e
From: Milind Changire <mchangir@redhat.com>
21ab4e
Date: Tue, 9 May 2017 17:51:18 +0530
21ab4e
Subject: [PATCH 424/426] rpc: fix transport add/remove race on port probing
21ab4e
21ab4e
Problem:
21ab4e
Spurious __gf_free() assertion failures seen all over the place with
21ab4e
header->magic being overwritten when running port probing tests with
21ab4e
'nmap'
21ab4e
21ab4e
Solution:
21ab4e
Fix sequence of:
21ab4e
1. add accept()ed socket connection fd to epoll set
21ab4e
2. add newly created rpc_transport_t object in RPCSVC service list
21ab4e
21ab4e
Correct sequence is #2 followed by #1.
21ab4e
21ab4e
Reason:
21ab4e
Adding new fd returned by accept() to epoll set causes an epoll_wait()
21ab4e
to return immediately with a POLLIN event. This races ahead to a readv()
21ab4e
which returms with errno:104 (Connection reset by peer) during port
21ab4e
probing using 'nmap'. The error is then handled by POLLERR code to
21ab4e
remove the new transport object from RPCSVC service list and later
21ab4e
unref and destroy the rpc transport object.
21ab4e
socket_server_event_handler() then catches up with registering the
21ab4e
unref'd/destroyed rpc transport object. This is later manifest as
21ab4e
assertion failures in __gf_free() with the header->magic field botched
21ab4e
due to invalid address references.
21ab4e
All this does not result in a Segmentation Fault since the address
21ab4e
space continues to be mapped into the process and pages still being
21ab4e
referenced elsewhere.
21ab4e
21ab4e
As a further note:
21ab4e
This race happens only in accept() codepath. Only in this codepath,
21ab4e
the notify will be referring to two transports:
21ab4e
1, listener transport and
21ab4e
2. newly accepted transport
21ab4e
All other notify refer to only one transport i.e., the transport/socket
21ab4e
on which the event is received. Since epoll is ONE_SHOT another event
21ab4e
won't arrive on the same socket till the current event is processed.
21ab4e
However, in the accept() codepath, the current event - ACCEPT - and the
21ab4e
new event - POLLIN/POLLER - arrive on two different sockets:
21ab4e
1. ACCEPT on listener socket and
21ab4e
2. POLLIN/POLLERR on newly registered socket.
21ab4e
Also, note that these two events are handled different thread contexts.
21ab4e
21ab4e
Cleanup:
21ab4e
Critical section in socket_server_event_handler() has been removed.
21ab4e
Instead, an additional ref on new_trans has been used to avoid ref/unref
21ab4e
race when notifying RPCSVC.
21ab4e
21ab4e
mainline:
21ab4e
> BUG: 1438966
21ab4e
> Signed-off-by: Milind Changire <mchangir@redhat.com>
21ab4e
> Reviewed-on: https://review.gluster.org/17139
21ab4e
> Smoke: Gluster Build System <jenkins@build.gluster.org>
21ab4e
> NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org>
21ab4e
> CentOS-regression: Gluster Build System <jenkins@build.gluster.org>
21ab4e
> Reviewed-by: Amar Tumballi <amarts@redhat.com>
21ab4e
> Reviewed-by: Oleksandr Natalenko <oleksandr@natalenko.name>
21ab4e
> Reviewed-by: Jeff Darcy <jeff@pl.atyp.us>
21ab4e
(cherry picked from commit 4f7ef3020edcc75cdeb22d8da8a1484f9db77ac9)
21ab4e
21ab4e
Change-Id: I4417924bc9e6277d24bd1a1c5bcb7445bcb226a3
21ab4e
BUG: 1442535
21ab4e
Signed-off-by: Milind Changire <mchangir@redhat.com>
21ab4e
Reviewed-on: https://code.engineering.redhat.com/gerrit/105653
21ab4e
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
21ab4e
---
21ab4e
 rpc/rpc-transport/socket/src/socket.c | 359 ++++++++++++++++++----------------
21ab4e
 1 file changed, 195 insertions(+), 164 deletions(-)
21ab4e
21ab4e
diff --git a/rpc/rpc-transport/socket/src/socket.c b/rpc/rpc-transport/socket/src/socket.c
21ab4e
index ca939bb..f64e2f0 100644
21ab4e
--- a/rpc/rpc-transport/socket/src/socket.c
21ab4e
+++ b/rpc/rpc-transport/socket/src/socket.c
21ab4e
@@ -2671,107 +2671,106 @@ socket_server_event_handler (int fd, int idx, void *data,
21ab4e
         priv = this->private;
21ab4e
         ctx  = this->ctx;
21ab4e
 
21ab4e
-        pthread_mutex_lock (&priv->lock);
21ab4e
-        {
21ab4e
-                priv->idx = idx;
21ab4e
-
21ab4e
-                if (poll_in) {
21ab4e
-                        new_sock = accept (priv->sock, SA (&new_sockaddr),
21ab4e
-                                           &addrlen);
21ab4e
-
21ab4e
-                        if (new_sock == -1) {
21ab4e
-                                gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                        "accept on %d failed (%s)",
21ab4e
-                                        priv->sock, strerror (errno));
21ab4e
-                                goto unlock;
21ab4e
-                        }
21ab4e
-
21ab4e
-                        if (priv->nodelay && (new_sockaddr.ss_family != AF_UNIX)) {
21ab4e
-                                ret = __socket_nodelay (new_sock);
21ab4e
-                                if (ret == -1) {
21ab4e
-                                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                                "setsockopt() failed for "
21ab4e
-                                                "NODELAY (%s)",
21ab4e
-                                                strerror (errno));
21ab4e
-                                }
21ab4e
-                        }
21ab4e
+        /* NOTE:
21ab4e
+         * We have done away with the critical section in this function. since
21ab4e
+         * there's little that it helps with. There's no other code that
21ab4e
+         * attempts to unref the listener socket/transport from any other
21ab4e
+         * thread context while we are using it here.
21ab4e
+         */
21ab4e
+        priv->idx = idx;
21ab4e
 
21ab4e
-                        if (priv->keepalive &&
21ab4e
-                            new_sockaddr.ss_family != AF_UNIX) {
21ab4e
-                                ret = __socket_keepalive (new_sock,
21ab4e
-                                                          new_sockaddr.ss_family,
21ab4e
-                                                          priv->keepaliveintvl,
21ab4e
-                                                          priv->keepaliveidle,
21ab4e
-                                                          priv->timeout);
21ab4e
-                                if (ret == -1)
21ab4e
-                                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                                "Failed to set keep-alive: %s",
21ab4e
-                                                strerror (errno));
21ab4e
-                        }
21ab4e
+        if (poll_in) {
21ab4e
+                new_sock = accept (priv->sock, SA (&new_sockaddr), &addrlen);
21ab4e
 
21ab4e
-                        new_trans = GF_CALLOC (1, sizeof (*new_trans),
21ab4e
-                                               gf_common_mt_rpc_trans_t);
21ab4e
-                        if (!new_trans) {
21ab4e
-                                sys_close (new_sock);
21ab4e
-                                goto unlock;
21ab4e
-                        }
21ab4e
+                if (new_sock == -1) {
21ab4e
+                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
+                                "accept on %d failed (%s)",
21ab4e
+                                priv->sock, strerror (errno));
21ab4e
+                        goto out;
21ab4e
+                }
21ab4e
 
21ab4e
-                        ret = pthread_mutex_init(&new_trans->lock, NULL);
21ab4e
+                if (priv->nodelay && (new_sockaddr.ss_family != AF_UNIX)) {
21ab4e
+                        ret = __socket_nodelay (new_sock);
21ab4e
                         if (ret == -1) {
21ab4e
                                 gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                        "pthread_mutex_init() failed: %s",
21ab4e
+                                        "setsockopt() failed for "
21ab4e
+                                        "NODELAY (%s)",
21ab4e
                                         strerror (errno));
21ab4e
-                                sys_close (new_sock);
21ab4e
-                                GF_FREE (new_trans);
21ab4e
-                                goto unlock;
21ab4e
                         }
21ab4e
-                        INIT_LIST_HEAD (&new_trans->list);
21ab4e
+                }
21ab4e
 
21ab4e
-                        new_trans->name = gf_strdup (this->name);
21ab4e
+                if (priv->keepalive &&
21ab4e
+                                new_sockaddr.ss_family != AF_UNIX) {
21ab4e
+                        ret = __socket_keepalive (new_sock,
21ab4e
+                                                  new_sockaddr.ss_family,
21ab4e
+                                                  priv->keepaliveintvl,
21ab4e
+                                                  priv->keepaliveidle,
21ab4e
+                                                  priv->timeout);
21ab4e
+                        if (ret == -1)
21ab4e
+                                gf_log (this->name, GF_LOG_WARNING,
21ab4e
+                                        "Failed to set keep-alive: %s",
21ab4e
+                                        strerror (errno));
21ab4e
+                }
21ab4e
 
21ab4e
-                        memcpy (&new_trans->peerinfo.sockaddr, &new_sockaddr,
21ab4e
-                                addrlen);
21ab4e
-                        new_trans->peerinfo.sockaddr_len = addrlen;
21ab4e
+                new_trans = GF_CALLOC (1, sizeof (*new_trans),
21ab4e
+                                gf_common_mt_rpc_trans_t);
21ab4e
+                if (!new_trans) {
21ab4e
+                        sys_close (new_sock);
21ab4e
+                        goto out;
21ab4e
+                }
21ab4e
 
21ab4e
-                        new_trans->myinfo.sockaddr_len =
21ab4e
-                                sizeof (new_trans->myinfo.sockaddr);
21ab4e
+                ret = pthread_mutex_init(&new_trans->lock, NULL);
21ab4e
+                if (ret == -1) {
21ab4e
+                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
+                                "pthread_mutex_init() failed: %s",
21ab4e
+                                strerror (errno));
21ab4e
+                        sys_close (new_sock);
21ab4e
+                        GF_FREE (new_trans);
21ab4e
+                        goto out;
21ab4e
+                }
21ab4e
+                INIT_LIST_HEAD (&new_trans->list);
21ab4e
 
21ab4e
-                        ret = getsockname (new_sock,
21ab4e
-                                           SA (&new_trans->myinfo.sockaddr),
21ab4e
-                                           &new_trans->myinfo.sockaddr_len);
21ab4e
-                        if (ret == -1) {
21ab4e
-                                gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                        "getsockname on %d failed (%s)",
21ab4e
-                                        new_sock, strerror (errno));
21ab4e
-                                sys_close (new_sock);
21ab4e
-                                GF_FREE (new_trans->name);
21ab4e
-                                GF_FREE (new_trans);
21ab4e
-                                goto unlock;
21ab4e
-                        }
21ab4e
+                new_trans->name = gf_strdup (this->name);
21ab4e
 
21ab4e
-                        get_transport_identifiers (new_trans);
21ab4e
-                        ret = socket_init(new_trans);
21ab4e
-                        if (ret != 0) {
21ab4e
-                                sys_close (new_sock);
21ab4e
-                                GF_FREE (new_trans->name);
21ab4e
-                                GF_FREE (new_trans);
21ab4e
-                                goto unlock;
21ab4e
-                        }
21ab4e
-                        new_trans->ops = this->ops;
21ab4e
-                        new_trans->init = this->init;
21ab4e
-                        new_trans->fini = this->fini;
21ab4e
-                        new_trans->ctx  = ctx;
21ab4e
-                        new_trans->xl   = this->xl;
21ab4e
-                        new_trans->mydata = this->mydata;
21ab4e
-                        new_trans->notify = this->notify;
21ab4e
-                        new_trans->listener = this;
21ab4e
-                        new_priv = new_trans->private;
21ab4e
-
21ab4e
-                        if (new_sockaddr.ss_family == AF_UNIX) {
21ab4e
-                                new_priv->use_ssl = _gf_false;
21ab4e
-                        }
21ab4e
-                        else {
21ab4e
-                                switch (priv->srvr_ssl) {
21ab4e
+                memcpy (&new_trans->peerinfo.sockaddr, &new_sockaddr, addrlen);
21ab4e
+                new_trans->peerinfo.sockaddr_len = addrlen;
21ab4e
+
21ab4e
+                new_trans->myinfo.sockaddr_len = sizeof (new_trans->myinfo.sockaddr);
21ab4e
+
21ab4e
+                ret = getsockname (new_sock, SA (&new_trans->myinfo.sockaddr),
21ab4e
+                                   &new_trans->myinfo.sockaddr_len);
21ab4e
+                if (ret == -1) {
21ab4e
+                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
+                                "getsockname on %d failed (%s)",
21ab4e
+                                new_sock, strerror (errno));
21ab4e
+                        sys_close (new_sock);
21ab4e
+                        GF_FREE (new_trans->name);
21ab4e
+                        GF_FREE (new_trans);
21ab4e
+                        goto out;
21ab4e
+                }
21ab4e
+
21ab4e
+                get_transport_identifiers (new_trans);
21ab4e
+                ret = socket_init(new_trans);
21ab4e
+                if (ret != 0) {
21ab4e
+                        sys_close (new_sock);
21ab4e
+                        GF_FREE (new_trans->name);
21ab4e
+                        GF_FREE (new_trans);
21ab4e
+                        goto out;
21ab4e
+                }
21ab4e
+                new_trans->ops = this->ops;
21ab4e
+                new_trans->init = this->init;
21ab4e
+                new_trans->fini = this->fini;
21ab4e
+                new_trans->ctx  = ctx;
21ab4e
+                new_trans->xl   = this->xl;
21ab4e
+                new_trans->mydata = this->mydata;
21ab4e
+                new_trans->notify = this->notify;
21ab4e
+                new_trans->listener = this;
21ab4e
+                new_priv = new_trans->private;
21ab4e
+
21ab4e
+                if (new_sockaddr.ss_family == AF_UNIX) {
21ab4e
+                        new_priv->use_ssl = _gf_false;
21ab4e
+                } else {
21ab4e
+                        switch (priv->srvr_ssl) {
21ab4e
                                 case MGMT_SSL_ALWAYS:
21ab4e
                                         /* Glusterd with secure_mgmt. */
21ab4e
                                         new_priv->use_ssl = _gf_true;
21ab4e
@@ -2782,95 +2781,127 @@ socket_server_event_handler (int fd, int idx, void *data,
21ab4e
                                         break;
21ab4e
                                 default:
21ab4e
                                         new_priv->use_ssl = _gf_false;
21ab4e
-                                }
21ab4e
                         }
21ab4e
+                }
21ab4e
 
21ab4e
-			new_priv->sock = new_sock;
21ab4e
-			new_priv->own_thread = priv->own_thread;
21ab4e
-
21ab4e
-                        new_priv->ssl_ctx = priv->ssl_ctx;
21ab4e
-			if (new_priv->use_ssl && !new_priv->own_thread) {
21ab4e
-				cname = ssl_setup_connection(new_trans,1);
21ab4e
-                                if (!cname) {
21ab4e
-					gf_log(this->name,GF_LOG_ERROR,
21ab4e
-					       "server setup failed");
21ab4e
-					sys_close (new_sock);
21ab4e
-                                        GF_FREE (new_trans->name);
21ab4e
-                                        GF_FREE (new_trans);
21ab4e
-					goto unlock;
21ab4e
-				}
21ab4e
-                                this->ssl_name = cname;
21ab4e
-			}
21ab4e
+                new_priv->sock = new_sock;
21ab4e
+                new_priv->own_thread = priv->own_thread;
21ab4e
 
21ab4e
-                        if (!priv->bio && !priv->own_thread) {
21ab4e
-                                ret = __socket_nonblock (new_sock);
21ab4e
+                new_priv->ssl_ctx = priv->ssl_ctx;
21ab4e
+                if (new_priv->use_ssl && !new_priv->own_thread) {
21ab4e
+                        cname = ssl_setup_connection(new_trans, 1);
21ab4e
+                        if (!cname) {
21ab4e
+                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
+                                       "server setup failed");
21ab4e
+                                sys_close (new_sock);
21ab4e
+                                GF_FREE (new_trans->name);
21ab4e
+                                GF_FREE (new_trans);
21ab4e
+                                goto out;
21ab4e
+                        }
21ab4e
+                        this->ssl_name = cname;
21ab4e
+                }
21ab4e
 
21ab4e
-                                if (ret == -1) {
21ab4e
-                                        gf_log (this->name, GF_LOG_WARNING,
21ab4e
-                                                "NBIO on %d failed (%s)",
21ab4e
-                                                new_sock, strerror (errno));
21ab4e
+                if (!priv->bio && !priv->own_thread) {
21ab4e
+                        ret = __socket_nonblock (new_sock);
21ab4e
 
21ab4e
-                                        sys_close (new_sock);
21ab4e
-                                        GF_FREE (new_trans->name);
21ab4e
-                                        GF_FREE (new_trans);
21ab4e
-                                        goto unlock;
21ab4e
-                                }
21ab4e
+                        if (ret == -1) {
21ab4e
+                                gf_log (this->name, GF_LOG_WARNING,
21ab4e
+                                        "NBIO on %d failed (%s)",
21ab4e
+                                        new_sock, strerror (errno));
21ab4e
+
21ab4e
+                                sys_close (new_sock);
21ab4e
+                                GF_FREE (new_trans->name);
21ab4e
+                                GF_FREE (new_trans);
21ab4e
+                                goto out;
21ab4e
                         }
21ab4e
+                }
21ab4e
 
21ab4e
-                        pthread_mutex_lock (&new_priv->lock);
21ab4e
-                        {
21ab4e
-                                /*
21ab4e
-                                 * In the own_thread case, this is used to
21ab4e
-                                 * indicate that we're initializing a server
21ab4e
-                                 * connection.
21ab4e
-                                 */
21ab4e
-                                new_priv->connected = 1;
21ab4e
-                                new_priv->is_server = _gf_true;
21ab4e
-                                rpc_transport_ref (new_trans);
21ab4e
-
21ab4e
-                                if (new_priv->own_thread) {
21ab4e
-                                        if (pipe(new_priv->pipe) < 0) {
21ab4e
-                                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
-                                                       "could not create pipe");
21ab4e
-                                        }
21ab4e
-                                        ret = socket_spawn(new_trans);
21ab4e
-                                        if (ret) {
21ab4e
-                                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
-                                                       "could not spawn thread");
21ab4e
-                                                sys_close (new_priv->pipe[0]);
21ab4e
-                                                sys_close (new_priv->pipe[1]);
21ab4e
-                                        }
21ab4e
-                                }  else {
21ab4e
-                                        new_priv->idx =
21ab4e
-                                                event_register (ctx->event_pool,
21ab4e
-                                                                new_sock,
21ab4e
-                                                           socket_event_handler,
21ab4e
-                                                                new_trans,
21ab4e
-                                                                1, 0);
21ab4e
-                                        if (new_priv->idx == -1) {
21ab4e
-                                                ret = -1;
21ab4e
-                                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
-                                                       "failed to register the socket with event");
21ab4e
-                                        }
21ab4e
-                                }
21ab4e
+                /*
21ab4e
+                 * In the own_thread case, this is used to
21ab4e
+                 * indicate that we're initializing a server
21ab4e
+                 * connection.
21ab4e
+                 */
21ab4e
+                new_priv->connected = 1;
21ab4e
+                new_priv->is_server = _gf_true;
21ab4e
+                rpc_transport_ref (new_trans);
21ab4e
 
21ab4e
+                if (new_priv->own_thread) {
21ab4e
+                        if (pipe(new_priv->pipe) < 0) {
21ab4e
+                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
+                                       "could not create pipe");
21ab4e
                         }
21ab4e
-                        pthread_mutex_unlock (&new_priv->lock);
21ab4e
-                        if (ret == -1) {
21ab4e
-                                sys_close (new_sock);
21ab4e
-                                rpc_transport_unref (new_trans);
21ab4e
-                                goto unlock;
21ab4e
+                        ret = socket_spawn(new_trans);
21ab4e
+                        if (ret) {
21ab4e
+                                gf_log(this->name, GF_LOG_ERROR,
21ab4e
+                                       "could not spawn thread");
21ab4e
+                                sys_close (new_priv->pipe[0]);
21ab4e
+                                sys_close (new_priv->pipe[1]);
21ab4e
                         }
21ab4e
+                }  else {
21ab4e
+                        /* Take a ref on the new_trans to avoid
21ab4e
+                         * getting deleted when event_register()
21ab4e
+                         * causes socket_event_handler() to race
21ab4e
+                         * ahead of this path to eventually find
21ab4e
+                         * a disconnect and unref the transport
21ab4e
+                         */
21ab4e
+                        rpc_transport_ref (new_trans);
21ab4e
 
21ab4e
-                        if (!priv->own_thread) {
21ab4e
-                                ret = rpc_transport_notify (this,
21ab4e
-                                        RPC_TRANSPORT_ACCEPT, new_trans);
21ab4e
+                        /* Send a notification to RPCSVC layer
21ab4e
+                         * to save the new_trans in its service
21ab4e
+                         * list before we register the new_sock
21ab4e
+                         * with epoll to begin receiving notifications
21ab4e
+                         * for data handling.
21ab4e
+                         */
21ab4e
+                        ret = rpc_transport_notify (this, RPC_TRANSPORT_ACCEPT, new_trans);
21ab4e
+
21ab4e
+                        if (ret != -1) {
21ab4e
+                                new_priv->idx =
21ab4e
+                                        event_register (ctx->event_pool,
21ab4e
+                                                        new_sock,
21ab4e
+                                                        socket_event_handler,
21ab4e
+                                                        new_trans,
21ab4e
+                                                        1, 0);
21ab4e
+                                if (new_priv->idx == -1) {
21ab4e
+                                        ret = -1;
21ab4e
+                                        gf_log(this->name, GF_LOG_ERROR,
21ab4e
+                                               "failed to register the socket "
21ab4e
+                                               "with event");
21ab4e
+
21ab4e
+                                        /* event_register() could have failed for some
21ab4e
+                                         * reason, implying that the new_sock cannot be
21ab4e
+                                         * added to the epoll set. If we wont get any
21ab4e
+                                         * more notifications for new_sock from epoll,
21ab4e
+                                         * then we better remove the corresponding
21ab4e
+                                         * new_trans object from the RPCSVC service list.
21ab4e
+                                         * Since we've notified RPC service of new_trans
21ab4e
+                                         * before we attempted event_register(), we better
21ab4e
+                                         * unlink the new_trans from the RPCSVC service list
21ab4e
+                                         * to cleanup the stateby sending out a DISCONNECT
21ab4e
+                                         * notification.
21ab4e
+                                         */
21ab4e
+                                        rpc_transport_notify (this, RPC_TRANSPORT_DISCONNECT, new_trans);
21ab4e
+                                }
21ab4e
                         }
21ab4e
+
21ab4e
+                        /* this rpc_transport_unref() is for managing race between
21ab4e
+                         * 1. socket_server_event_handler and
21ab4e
+                         * 2. socket_event_handler
21ab4e
+                         * trying to add and remove new_trans from the rpcsvc
21ab4e
+                         * service list
21ab4e
+                         * now that we are done with the notifications, lets
21ab4e
+                         * reduce the reference
21ab4e
+                         */
21ab4e
+                        rpc_transport_unref (new_trans);
21ab4e
                 }
21ab4e
-        }
21ab4e
-unlock:
21ab4e
-        pthread_mutex_unlock (&priv->lock);
21ab4e
 
21ab4e
+                if (ret == -1) {
21ab4e
+                        sys_close (new_sock);
21ab4e
+                        /* this unref is to actually cause the destruction of
21ab4e
+                         * the new_trans since we've failed at everything so far
21ab4e
+                         */
21ab4e
+                        rpc_transport_unref (new_trans);
21ab4e
+                }
21ab4e
+        }
21ab4e
 out:
21ab4e
         if (cname && (cname != this->ssl_name)) {
21ab4e
                 GF_FREE(cname);
21ab4e
-- 
21ab4e
1.8.3.1
21ab4e