Home | History | Annotate | Download | only in pool
      1 /* Copyright (c) 2016, Google Inc.
      2  *
      3  * Permission to use, copy, modify, and/or distribute this software for any
      4  * purpose with or without fee is hereby granted, provided that the above
      5  * copyright notice and this permission notice appear in all copies.
      6  *
      7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
     14 
     15 #include <openssl/pool.h>
     16 
     17 #include <assert.h>
     18 #include <string.h>
     19 
     20 #include <openssl/buf.h>
     21 #include <openssl/bytestring.h>
     22 #include <openssl/mem.h>
     23 #include <openssl/thread.h>
     24 
     25 #include "../internal.h"
     26 #include "internal.h"
     27 
     28 
     29 DEFINE_LHASH_OF(CRYPTO_BUFFER)
     30 
     31 static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
     32   return OPENSSL_hash32(buf->data, buf->len);
     33 }
     34 
     35 static int CRYPTO_BUFFER_cmp(const CRYPTO_BUFFER *a, const CRYPTO_BUFFER *b) {
     36   if (a->len != b->len) {
     37     return 1;
     38   }
     39   return OPENSSL_memcmp(a->data, b->data, a->len);
     40 }
     41 
     42 CRYPTO_BUFFER_POOL* CRYPTO_BUFFER_POOL_new(void) {
     43   CRYPTO_BUFFER_POOL *pool = OPENSSL_malloc(sizeof(CRYPTO_BUFFER_POOL));
     44   if (pool == NULL) {
     45     return NULL;
     46   }
     47 
     48   OPENSSL_memset(pool, 0, sizeof(CRYPTO_BUFFER_POOL));
     49   pool->bufs = lh_CRYPTO_BUFFER_new(CRYPTO_BUFFER_hash, CRYPTO_BUFFER_cmp);
     50   if (pool->bufs == NULL) {
     51     OPENSSL_free(pool);
     52     return NULL;
     53   }
     54 
     55   CRYPTO_MUTEX_init(&pool->lock);
     56 
     57   return pool;
     58 }
     59 
     60 void CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL *pool) {
     61   if (pool == NULL) {
     62     return;
     63   }
     64 
     65 #if !defined(NDEBUG)
     66   CRYPTO_MUTEX_lock_write(&pool->lock);
     67   assert(lh_CRYPTO_BUFFER_num_items(pool->bufs) == 0);
     68   CRYPTO_MUTEX_unlock_write(&pool->lock);
     69 #endif
     70 
     71   lh_CRYPTO_BUFFER_free(pool->bufs);
     72   CRYPTO_MUTEX_cleanup(&pool->lock);
     73   OPENSSL_free(pool);
     74 }
     75 
     76 CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
     77                                  CRYPTO_BUFFER_POOL *pool) {
     78   if (pool != NULL) {
     79     CRYPTO_BUFFER tmp;
     80     tmp.data = (uint8_t *) data;
     81     tmp.len = len;
     82 
     83     CRYPTO_MUTEX_lock_read(&pool->lock);
     84     CRYPTO_BUFFER *const duplicate =
     85         lh_CRYPTO_BUFFER_retrieve(pool->bufs, &tmp);
     86     if (duplicate != NULL) {
     87       CRYPTO_refcount_inc(&duplicate->references);
     88     }
     89     CRYPTO_MUTEX_unlock_read(&pool->lock);
     90 
     91     if (duplicate != NULL) {
     92       return duplicate;
     93     }
     94   }
     95 
     96   CRYPTO_BUFFER *const buf = OPENSSL_malloc(sizeof(CRYPTO_BUFFER));
     97   if (buf == NULL) {
     98     return NULL;
     99   }
    100   OPENSSL_memset(buf, 0, sizeof(CRYPTO_BUFFER));
    101 
    102   buf->data = BUF_memdup(data, len);
    103   if (len != 0 && buf->data == NULL) {
    104     OPENSSL_free(buf);
    105     return NULL;
    106   }
    107 
    108   buf->len = len;
    109   buf->references = 1;
    110 
    111   if (pool == NULL) {
    112     return buf;
    113   }
    114 
    115   buf->pool = pool;
    116 
    117   CRYPTO_MUTEX_lock_write(&pool->lock);
    118   CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
    119   int inserted = 0;
    120   if (duplicate == NULL) {
    121     CRYPTO_BUFFER *old = NULL;
    122     inserted = lh_CRYPTO_BUFFER_insert(pool->bufs, &old, buf);
    123     assert(old == NULL);
    124   } else {
    125     CRYPTO_refcount_inc(&duplicate->references);
    126   }
    127   CRYPTO_MUTEX_unlock_write(&pool->lock);
    128 
    129   if (!inserted) {
    130     // We raced to insert |buf| into the pool and lost, or else there was an
    131     // error inserting.
    132     OPENSSL_free(buf->data);
    133     OPENSSL_free(buf);
    134     return duplicate;
    135   }
    136 
    137   return buf;
    138 }
    139 
    140 CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
    141   CRYPTO_BUFFER *const buf = OPENSSL_malloc(sizeof(CRYPTO_BUFFER));
    142   if (buf == NULL) {
    143     return NULL;
    144   }
    145   OPENSSL_memset(buf, 0, sizeof(CRYPTO_BUFFER));
    146 
    147   buf->data = OPENSSL_malloc(len);
    148   if (len != 0 && buf->data == NULL) {
    149     OPENSSL_free(buf);
    150     return NULL;
    151   }
    152   buf->len = len;
    153   buf->references = 1;
    154 
    155   *out_data = buf->data;
    156   return buf;
    157 }
    158 
    159 CRYPTO_BUFFER* CRYPTO_BUFFER_new_from_CBS(CBS *cbs, CRYPTO_BUFFER_POOL *pool) {
    160   return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
    161 }
    162 
    163 void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
    164   if (buf == NULL) {
    165     return;
    166   }
    167 
    168   CRYPTO_BUFFER_POOL *const pool = buf->pool;
    169   if (pool == NULL) {
    170     if (CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
    171       // If a reference count of zero is observed, there cannot be a reference
    172       // from any pool to this buffer and thus we are able to free this
    173       // buffer.
    174       OPENSSL_free(buf->data);
    175       OPENSSL_free(buf);
    176     }
    177 
    178     return;
    179   }
    180 
    181   CRYPTO_MUTEX_lock_write(&pool->lock);
    182   if (!CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
    183     CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
    184     return;
    185   }
    186 
    187   // We have an exclusive lock on the pool, therefore no concurrent lookups can
    188   // find this buffer and increment the reference count. Thus, if the count is
    189   // zero there are and can never be any more references and thus we can free
    190   // this buffer.
    191   void *found = lh_CRYPTO_BUFFER_delete(pool->bufs, buf);
    192   assert(found != NULL);
    193   assert(found == buf);
    194   (void)found;
    195   CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
    196   OPENSSL_free(buf->data);
    197   OPENSSL_free(buf);
    198 }
    199 
    200 int CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER *buf) {
    201   // This is safe in the case that |buf->pool| is NULL because it's just
    202   // standard reference counting in that case.
    203   //
    204   // This is also safe if |buf->pool| is non-NULL because, if it were racing
    205   // with |CRYPTO_BUFFER_free| then the two callers must have independent
    206   // references already and so the reference count will never hit zero.
    207   CRYPTO_refcount_inc(&buf->references);
    208   return 1;
    209 }
    210 
    211 const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
    212   return buf->data;
    213 }
    214 
    215 size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
    216   return buf->len;
    217 }
    218 
    219 void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
    220   CBS_init(out, buf->data, buf->len);
    221 }
    222