Coverage Report

Created: 2024-11-21 07:03

/src/boringssl/crypto/pool/pool.c
Line
Count
Source (jump to first uncovered line)
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
30
0
static uint32_t CRYPTO_BUFFER_hash(const CRYPTO_BUFFER *buf) {
31
0
  return (uint32_t)SIPHASH_24(buf->pool->hash_key, buf->data, buf->len);
32
0
}
33
34
0
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
0
  assert(a->pool != NULL);
37
0
  assert(a->pool == b->pool);
38
0
  if (a->len != b->len) {
39
0
    return 1;
40
0
  }
41
0
  return OPENSSL_memcmp(a->data, b->data, a->len);
42
0
}
43
44
0
CRYPTO_BUFFER_POOL* CRYPTO_BUFFER_POOL_new(void) {
45
0
  CRYPTO_BUFFER_POOL *pool = OPENSSL_zalloc(sizeof(CRYPTO_BUFFER_POOL));
46
0
  if (pool == NULL) {
47
0
    return NULL;
48
0
  }
49
50
0
  pool->bufs = lh_CRYPTO_BUFFER_new(CRYPTO_BUFFER_hash, CRYPTO_BUFFER_cmp);
51
0
  if (pool->bufs == NULL) {
52
0
    OPENSSL_free(pool);
53
0
    return NULL;
54
0
  }
55
56
0
  CRYPTO_MUTEX_init(&pool->lock);
57
0
  RAND_bytes((uint8_t *)&pool->hash_key, sizeof(pool->hash_key));
58
59
0
  return pool;
60
0
}
61
62
0
void CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL *pool) {
63
0
  if (pool == NULL) {
64
0
    return;
65
0
  }
66
67
0
#if !defined(NDEBUG)
68
0
  CRYPTO_MUTEX_lock_write(&pool->lock);
69
0
  assert(lh_CRYPTO_BUFFER_num_items(pool->bufs) == 0);
70
0
  CRYPTO_MUTEX_unlock_write(&pool->lock);
71
0
#endif
72
73
0
  lh_CRYPTO_BUFFER_free(pool->bufs);
74
0
  CRYPTO_MUTEX_cleanup(&pool->lock);
75
0
  OPENSSL_free(pool);
76
0
}
77
78
0
static void crypto_buffer_free_object(CRYPTO_BUFFER *buf) {
79
0
  if (!buf->data_is_static) {
80
0
    OPENSSL_free(buf->data);
81
0
  }
82
0
  OPENSSL_free(buf);
83
0
}
84
85
static CRYPTO_BUFFER *crypto_buffer_new(const uint8_t *data, size_t len,
86
                                        int data_is_static,
87
0
                                        CRYPTO_BUFFER_POOL *pool) {
88
0
  if (pool != NULL) {
89
0
    CRYPTO_BUFFER tmp;
90
0
    tmp.data = (uint8_t *) data;
91
0
    tmp.len = len;
92
0
    tmp.pool = pool;
93
94
0
    CRYPTO_MUTEX_lock_read(&pool->lock);
95
0
    CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, &tmp);
96
0
    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
0
      duplicate = NULL;
100
0
    }
101
0
    if (duplicate != NULL) {
102
0
      CRYPTO_refcount_inc(&duplicate->references);
103
0
    }
104
0
    CRYPTO_MUTEX_unlock_read(&pool->lock);
105
106
0
    if (duplicate != NULL) {
107
0
      return duplicate;
108
0
    }
109
0
  }
110
111
0
  CRYPTO_BUFFER *const buf = OPENSSL_zalloc(sizeof(CRYPTO_BUFFER));
112
0
  if (buf == NULL) {
113
0
    return NULL;
114
0
  }
115
116
0
  if (data_is_static) {
117
0
    buf->data = (uint8_t *)data;
118
0
    buf->data_is_static = 1;
119
0
  } else {
120
0
    buf->data = OPENSSL_memdup(data, len);
121
0
    if (len != 0 && buf->data == NULL) {
122
0
      OPENSSL_free(buf);
123
0
      return NULL;
124
0
    }
125
0
  }
