Fix memory leak from zero-length DTLS fragments.
authorAdam Langley <agl@imperialviolet.org>
Fri, 6 Jun 2014 21:30:33 +0000 (14:30 -0700)
committerMatt Caswell <matt@openssl.org>
Wed, 6 Aug 2014 21:02:00 +0000 (22:02 +0100)
The |pqueue_insert| function can fail if one attempts to insert a
duplicate sequence number. When handling a fragment of an out of
sequence message, |dtls1_process_out_of_seq_message| would not call
|dtls1_reassemble_fragment| if the fragment's length was zero. It would
then allocate a fresh fragment and attempt to insert it, but ignore the
return value, leaking the fragment.

This allows an attacker to exhaust the memory of a DTLS peer.

Fixes CVE-2014-3507

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Emilia Käsper <emilia@openssl.org>
ssl/d1_both.c

index e0eed129b80994541ece6191921c296d0f817d6b..99325e8f04a4c3bad9e9461e12229ad7d5f44cd7 100644 (file)
@@ -605,6 +605,9 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
            msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
                goto err;
 
+       if (frag_len == 0)
+               return DTLS1_HM_FRAGMENT_RETRY;
+
        /* Try to find item in queue */
        pq_64bit_init(&seq64);
        pq_64bit_assign_word(&seq64, msg_hdr->seq);
@@ -682,7 +685,12 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                        goto err;
                        }
 
-               pqueue_insert(s->d1->buffered_messages, item);
+               item = pqueue_insert(s->d1->buffered_messages, item);
+               /* pqueue_insert fails iff a duplicate item is inserted.
+                * However, |item| cannot be a duplicate. If it were,
+                * |pqueue_find|, above, would have returned it and control
+                * would never have reached this branch. */
+               OPENSSL_assert(item != NULL);
                }
 
        return DTLS1_HM_FRAGMENT_RETRY;
@@ -740,7 +748,7 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                }
        else
                {
-               if (frag_len && frag_len < msg_hdr->msg_len)
+               if (frag_len < msg_hdr->msg_len)
                        return dtls1_reassemble_fragment(s, msg_hdr, ok);
 
                if (frag_len > dtls1_max_handshake_message_len(s))
@@ -769,7 +777,15 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                if ( item == NULL)
                        goto err;
 
-               pqueue_insert(s->d1->buffered_messages, item);
+               item = pqueue_insert(s->d1->buffered_messages, item);
+               /* pqueue_insert fails iff a duplicate item is inserted.
+                * However, |item| cannot be a duplicate. If it were,
+                * |pqueue_find|, above, would have returned it. Then, either
+                * |frag_len| != |msg_hdr->msg_len| in which case |item| is set
+                * to NULL and it will have been processed with
+                * |dtls1_reassemble_fragment|, above, or the record will have
+                * been discarded. */
+               OPENSSL_assert(item != NULL);
                }
 
        return DTLS1_HM_FRAGMENT_RETRY;