summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--doc/ssl/SSL_CTX_set_mode.pod10
-rw-r--r--ssl/s23_clnt.c3
-rw-r--r--ssl/s23_srvr.c4
-rw-r--r--ssl/s3_both.c136
-rw-r--r--ssl/s3_lib.c4
-rw-r--r--ssl/s3_pkt.c23
-rw-r--r--ssl/ssl.h12
-rw-r--r--ssl/ssl_lib.c44
-rw-r--r--ssl/ssl_locl.h18
10 files changed, 246 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index c5c50787cd..78272fac5b 100644
--- 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]
diff --git a/doc/ssl/SSL_CTX_set_mode.pod b/doc/ssl/SSL_CTX_set_mode.pod
index 9822544e5e..8cb669daeb 100644
--- a/doc/ssl/SSL_CTX_set_mode.pod
+++ b/doc/ssl/SSL_CTX_set_mode.pod
@@ -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
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index 78c39d9af5..f90e107c9f 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -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;
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index ddf5d4c3f5..9d5481cd0e 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -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;
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 1a45e677a4..88bed0fa6b 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -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;
+ }
+
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 6844a43b9e..2c0dc7ab14 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -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
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index 3e5ce0c5b9..b4a1629853 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -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);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index b43fc0da0d..a9de499e0b 100644
--- 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
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index fac080c19d..ebff8e23e6 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -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);
@@ -1581,6 +1581,24 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
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:
SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE);
@@ -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)
{
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 55b23304c1..0238676ff3 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -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);