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