• 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/bytestring.h>
21 #include <openssl/mem.h>
22 #include <openssl/rand.h>
23 #include <openssl/siphash.h>
24 #include <openssl/thread.h>
25 
26 #include "../internal.h"
27 #include "internal.h"
28 
29 
CRYPTO_BUFFER_hash(const CRYPTO_BUFFER * buf)30 static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
31   return (uint32_t)SIPHASH_24(buf->pool->hash_key, buf->data, buf->len);
32 }
33 
CRYPTO_BUFFER_cmp(const CRYPTO_BUFFER * a,const CRYPTO_BUFFER * b)34 static int CRYPTO_BUFFER_cmp(const CRYPTO_BUFFER *a, const CRYPTO_BUFFER *b) {
35   // Only |CRYPTO_BUFFER|s from the same pool have compatible hashes.
36   assert(a->pool != NULL);
37   assert(a->pool == b->pool);
38   if (a->len != b->len) {
39     return 1;
40   }
41   return OPENSSL_memcmp(a->data, b->data, a->len);
42 }
43 
CRYPTO_BUFFER_POOL_new(void)44 CRYPTO_BUFFER_POOL* CRYPTO_BUFFER_POOL_new(void) {
45   CRYPTO_BUFFER_POOL *pool = OPENSSL_malloc(sizeof(CRYPTO_BUFFER_POOL));
46   if (pool == NULL) {
47     return NULL;
48   }
49 
50   OPENSSL_memset(pool, 0, sizeof(CRYPTO_BUFFER_POOL));
51   pool->bufs = lh_CRYPTO_BUFFER_new(CRYPTO_BUFFER_hash, CRYPTO_BUFFER_cmp);
52   if (pool->bufs == NULL) {
53     OPENSSL_free(pool);
54     return NULL;
55   }
56 
57   CRYPTO_MUTEX_init(&pool->lock);
58   RAND_bytes((uint8_t *)&pool->hash_key, sizeof(pool->hash_key));
59 
60   return pool;
61 }
62 
CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL * pool)63 void CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL *pool) {
64   if (pool == NULL) {
65     return;
66   }
67 
68 #if !defined(NDEBUG)
69   CRYPTO_MUTEX_lock_write(&pool->lock);
70   assert(lh_CRYPTO_BUFFER_num_items(pool->bufs) == 0);
71   CRYPTO_MUTEX_unlock_write(&pool->lock);
72 #endif
73 
74   lh_CRYPTO_BUFFER_free(pool->bufs);
75   CRYPTO_MUTEX_cleanup(&pool->lock);
76   OPENSSL_free(pool);
77 }
78 
crypto_buffer_free_object(CRYPTO_BUFFER * buf)79 static void crypto_buffer_free_object(CRYPTO_BUFFER *buf) {
80   if (!buf->data_is_static) {
81     OPENSSL_free(buf->data);
82   }
83   OPENSSL_free(buf);
84 }
85 
crypto_buffer_new(const uint8_t * data,size_t len,int data_is_static,CRYPTO_BUFFER_POOL * pool)86 static CRYPTO_BUFFER *crypto_buffer_new(const uint8_t *data, size_t len,
87                                         int data_is_static,
88                                         CRYPTO_BUFFER_POOL *pool) {
89   if (pool != NULL) {
90     CRYPTO_BUFFER tmp;
91     tmp.data = (uint8_t *) data;
92     tmp.len = len;
93     tmp.pool = pool;
94 
95     CRYPTO_MUTEX_lock_read(&pool->lock);
96     CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, &tmp);
97     if (data_is_static && duplicate != NULL && !duplicate->data_is_static) {
98       // If the new |CRYPTO_BUFFER| would have static data, but the duplicate
99       // does not, we replace the old one with the new static version.
100       duplicate = NULL;
101     }
102     if (duplicate != NULL) {
103       CRYPTO_refcount_inc(&duplicate->references);
104     }
105     CRYPTO_MUTEX_unlock_read(&pool->lock);
106 
107     if (duplicate != NULL) {
108       return duplicate;
109     }
110   }
111 
112   CRYPTO_BUFFER *const buf = OPENSSL_malloc(sizeof(CRYPTO_BUFFER));
113   if (buf == NULL) {
114     return NULL;
115   }
116   OPENSSL_memset(buf, 0, sizeof(CRYPTO_BUFFER));
117 
118   if (data_is_static) {
119     buf->data = (uint8_t *)data;
120     buf->data_is_static = 1;
121   } else {
122     buf->data = OPENSSL_memdup(data, len);
123     if (len != 0 && buf->data == NULL) {
124       OPENSSL_free(buf);
125       return NULL;
126     }
127   }
128 
129   buf->len = len;
130   buf->references = 1;
131 
132   if (pool == NULL) {
133     return buf;
134   }
135 
136   buf->pool = pool;
137 
138   CRYPTO_MUTEX_lock_write(&pool->lock);
139   CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
140   if (data_is_static && duplicate != NULL && !duplicate->data_is_static) {
141     // If the new |CRYPTO_BUFFER| would have static data, but the duplicate does
142     // not, we replace the old one with the new static version.
143     duplicate = NULL;
144   }
145   int inserted = 0;
146   if (duplicate == NULL) {
147     CRYPTO_BUFFER *old = NULL;
148     inserted = lh_CRYPTO_BUFFER_insert(pool->bufs, &old, buf);
149     // |old| may be non-NULL if a match was found but ignored. |pool->bufs| does
150     // not increment refcounts, so there is no need to clean up after the
151     // replacement.
152   } else {
153     CRYPTO_refcount_inc(&duplicate->references);
154   }
155   CRYPTO_MUTEX_unlock_write(&pool->lock);
156 
157   if (!inserted) {
158     // We raced to insert |buf| into the pool and lost, or else there was an
159     // error inserting.
160     crypto_buffer_free_object(buf);
161     return duplicate;
162   }
163 
164   return buf;
165 }
166 
CRYPTO_BUFFER_new(const uint8_t * data,size_t len,CRYPTO_BUFFER_POOL * pool)167 CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
168                                  CRYPTO_BUFFER_POOL *pool) {
169   return crypto_buffer_new(data, len, /*data_is_static=*/0, pool);
170 }
171 
CRYPTO_BUFFER_alloc(uint8_t ** out_data,size_t len)172 CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
173   CRYPTO_BUFFER *const buf = OPENSSL_malloc(sizeof(CRYPTO_BUFFER));
174   if (buf == NULL) {
175     return NULL;
176   }
177   OPENSSL_memset(buf, 0, sizeof(CRYPTO_BUFFER));
178 
179   buf->data = OPENSSL_malloc(len);
180   if (len != 0 && buf->data == NULL) {
181     OPENSSL_free(buf);
182     return NULL;
183   }
184   buf->len = len;
185   buf->references = 1;
186 
187   *out_data = buf->data;
188   return buf;
189 }
190 
CRYPTO_BUFFER_new_from_CBS(const CBS * cbs,CRYPTO_BUFFER_POOL * pool)191 CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_CBS(const CBS *cbs,
192                                           CRYPTO_BUFFER_POOL *pool) {
193   return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
194 }
195 
CRYPTO_BUFFER_new_from_static_data_unsafe(const uint8_t * data,size_t len,CRYPTO_BUFFER_POOL * pool)196 CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_static_data_unsafe(
197     const uint8_t *data, size_t len, CRYPTO_BUFFER_POOL *pool) {
198   return crypto_buffer_new(data, len, /*data_is_static=*/1, pool);
199 }
200 
CRYPTO_BUFFER_free(CRYPTO_BUFFER * buf)201 void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
202   if (buf == NULL) {
203     return;
204   }
205 
206   CRYPTO_BUFFER_POOL *const pool = buf->pool;
207   if (pool == NULL) {
208     if (CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
209       // If a reference count of zero is observed, there cannot be a reference
210       // from any pool to this buffer and thus we are able to free this
211       // buffer.
212       crypto_buffer_free_object(buf);
213     }
214 
215     return;
216   }
217 
218   CRYPTO_MUTEX_lock_write(&pool->lock);
219   if (!CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
220     CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
221     return;
222   }
223 
224   // We have an exclusive lock on the pool, therefore no concurrent lookups can
225   // find this buffer and increment the reference count. Thus, if the count is
226   // zero there are and can never be any more references and thus we can free
227   // this buffer.
228   //
229   // Note it is possible |buf| is no longer in the pool, if it was replaced by a
230   // static version. If that static version was since removed, it is even
231   // possible for |found| to be NULL.
232   CRYPTO_BUFFER *found = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
233   if (found == buf) {
234     found = lh_CRYPTO_BUFFER_delete(pool->bufs, buf);
235     assert(found == buf);
236     (void)found;
237   }
238 
239   CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
240   crypto_buffer_free_object(buf);
241 }
242 
CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER * buf)243 int CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER *buf) {
244   // This is safe in the case that |buf->pool| is NULL because it's just
245   // standard reference counting in that case.
246   //
247   // This is also safe if |buf->pool| is non-NULL because, if it were racing
248   // with |CRYPTO_BUFFER_free| then the two callers must have independent
249   // references already and so the reference count will never hit zero.
250   CRYPTO_refcount_inc(&buf->references);
251   return 1;
252 }
253 
CRYPTO_BUFFER_data(const CRYPTO_BUFFER * buf)254 const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
255   return buf->data;
256 }
257 
CRYPTO_BUFFER_len(const CRYPTO_BUFFER * buf)258 size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
259   return buf->len;
260 }
261 
CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER * buf,CBS * out)262 void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
263   CBS_init(out, buf->data, buf->len);
264 }
265