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
29DEFINE_LHASH_OF(CRYPTO_BUFFER)
30
31static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
32 return OPENSSL_hash32(buf->data, buf->len);
33}
34
35static 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
42CRYPTO_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
60void 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
76CRYPTO_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
140CRYPTO_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
159CRYPTO_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
163void 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
200int 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
211const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
212 return buf->data;
213}
214
215size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
216 return buf->len;
217}
218
219void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
220 CBS_init(out, buf->data, buf->len);
221}
222