Memory saving patch.
authorBen Laurie <ben@openssl.org>
Tue, 3 Jun 2008 02:48:34 +0000 (02:48 +0000)
committerBen Laurie <ben@openssl.org>
Tue, 3 Jun 2008 02:48:34 +0000 (02:48 +0000)
CHANGES
doc/ssl/SSL_CTX_set_mode.pod
ssl/s23_clnt.c
ssl/s23_srvr.c
ssl/s3_both.c
ssl/s3_lib.c
ssl/s3_pkt.c
ssl/ssl.h
ssl/ssl_lib.c
ssl/ssl_locl.h

diff --git a/CHANGES b/CHANGES
index c5c50787cdbd051ce959af1b3a7977f05a42ea5b..78272fac5b83fe0aa953a0a9a10bca6c483a06f7 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 0.9.8g and 0.9.9  [xx XXX xxxx]
 
+  *) Add a new SSL_MODE_RELEASE_BUFFERS mode flag to release unused buffer
+     RAM on SSL connections.  This option can save about 34k per idle SSL.
+     [Nick Mathewson]
+
   *) Expand ENGINE to support engine supplied SSL client certificate functions.
      [Steve Henson]
 
index 9822544e5e2d8feb37be9bc584a75e2df6776317..8cb669daeb78c122e91fe5f7b5a625359d06e0d5 100644 (file)
@@ -61,6 +61,16 @@ deal with read/write operations returning without success report. The
 flag SSL_MODE_AUTO_RETRY will cause read/write operations to only
 return after the handshake and successful completion.
 
+=item SSL_MODE_RELEASE_BUFFERS
+
+When we no longer need a read buffer or a write buffer for a given SSL,
+then release the memory we were using to hold it.  Released memory is
+either appended to a list of unused RAM chunks on the SSL_CTX, or simply
+freed if the list of unused chunks would become longer than 
+SSL_CTX->freelist_max_len, which defaults to 32.  Using this flag can
+save around 34k per idle SSL connection.
+This flag has no effect on SSL v2 connections, or on DTLS connections.
+
 =back
 
 =head1 RETURN VALUES
index 78c39d9af558bf1137a52017f33aad913cc4cccc..f90e107c9f5a4afca9b2786a2426f7b60a293aa4 100644 (file)
@@ -640,6 +640,9 @@ static int ssl23_get_server_hello(SSL *s)
                 * for SSLv3 */
                s->rstate=SSL_ST_READ_HEADER;
                s->packet_length=n;
+               if (s->s3->rbuf.buf == NULL)
+                       if (!ssl3_setup_read_buffer(s))
+                               goto err;
                s->packet= &(s->s3->rbuf.buf[0]);
                memcpy(s->packet,buf,n);
                s->s3->rbuf.left=n;
index ddf5d4c3f54b9e239b89b85a8c1876629e4f9e9d..9d5481cd0e020c07a150cf04f70e4a76411c2b41 100644 (file)
@@ -545,6 +545,10 @@ int ssl23_get_client_hello(SSL *s)
                         * for SSLv3 */
                        s->rstate=SSL_ST_READ_HEADER;
                        s->packet_length=n;
+                       if (s->s3->rbuf.buf == NULL)
+                               if (!ssl3_setup_read_buffer(s))
+                                       goto err;
+
                        s->packet= &(s->s3->rbuf.buf[0]);
                        memcpy(s->packet,buf,n);
                        s->s3->rbuf.left=n;
index 1a45e677a4897d89f68794b93f66dba13f8a4d4d..88bed0fa6bea5314aaed7c17cfb5635d5cd95b38 100644 (file)
@@ -591,7 +591,81 @@ int ssl_verify_alarm_type(long type)
        return(al);
        }
 
