Hi again Nenad,

On Tue, Jul 17, 2018 at 05:18:45AM +0200, Willy Tarreau wrote:
> Hi Nenad,
> 
> On Tue, Jul 17, 2018 at 03:37:37AM +0200, Nenad Merdanovic wrote:
> > Ugh, this was a long time ago. [FROM MEMORY] The element should not be
> > duplicated as far as I can remember. The references are stored in an ebtree
> > in order to prevent duplication and to provide consistent view when updated
> > dynamically.
> 
> OK. Then maybe the elements are freed after scanning the ebtree as well,
> and we're meeting the node again after it was freed. I'll run a test
> with the memory debugging options to see if it crashes on first dereference.

OK I found it, the same keys_ref is indeed assigned to multiple bind_confs :

static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy 
*px, struct bind_conf *conf, char **err)
{
...
        keys_ref = tlskeys_ref_lookup(args[cur_arg + 1]);
        if(keys_ref) {
                conf->keys_ref = keys_ref;
                return 0;
        }

So we clearly end up with a normal double-free. I'm checking what's the best
solution to fix this now, as we don't have flags nor any such thing in the
bind_conf to indicate that it must or must not be freed. We can't duplicate
the allocation either because it's used for the updates. I think the cleanest
solution will be to add a refcount to the tls_keys_ref entry.

Then I'm proposing the attached patch which works for me and is obvious
enough to make valgrind happy as well.

Could you guys please give it a try to confirm ? I'll then merge it.

Thanks,
Willy
>From 50588a4e3ccc92cdb0c8a347193192030f0ea9f0 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Tue, 17 Jul 2018 10:05:32 +0200
Subject: BUG/MINOR: ssl: properly ref-count the tls_keys entries

Commit 200b0fa ("MEDIUM: Add support for updating TLS ticket keys via
socket") introduced support for updating TLS ticket keys from the CLI,
but missed a small corner case : if multiple bind lines reference the
same tls_keys file, the same reference is used (as expected), but during
the clean shutdown, it will lead to a double free when destroying the
bind_conf contexts since none of the lines knows if others still use
it. The impact is very low however, mostly a core and/or a message in
the system's log upon old process termination.

Let's introduce some basic refcounting to prevent this from happening,
so that only the last bind_conf frees it.

Thanks to Janusz Dziemidowicz and Thierry Fournier for both reporting
the same issue with an easy reproducer.

This fix needs to be backported from 1.6 to 1.8.
---
 include/types/ssl_sock.h | 1 +
 src/ssl_sock.c           | 6 ++++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index 8a5b3ee..2e02631 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -59,6 +59,7 @@ struct tls_keys_ref {
        struct list list; /* Used to chain refs. */
        char *filename;
        int unique_id; /* Each pattern reference have unique id. */
+       int refcount;  /* number of users of this tls_keys_ref. */
        struct tls_sess_key *tlskeys;
        int tls_ticket_enc_index;
        __decl_hathreads(HA_RWLOCK_T lock); /* lock used to protect the ref */
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index b5547cc..3f70b12 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -4823,7 +4823,7 @@ void ssl_sock_destroy_bind_conf(struct bind_conf 
*bind_conf)
        ssl_sock_free_ssl_conf(&bind_conf->ssl_conf);
        free(bind_conf->ca_sign_file);
        free(bind_conf->ca_sign_pass);
-       if (bind_conf->keys_ref) {
+       if (bind_conf->keys_ref && !--bind_conf->keys_ref->refcount) {
                free(bind_conf->keys_ref->filename);
                free(bind_conf->keys_ref->tlskeys);
                LIST_DEL(&bind_conf->keys_ref->list);
@@ -7672,7 +7672,8 @@ static int bind_parse_tls_ticket_keys(char **args, int 
cur_arg, struct proxy *px
        }
 
        keys_ref = tlskeys_ref_lookup(args[cur_arg + 1]);
-       if(keys_ref) {
+       if (keys_ref) {
+               keys_ref->refcount++;
                conf->keys_ref = keys_ref;
                return 0;
        }
@@ -7719,6 +7720,7 @@ static int bind_parse_tls_ticket_keys(char **args, int 
cur_arg, struct proxy *px
        i -= 2;
        keys_ref->tls_ticket_enc_index = i < 0 ? 0 : i % TLS_TICKETS_NO;
        keys_ref->unique_id = -1;
+       keys_ref->refcount = 1;
        HA_RWLOCK_INIT(&keys_ref->lock);
        conf->keys_ref = keys_ref;
 
-- 
1.7.12.1

Reply via email to