• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CRYPTO_BUFFER_hash(const CRYPTO_BUFFER * buf)29 static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
30   return OPENSSL_hash32(buf->data, buf->len);
31 }
32 
CRYPTO_BUFFER_cmp(const CRYPTO_BUFFER * a,const CRYPTO_BUFFER * b)33 static int CRYPTO_BUFFER_cmp(const CRYPTO_BUFFER *a, const CRYPTO_BUFFER *b) {
34   if (a->len != b->len) {
35     return 1;
36   }
37   return OPENSSL_memcmp(a->data, b->data, a->len);
38 }
39 
CRYPTO_BUFFER_POOL_new(void)40 CRYPTO_BUFFER_POOL* CRYPTO_BUFFER_POOL_new(void) {
41   CRYPTO_BUFFER_POOL *pool = OPENSSL_malloc(sizeof(CRYPTO_BUFFER_POOL));
42   if (pool == NULL) {
43     return NULL;
44   }
45 
46   OPENSSL_memset(pool, 0, sizeof(CRYPTO_BUFFER_POOL));
47   pool->bufs = lh_CRYPTO_BUFFER_new(CRYPTO_BUFFER_hash, CRYPTO_BUFFER_cmp);
48   if (pool->bufs == NULL) {
49     OPENSSL_free(pool);
50     return NULL;
51   }
52 
53   CRYPTO_MUTEX_init(&pool->lock);
54 
55   return pool;
56 }
57 
CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL * pool)58 void CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL *pool) {
59   if (pool == NULL) {
60     return;
61   }
62 
63 #if !defined(NDEBUG)
64   CRYPTO_MUTEX_lock_write(&pool->lock);
65   assert(lh_CRYPTO_BUFFER_num_items(pool->bufs) == 0);
66   CRYPTO_MUTEX_unlock_write(&pool->lock);
67 #endif
68 
69   lh_CRYPTO_BUFFER_free(pool->bufs);
70   CRYPTO_MUTEX_cleanup(&pool->lock);
71   OPENSSL_free(pool);
72 }
73 
CRYPTO_BUFFER_new(const uint8_t * data,size_t len,CRYPTO_BUFFER_POOL * pool)74 CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
75                                  CRYPTO_BUFFER_POOL *pool) {
76   if (pool != NULL) {
77     CRYPTO_BUFFER tmp;
78     tmp.data = (uint8_t *) data;
79     tmp.len = len;
80 
81     CRYPTO_MUTEX_lock_read(&pool->lock);
82     CRYPTO_BUFFER *const duplicate =
83         lh_CRYPTO_BUFFER_retrieve(pool->bufs, &tmp);
84     if (duplicate != NULL) {
85       CRYPTO_refcount_inc(&duplicate->references);
86     }
87     CRYPTO_MUTEX_unlock_read(&pool->lock);
88 
89     if (duplicate != NULL) {
90       return duplicate;
91     }
92   }
93 
94   CRYPTO_BUFFER *const buf = OPENSSL_malloc(sizeof(CRYPTO_BUFFER));
95   if (buf == NULL) {
96     return NULL;
97   }
98   OPENSSL_memset(buf, 0, sizeof(CRYPTO_BUFFER));
99 
100   buf->data = BUF_memdup(data, len);
101   if (len != 0 && buf->data == NULL) {
102     OPENSSL_free(buf);
103     return NULL;
104   }
105 
106   buf->len = len;
107   buf->references = 1;
108 
109   if (pool == NULL) {
110     return buf;
111   }
112 
113   buf->pool = pool;
114 
115   CRYPTO_MUTEX_lock_write(&pool->lock);
116   CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
117   int inserted = 0;
118   if (duplicate == NULL) {
119     CRYPTO_BUFFER *old = NULL;
120     inserted = lh_CRYPTO_BUFFER_insert(pool->bufs, &old, buf);
121     assert(old == NULL);
122   } else {
123     CRYPTO_refcount_inc(&duplicate->references);
124   }
125   CRYPTO_MUTEX_unlock_write(&pool->lock);
126 
127   if (!inserted) {
128     /* We raced to insert |buf| into the pool and lost, or else there was an
129      * error inserting. */
130     OPENSSL_free(buf->data);
131     OPENSSL_free(buf);
132     return duplicate;
133   }
134 
135   return buf;
136 }
137 
CRYPTO_BUFFER_new_from_CBS(CBS * cbs,CRYPTO_BUFFER_POOL * pool)138 CRYPTO_BUFFER* CRYPTO_BUFFER_new_from_CBS(CBS *cbs, CRYPTO_BUFFER_POOL *pool) {
139   return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
140 }
141 
CRYPTO_BUFFER_free(CRYPTO_BUFFER * buf)142 void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
143   if (buf == NULL) {
144     return;
145   }
146 
147   CRYPTO_BUFFER_POOL *const pool = buf->pool;
148   if (pool == NULL) {
149     if (CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
150       /* If a reference count of zero is observed, there cannot be a reference
151        * from any pool to this buffer and thus we are able to free this
152        * buffer. */
153       OPENSSL_free(buf->data);
154       OPENSSL_free(buf);
155     }
156 
157     return;
158   }
159 
160   CRYPTO_MUTEX_lock_write(&pool->lock);
161   if (!CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
162     CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
163     return;
164   }
165 
166   /* We have an exclusive lock on the pool, therefore no concurrent lookups can
167    * find this buffer and increment the reference count. Thus, if the count is
168    * zero there are and can never be any more references and thus we can free
169    * this buffer. */
170   void *found = lh_CRYPTO_BUFFER_delete(pool->bufs, buf);
171   assert(found != NULL);
172   assert(found == buf);
173   (void)found;
174   CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
175   OPENSSL_free(buf->data);
176   OPENSSL_free(buf);
177 }
178 
CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER * buf)179 int CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER *buf) {
180   /* This is safe in the case that |buf->pool| is NULL because it's just
181    * standard reference counting in that case.
182    *
183    * This is also safe if |buf->pool| is non-NULL because, if it were racing
184    * with |CRYPTO_BUFFER_free| then the two callers must have independent
185    * references already and so the reference count will never hit zero. */
186   CRYPTO_refcount_inc(&buf->references);
187   return 1;
188 }
189 
CRYPTO_BUFFER_data(const CRYPTO_BUFFER * buf)190 const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
191   return buf->data;
192 }
193 
CRYPTO_BUFFER_len(const CRYPTO_BUFFER * buf)194 size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
195   return buf->len;
196 }
197 
CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER * buf,CBS * out)198 void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
199   CBS_init(out, buf->data, buf->len);
200 }
201