-int ssl3_setup_buffers(SSL *s)
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+/* On some platforms, malloc() performance is bad enough that you can't just
+ * free() and malloc() buffers all the time, so we need to use freelists from
+ * unused buffers.  Currently, each freelist holds memory chunks of only a
+ * given size (list->chunklen); other sized chunks are freed and malloced.
+ * This doesn't help much if you're using many different SSL option settings
+ * with a given context.  (The options affecting buffer size are
+ * max_send_fragment, read buffer vs write buffer,
+ * SSL_OP_MICROSOFT_BIG_WRITE_BUFFER, SSL_OP_NO_COMPRESSION, and
+ * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS.)  Using a separate freelist for every
+ * possible size is not an option, since max_send_fragment can take on many
+ * different values.
+ *
+ * If you are on a platform with a slow malloc(), and you're using SSL
+ * connections with many different settings for these options, and you need to
+ * use the SSL_MOD_RELEASE_BUFFERS feature, you have a few options:
+ *    - Link against a faster malloc implementation.
+ *    - Use a separate SSL_CTX for each option set.
+ *    - Improve this code.
+ */
+static void *
+freelist_extract(SSL_CTX *ctx, int for_read, int sz)
+       {
+       SSL3_BUF_FREELIST *list;
+       SSL3_BUF_FREELIST_ENTRY *ent = NULL;
+       void *result = NULL;
+
+       CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+       list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
+       if (list != NULL && sz == list->chunklen)
+               ent = list->head;
+       if (ent != NULL)
+               {
+               list->head = ent->next;
+               result = ent;
+               if (--list->len == 0)
+                       list->chunklen = 0;
+               }
+       CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+       if (!result)
+               result = OPENSSL_malloc(sz);
+       return result;
+}
+
+static void
+freelist_insert(SSL_CTX *ctx, int for_read, size_t sz, void *mem)
+       {
+       SSL3_BUF_FREELIST *list;
+       SSL3_BUF_FREELIST_ENTRY *ent;
+
+       CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+       list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
+       if (list != NULL &&
+           (sz == list->chunklen || list->chunklen == 0) &&
+           list->len < ctx->freelist_max_len &&
+           sz >= sizeof(*ent))
+               {
+               list->chunklen = sz;
+               ent = mem;
+               ent->next = list->head;
+               list->head = ent;
+               ++list->len;
+               mem = NULL;
+               }
+
+       CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+       if (mem)
+               OPENSSL_free(mem);
+       }
+#else
+#define freelist_extract(c,fr,sz) OPENSSL_malloc(sz)
+#define freelist_insert(c,fr,sz,m) OPENSSL_free(m)
+#endif
+
+int ssl3_setup_read_buffer(SSL *s)
        {
        unsigned char *p;
        size_t len,align=0;
@@ -614,12 +688,29 @@ int ssl3_setup_buffers(SSL *s)
                if (!(s->options & SSL_OP_NO_COMPRESSION))
                        len += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
 #endif
-               if ((p=OPENSSL_malloc(len)) == NULL)
+               if ((p=freelist_extract(s->ctx, 1, len)) == NULL)
                        goto err;
                s->s3->rbuf.buf = p;
                s->s3->rbuf.len = len;
                }
 
+       s->packet= &(s->s3->rbuf.buf[0]);
+       return 1;
+
+err:
+       SSLerr(SSL_F_SSL3_SETUP_BUFFERS,ERR_R_MALLOC_FAILURE);
+       return 0;
+       }
+
+int ssl3_setup_write_buffer(SSL *s)
+       {
+       unsigned char *p;
+       size_t len,align=0;
+
+#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
+       align = (-SSL3_RT_HEADER_LENGTH)&(SSL3_ALIGN_PAYLOAD-1);
+#endif
+
        if (s->s3->wbuf.buf == NULL)
                {
                len = s->max_send_fragment
@@ -632,14 +723,47 @@ int ssl3_setup_buffers(SSL *s)
                if (!(s->options & SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS))
                        len += SSL3_RT_HEADER_LENGTH + align
                                + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD;
-               if ((p=OPENSSL_malloc(len)) == NULL)
+
+               if ((p=freelist_extract(s->ctx, 0, len)) == NULL)
                        goto err;
                s->s3->wbuf.buf = p;
                s->s3->wbuf.len = len;
                }
-       s->packet= &(s->s3->rbuf.buf[0]);
-       return(1);
+
+       return 1;
+
 err:
        SSLerr(SSL_F_SSL3_SETUP_BUFFERS,ERR_R_MALLOC_FAILURE);
-       return(0);
+       return 0;
+       }
+
+
+int ssl3_setup_buffers(SSL *s)
+       {
+       if (!ssl3_setup_read_buffer(s))
+               return 0;
+       if (!ssl3_setup_write_buffer(s))
+               return 0;
+       return 1;
+       }
+
+int ssl3_release_write_buffer(SSL *s)
+       {
+       if (s->s3->wbuf.buf != NULL)
+               {
+               freelist_insert(s->ctx, 0, s->s3->wbuf.len, s->s3->wbuf.buf);
+               s->s3->wbuf.buf = NULL;
+               }
+       return 1;
        }
+
+int ssl3_release_read_buffer(SSL *s)
+       {
+       if (s->s3->rbuf.buf != NULL)
+               {
+               freelist_insert(s->ctx, 1, s->s3->rbuf.len, s->s3->rbuf.buf);
+               s->s3->rbuf.buf = NULL;
+               }
+       return 1;
+       }
+
index 6844a43b9e757ba0427dce3249ab6254a57f70d0..2c0dc7ab14f709846d875559dcf5f55d1f133f80 100644 (file)
@@ -2148,9 +2148,9 @@ void ssl3_free(SSL *s)
 
        ssl3_cleanup_key_block(s);
        if (s->s3->rbuf.buf != NULL)
