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
30static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
31 return (uint32_t)SIPHASH_24(key: buf->pool->hash_key, input: buf->data, input_len: buf->len);
32}
33
34static 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(s1: a->data, s2: b->data, n: a->len);
42}
43
44CRYPTO_BUFFER_POOL* CRYPTO_BUFFER_POOL_new(void) {
45 CRYPTO_BUFFER_POOL *pool = OPENSSL_malloc(size: sizeof(CRYPTO_BUFFER_POOL));
46 if (pool == NULL) {
47 return NULL;
48 }
49
50 OPENSSL_memset(dst: pool, c: 0, n: sizeof(CRYPTO_BUFFER_POOL));
51 pool->bufs = lh_CRYPTO_BUFFER_new(hash: CRYPTO_BUFFER_hash, comp: CRYPTO_BUFFER_cmp);
52 if (pool->bufs == NULL) {
53 OPENSSL_free(ptr: pool);
54 return NULL;
55 }
56
57 CRYPTO_MUTEX_init(lock: &pool->lock);
58 RAND_bytes(buf: (uint8_t *)&pool->hash_key, len: sizeof(pool->hash_key));
59
60 return pool;
61}
62
63void 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(lock: &pool->lock);
70 assert(lh_CRYPTO_BUFFER_num_items(lh: pool->bufs) == 0);
71 CRYPTO_MUTEX_unlock_write(lock: &pool->lock);
72#endif
73
74 lh_CRYPTO_BUFFER_free(lh: pool->bufs);
75 CRYPTO_MUTEX_cleanup(lock: &pool->lock);
76 OPENSSL_free(ptr: pool);
77}
78
79static void crypto_buffer_free_object(CRYPTO_BUFFER *buf) {
80 if (!buf->data_is_static) {
81 OPENSSL_free(ptr: buf->data);
82 }
83 OPENSSL_free(ptr: buf);
84}
85
86static 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(lock: &pool->lock);
96 CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(lh: pool->bufs, data: &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(count: &duplicate->references);
104 }
105 CRYPTO_MUTEX_unlock_read(lock: &pool->lock);
106
107 if (duplicate != NULL) {
108 return duplicate;
109 }
110 }
111
112 CRYPTO_BUFFER *const buf = OPENSSL_malloc(size: sizeof(CRYPTO_BUFFER));
113 if (buf == NULL) {
114 return NULL;
115 }
116 OPENSSL_memset(dst: buf, c: 0, n: 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, size: len);
123 if (len != 0 && buf->data == NULL) {
124 OPENSSL_free(ptr: 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(lock: &pool->lock);
139 CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(lh: pool->bufs, data: 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(lh: pool->bufs, old_data: &old, data: 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(count: &duplicate->references);
154 }
155 CRYPTO_MUTEX_unlock_write(lock: &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
167CRYPTO_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
172CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
173 CRYPTO_BUFFER *const buf = OPENSSL_malloc(size: sizeof(CRYPTO_BUFFER));
174 if (buf == NULL) {
175 return NULL;
176 }
177 OPENSSL_memset(dst: buf, c: 0, n: sizeof(CRYPTO_BUFFER));
178
179 buf->data = OPENSSL_malloc(size: len);
180 if (len != 0 && buf->data == NULL) {
181 OPENSSL_free(ptr: buf);
182 return NULL;
183 }
184 buf->len = len;
185 buf->references = 1;
186
187 *out_data = buf->data;
188 return buf;
189}
190
191CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_CBS(const CBS *cbs,
192 CRYPTO_BUFFER_POOL *pool) {
193 return CRYPTO_BUFFER_new(data: CBS_data(cbs), len: CBS_len(cbs), pool);
194}
195
196CRYPTO_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
201void 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(count: &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(lock: &pool->lock);
219 if (!CRYPTO_refcount_dec_and_test_zero(count: &buf->references)) {
220 CRYPTO_MUTEX_unlock_write(lock: &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(lh: pool->bufs, data: buf);
233 if (found == buf) {
234 found = lh_CRYPTO_BUFFER_delete(lh: pool->bufs, data: buf);
235 assert(found == buf);
236 (void)found;
237 }
238
239 CRYPTO_MUTEX_unlock_write(lock: &buf->pool->lock);
240 crypto_buffer_free_object(buf);
241}
242
243int 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(count: &buf->references);
251 return 1;
252}
253
254const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
255 return buf->data;
256}
257
258size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
259 return buf->len;
260}
261
262void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
263 CBS_init(cbs: out, data: buf->data, len: buf->len);
264}
265

source code of dart_sdk/third_party/boringssl/src/crypto/pool/pool.c