126
127
0
  buf->len = len;
128
0
  buf->references = 1;
129
130
0
  if (pool == NULL) {
131
0
    return buf;
132
0
  }
133
134
0
  buf->pool = pool;
135
136
0
  CRYPTO_MUTEX_lock_write(&pool->lock);
137
0
  CRYPTO_BUFFER *duplicate = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
138
0
  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
0
    duplicate = NULL;
142
0
  }
143
0
  int inserted = 0;
144
0
  if (duplicate == NULL) {
145
0
    CRYPTO_BUFFER *old = NULL;
146
0
    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
0
  } else {
151
0
    CRYPTO_refcount_inc(&duplicate->references);
152
0
  }
153
0
  CRYPTO_MUTEX_unlock_write(&pool->lock);
154
155
0
  if (!inserted) {
156
    // We raced to insert |buf| into the pool and lost, or else there was an
157
    // error inserting.
158
0
    crypto_buffer_free_object(buf);
159
0
    return duplicate;
160
0
  }
161
162
0
  return buf;
163
0
}
164
165
CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
166
0
                                 CRYPTO_BUFFER_POOL *pool) {
167
0
  return crypto_buffer_new(data, len, /*data_is_static=*/0, pool);
168
0
}
169
170
0
CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
171
0
  CRYPTO_BUFFER *const buf = OPENSSL_zalloc(sizeof(CRYPTO_BUFFER));
172
0
  if (buf == NULL) {
173
0
    return NULL;
174
0
  }
175
176
0
  buf->data = OPENSSL_malloc(len);
177
0
  if (len != 0 && buf->data == NULL) {
178
0
    OPENSSL_free(buf);
179
0
    return NULL;
180
0
  }
181
0
  buf->len = len;
182
0
  buf->references = 1;
183
184
0
  *out_data = buf->data;
185
0
  return buf;
186
0
}
187
188
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_CBS(const CBS *cbs,
189
0
                                          CRYPTO_BUFFER_POOL *pool) {
190
0
  return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
191
0
}
192
193
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_static_data_unsafe(
194
0
    const uint8_t *data, size_t len, CRYPTO_BUFFER_POOL *pool) {
195
0
  return crypto_buffer_new(data, len, /*data_is_static=*/1, pool);
196
0
}
197
198
0
void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
199
0
  if (buf == NULL) {
200
0
    return;
201
0
  }
202
203
0
  CRYPTO_BUFFER_POOL *const pool = buf->pool;
204
0
  if (pool == NULL) {
205
0
    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
0
      crypto_buffer_free_object(buf);
210
0
    }
211
212
0
    return;
213
0
  }
214
215
0
  CRYPTO_MUTEX_lock_write(&pool->lock);
216
0
  if (!CRYPTO_refcount_dec_and_test_zero(&buf->references)) {
217
0
    CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
218
0
    return;
219
0
  }
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
0
  CRYPTO_BUFFER *found = lh_CRYPTO_BUFFER_retrieve(pool->bufs, buf);
230
0
  if (found == buf) {
231
0
    found = lh_CRYPTO_BUFFER_delete(pool->bufs, buf);
232
0
    assert(found == buf);
233
0
    (void)found;
234
0
  }
235
236
0
  CRYPTO_MUTEX_unlock_write(&buf->pool->lock);
237
0
  crypto_buffer_free_object(buf);
238
0
}
239
240
0
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
0
  CRYPTO_refcount_inc(&buf->references);
248
0
  return 1;
249
0
}
250
251
0
const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
252
0
  return buf->data;
253
0
}
254
255
0
size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
256
0
  return buf->len;
257
0
}
258
259
0
void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
260
0
  CBS_init(out, buf->data, buf->len);
261
0
}