-               OPENSSL_free(s->s3->rbuf.buf);
+               ssl3_release_read_buffer(s);
        if (s->s3->wbuf.buf != NULL)
-               OPENSSL_free(s->s3->wbuf.buf);
+               ssl3_release_write_buffer(s);
        if (s->s3->rrec.comp != NULL)
                OPENSSL_free(s->s3->rrec.comp);
 #ifndef OPENSSL_NO_DH
index 3e5ce0c5b9bbb1970a5f92b3a318809b74befb24..b4a1629853afbe9eb9a6959800ec95893dd968f7 100644 (file)
@@ -137,6 +137,10 @@ int ssl3_read_n(SSL *s, int n, int max, int extend)
        if (n <= 0) return n;
 
        rb    = &(s->s3->rbuf);
+       if (rb->buf == NULL)
+               if (!ssl3_setup_read_buffer(s))
+                       return -1;
+
        left  = rb->left;
 #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
        align = (long)rb->buf + SSL3_RT_HEADER_LENGTH;
@@ -234,6 +238,11 @@ int ssl3_read_n(SSL *s, int n, int max, int extend)
                if (i <= 0)
                        {
                        rb->left = left;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+                       if (len+left == 0 &&
+                           (s->mode & SSL_MODE_RELEASE_BUFFERS))
+                               ssl3_release_read_buffer(s);
+#endif
                        return(i);
                        }
                left+=i;
@@ -609,6 +618,10 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
        SSL3_BUFFER *wb=&(s->s3->wbuf);
        SSL_SESSION *sess;
 
+       if (wb->buf == NULL)
+               if (!ssl3_setup_write_buffer(s))
+                       return -1;
+
        /* first check if there is a SSL3_BUFFER still being written
         * out.  This will happen with non blocking IO */
        if (wb->left != 0)
@@ -812,6 +825,10 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
                        {
                        wb->left=0;
                        wb->offset+=i;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+                       if (s->mode & SSL_MODE_RELEASE_BUFFERS)
+                               ssl3_release_write_buffer(s);
+#endif
                        s->rwstate=SSL_NOTHING;
                        return(s->s3->wpend_ret);
                        }
@@ -857,7 +874,7 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
        void (*cb)(const SSL *ssl,int type2,int val)=NULL;
 
        if (s->s3->rbuf.buf == NULL) /* Not initialized yet */
-               if (!ssl3_setup_buffers(s))
+               if (!ssl3_setup_read_buffer(s))
                        return(-1);
 
        if ((type && (type != SSL3_RT_APPLICATION_DATA) && (type != SSL3_RT_HANDSHAKE) && type) ||
@@ -966,6 +983,10 @@ start:
                                {
                                s->rstate=SSL_ST_READ_HEADER;
                                rr->off=0;
+#ifndef OPENSSL_NO_RELEASE_BUFFERS
+                               if ((s->mode & SSL_MODE_RELEASE_BUFFERS))
+                                       ssl3_release_read_buffer(s);
+#endif
                                }
                        }
                return(n);
index b43fc0da0dacfca49894a4d06d3150e664ca8993..a9de499e0b6182385e1e2d63be2fccce1136e31a 100644 (file)
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -587,7 +587,10 @@ typedef struct ssl_session_st
 #define SSL_MODE_AUTO_RETRY 0x00000004L
 /* Don't attempt to automatically build certificate chain */
 #define SSL_MODE_NO_AUTO_CHAIN 0x00000008L
-
+/* Save RAM by releasing read and write buffers when they're empty. (SSL3 and
+ * TLS only.)  "Released" buffers are put onto a free-list in the context
+ * or just freed (depending on the context's setting for freelist_max_len). */
+#define SSL_MODE_RELEASE_BUFFERS 0x00000010L
 
 /* Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value,
  * they cannot be used to clear bits. */
@@ -836,6 +839,13 @@ struct ssl_ctx_st
        unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
                unsigned char *psk, unsigned int max_psk_len);
 #endif
+
+#if !defined(OPENSSL_NO_BUF_FREELISTS) || !defined(OPENSSL_NO_RELEASE_BUFFERS)
+#define SSL_MAX_BUF_FREELIST_LEN_DEFAULT 32
+       unsigned int freelist_max_len;
+       struct ssl3_buf_freelist_st *wbuf_freelist;
+       struct ssl3_buf_freelist_st *rbuf_freelist;
+#endif
        };
 
 #define SSL_SESS_CACHE_OFF                     0x0000
index fac080c19d4661612fd733fab1cecc8cdec6d4e4..ebff8e23e64c32dd1df0036ba313566d611d37e5 100644 (file)
@@ -1449,7 +1449,7 @@ static IMPLEMENT_LHASH_COMP_FN(ssl_session, SSL_SESSION)
 SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
        {
        SSL_CTX *ret=NULL;
-       
+
        if (meth == NULL)
                {
                SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_NULL_SSL_METHOD_PASSED);
@@ -1580,6 +1580,24 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
        ret->psk_identity_hint=NULL;
        ret->psk_client_callback=NULL;
        ret->psk_server_callback=NULL;
+#endif
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+       ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
+       ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
+       if (!ret->rbuf_freelist)
+               goto err;
+       ret->rbuf_freelist->chunklen = 0;
+       ret->rbuf_freelist->len = 0;
+       ret->rbuf_freelist->head = NULL;
+       ret->wbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
+       if (!ret->wbuf_freelist)
+               {
+               OPENSSL_free(ret->rbuf_freelist);
+               goto err;
+               }
+       ret->wbuf_freelist->chunklen = 0;
+       ret->wbuf_freelist->len = 0;
+       ret->wbuf_freelist->head = NULL;
 #endif
        return(ret);
 err:
@@ -1594,6 +1612,20 @@ static void SSL_COMP_free(SSL_COMP *comp)
     { OPENSSL_free(comp); }
 #endif
 
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+static void
+ssl_buf_freelist_free(SSL3_BUF_FREELIST *list)
+       {
+       SSL3_BUF_FREELIST_ENTRY *ent, *next;
+       for (ent = list->head; ent; ent = next)
+               {
+               next = ent->next;
+               OPENSSL_free(ent);
+               }
+       OPENSSL_free(list);
+       }
+#endif
+
 void SSL_CTX_free(SSL_CTX *a)
        {
        int i;
@@ -1660,6 +1692,14 @@ void SSL_CTX_free(SSL_CTX *a)
        if (a->client_cert_engine)
                ENGINE_finish(a->client_cert_engine);
 #endif
+
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+       if (a->wbuf_freelist)
+               ssl_buf_freelist_free(a->wbuf_freelist);
+       if (a->rbuf_freelist)
+               ssl_buf_freelist_free(a->rbuf_freelist);
+#endif
+
        OPENSSL_free(a);
        }
 
@@ -2901,7 +2941,7 @@ void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int version, int con
  * vairable, freeing  EVP_MD_CTX previously stored in that variable, if
  * any. If EVP_MD pointer is passed, initializes ctx with this md
  * Returns newly allocated ctx;
- */ 
+ */
 
 EVP_MD_CTX *ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) 
 {
index 55b23304c1ad0b6f2a2de97c21d566faa5141dd1..0238676ff3949ff9e43e68c7dfb02f7851560d84 100644 (file)
@@ -561,6 +561,20 @@ typedef struct ssl3_comp_st
        COMP_METHOD *method; /* The method :-) */
        } SSL3_COMP;
 
+#if !defined(OPENSSL_NO_BUF_FREELISTS) && !defined(OPENSSL_NO_RELEASE_BUFFERS)
+typedef struct ssl3_buf_freelist_st
+       {
+       size_t chunklen;
+       int len;
+       struct ssl3_buf_freelist_entry_st *head;
+       } SSL3_BUF_FREELIST;
+
+typedef struct ssl3_buf_freelist_entry_st
+       {
+       struct ssl3_buf_freelist_entry_st *next;
+       } SSL3_BUF_FREELIST_ENTRY;
+#endif
+
 extern SSL3_ENC_METHOD ssl3_undef_enc_method;
 OPENSSL_EXTERN SSL_CIPHER ssl2_ciphers[];
 OPENSSL_EXTERN SSL_CIPHER ssl3_ciphers[];
@@ -859,6 +873,10 @@ unsigned long ssl3_output_cert_chain(SSL *s, X509 *x);
 SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,STACK_OF(SSL_CIPHER) *clnt,
                               STACK_OF(SSL_CIPHER) *srvr);
 int    ssl3_setup_buffers(SSL *s);
+int    ssl3_setup_read_buffer(SSL *s);
+int    ssl3_setup_write_buffer(SSL *s);
+int    ssl3_release_read_buffer(SSL *s);
+int    ssl3_release_write_buffer(SSL *s);
 void ssl3_digest_cached_records(SSL *s);
 int    ssl3_new(SSL *s);
 void   ssl3_free(SSL *s);