Coverage Report

Created: 2026-10-19 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/pool/pool.cc
Line
Count
Source
1
// Copyright 2016 The BoringSSL Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
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
25
#include "../internal.h"
26
#include "../mem_internal.h"
27
#include "internal.h"
28
29
30
using namespace bssl;
31
32
0
static uint32_t CRYPTO_BUFFER_hash(const CryptoBuffer *buf) {
33
  // This function must be called while there is a read or write lock on the
34
  // pool, so it is safe to read `pool_`.
35
0
  return buf->pool_handle_->pool_->Hash(buf->span());
36
0
}
37
38
0
static int CRYPTO_BUFFER_cmp(const CryptoBuffer *a, const CryptoBuffer *b) {
39
  // Only `CRYPTO_BUFFER`s from the same pool have compatible hashes.
40
0
  assert(a->pool_handle_ != nullptr);
41
0
  assert(a->pool_handle_ == b->pool_handle_);
42
0
  return a->span() == b->span() ? 0 : 1;
43
0
}
44
45
0
CryptoBufferPool::CryptoBufferPool() : RefCounted(CheckSubClass()) {
46
0
  RAND_bytes(reinterpret_cast<uint8_t *>(&hash_key_), sizeof(hash_key_));
47
0
}
48
49
0
CryptoBufferPool::~CryptoBufferPool() {
50
0
  if (handle_) {
51
0
    MutexWriteLock lock(&handle_->lock_);
52
0
    handle_->pool_ = nullptr;
53
0
  }
54
0
  lh_CryptoBuffer_free(bufs_);
55
0
}
56
57
0
uint32_t CryptoBufferPool::Hash(Span<const uint8_t> data) const {
58
0
  return static_cast<uint32_t>(SIPHASH_24(hash_key_, data.data(), data.size()));
59
0
}
60
61
CryptoBuffer *CryptoBufferPool::FindBufferLocked(uint32_t hash,
62
0
                                                 Span<const uint8_t> data) {
63
0
  return lh_CryptoBuffer_retrieve_key(
64
0
      bufs_, &data, hash,
65
0
      [](const void *key_v, const CryptoBuffer *buf) -> int {
66
0
        Span<const uint8_t> key =
67
0
            *static_cast<const Span<const uint8_t> *>(key_v);
68
0
        return key == buf->span() ? 0 : 1;
69
0
      });
70
0
}
71
72
0
CRYPTO_BUFFER_POOL *CRYPTO_BUFFER_POOL_new() {
73
0
  auto pool = MakeUnique<CryptoBufferPool>();
74
0
  if (pool == nullptr) {
75
0
    return nullptr;
76
0
  }
77
78
0
  pool->bufs_ = lh_CryptoBuffer_new(CRYPTO_BUFFER_hash, CRYPTO_BUFFER_cmp);
79
0
  pool->handle_ = MakeUnique<CryptoBufferPoolHandle>(pool.get());
80
0
  if (pool->bufs_ == nullptr || pool->handle_ == nullptr) {
81
0
    return nullptr;
82
0
  }
83
84
0
  return pool.release();
85
0
}
86
87
0
void CRYPTO_BUFFER_POOL_free(CRYPTO_BUFFER_POOL *pool) {
88
0
  if (pool != nullptr) {
89
0
    FromOpaque(pool)->DecRefInternal();
90
0
  }
91
0
}
92
93
0
int CRYPTO_BUFFER_POOL_up_ref(CRYPTO_BUFFER_POOL *pool) {
94
0
  FromOpaque(pool)->UpRefInternal();
95
0
  return 1;
96
0
}
97
98
0
CRYPTO_BUFFER_POOL *CRYPTO_BUFFER_POOL_dup_ref(const CRYPTO_BUFFER_POOL *pool) {
99
0
  if (pool == nullptr) {
100
0
    return nullptr;
101
0
  }
102
0
  return FromOpaque(pool)->DupRefInternal();
103
0
}
104
105
440k
void CryptoBuffer::UpRefInternal() const {
106
  // This is safe in the case that `buf->pool` is NULL because it's just
107
  // standard reference counting in that case.
108
  //
109
  // This is also safe if `buf->pool` is non-NULL because, if it were racing
110
  // with `CRYPTO_BUFFER_free` then the two callers must have independent
111
  // references already and so the reference count will never hit zero.
112
440k
  CRYPTO_refcount_inc(&references_);
113
440k
}
114
115
792k
void CryptoBuffer::DecRefInternal() {
116
  // If there is a pool, decrementing the refcount must synchronize with it.
117
792k
  if (pool_handle_ == nullptr) {
118
792k
    if (!CRYPTO_refcount_dec_and_test_zero(&references_)) {
119
440k
      return;
120
440k
    }
121
792k
  } else {
122
0
    MutexWriteLock lock(&pool_handle_->lock_);
123
0
    if (!CRYPTO_refcount_dec_and_test_zero(&references_)) {
124
0
      return;
125
0
    }
126
127
    // We have an exclusive lock on the pool handle, therefore no concurrent
128
    // lookups can find this buffer and increment the reference count. Thus, if
129
    // the count is zero there are and can never be any more references and thus
130
    // we can free this buffer. It is possible the pool was already destroyed,
131
    // but it cannot be destroyed concurrently.
132
    //
133
    // Note it is possible `buf` is no longer in the pool, if it was replaced by
134
    // a static version. If that static version was since removed, it is even
135
    // possible for `found` to be NULL.
136
0
    if (CryptoBufferPool *pool = pool_handle_->pool_; pool != nullptr) {
137
0
      CryptoBuffer *found = lh_CryptoBuffer_retrieve(pool->bufs_, this);
138
0
      if (found == this) {
139
0
        found = lh_CryptoBuffer_delete(pool->bufs_, this);
140
0
        assert(found == this);
141
0
        (void)found;
142
0
      }
143
0
    }
144
0
  }
145
146
351k
  this->~CryptoBuffer();
147
351k
  OPENSSL_free(this);
148
351k
}
149
150
351k
CryptoBuffer::~CryptoBuffer() {
151
351k
  if (!data_is_static_) {
152
351k
    OPENSSL_free(data_);
153
351k
  }
154
351k
}
155
156
static UniquePtr<CryptoBuffer> crypto_buffer_new(Span<const uint8_t> data,
157
351k
                                                 bool data_is_static) {
158
351k
  UniquePtr<CryptoBuffer> buf = MakeUnique<CryptoBuffer>();
159
351k
  if (buf == nullptr) {
160
0
    return nullptr;
161
0
  }
162
163
351k
  if (data_is_static) {
164
0
    buf->data_ = const_cast<uint8_t *>(data.data());
165
0
    buf->data_is_static_ = true;
166
351k
  } else {
167
351k
    buf->data_ =
168
351k
        static_cast<uint8_t *>(OPENSSL_memdup(data.data(), data.size()));
169
351k
    if (!data.empty() && buf->data_ == nullptr) {
170
0
      return nullptr;
171
0
    }
172
351k
  }
173
174
351k
  buf->len_ = data.size();
175
351k
  return buf;
176
351k
}
177
178
static UniquePtr<CryptoBuffer> crypto_buffer_new_with_pool(
179
351k
    Span<const uint8_t> data, bool data_is_static, CryptoBufferPool *pool) {
180
351k
  if (pool == nullptr) {
181
351k
    return crypto_buffer_new(data, data_is_static);
182
351k
  }
183
184
0
  const uint32_t hash = pool->Hash(data);
185
0
  {
186
    // Look for a matching buffer in the pool.
187
0
    MutexReadLock lock(&pool->handle_->lock_);
188
0
    CryptoBuffer *duplicate = pool->FindBufferLocked(hash, data);
189
0
    if (data_is_static && duplicate != nullptr && !duplicate->data_is_static_) {
190
      // If the new `CRYPTO_BUFFER` would have static data, but the duplicate
191
      // does not, we replace the old one with the new static version.
192
0
      duplicate = nullptr;
193
0
    }
194
0
    if (duplicate != nullptr) {
195
0
      return UpRef(duplicate);
196
0
    }
197
0
  }
198
199
0
  UniquePtr<CryptoBuffer> buf = crypto_buffer_new(data, data_is_static);
200
0
  if (buf == nullptr) {
201
0
    return nullptr;
202
0
  }
203
204
0
  MutexWriteLock lock(&pool->handle_->lock_);
205
0
  CryptoBuffer *duplicate = pool->FindBufferLocked(hash, data);
206
0
  if (data_is_static && duplicate != nullptr && !duplicate->data_is_static_) {
207
    // If the new `CRYPTO_BUFFER` would have static data, but the duplicate does
208
    // not, we replace the old one with the new static version.
209
0
    duplicate = nullptr;
210
0
  }
211
0
  if (duplicate != nullptr) {
212
0
    return UpRef(duplicate);
213
0
  }
214
215
  // Insert `buf` into the pool. Note `old` may be non-NULL if a match was found
216
  // but ignored. `pool->bufs_` does not increment refcounts, so there is no
217
  // need to clean up after the replacement.
218
0
  buf->pool_handle_ = UpRef(pool->handle_);
219
0
  CryptoBuffer *old = nullptr;
220
0
  if (!lh_CryptoBuffer_insert(pool->bufs_, &old, buf.get())) {
221
0
    buf->pool_handle_ = nullptr;  // No need to synchronize with the pool.
222
0
    return nullptr;
223
0
  }
224
0
  return buf;
225
0
}
226
227
CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
228
351k
                                 CRYPTO_BUFFER_POOL *pool) {
229
351k
  return crypto_buffer_new_with_pool(Span(data, len), /*data_is_static=*/false,
230
351k
                                     FromOpaque(pool))
231
351k
      .release();
232
351k
}
233
234
0
CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
235
0
  auto buf = MakeUnique<CryptoBuffer>();
236
0
  if (buf == nullptr) {
237
0
    return nullptr;
238
0
  }
239
240
0
  buf->data_ = reinterpret_cast<uint8_t *>(OPENSSL_malloc(len));
241
0
  if (len != 0 && buf->data_ == nullptr) {
242
0
    return nullptr;
243
0
  }
244
0
  buf->len_ = len;
245
246
0
  *out_data = buf->data_;
247
0
  return buf.release();
248
0
}
249
250
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_CBS(const CBS *cbs,
251
307k
                                          CRYPTO_BUFFER_POOL *pool) {
252
307k
  return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
253
307k
}
254
255
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_static_data_unsafe(
256
0
    const uint8_t *data, size_t len, CRYPTO_BUFFER_POOL *pool) {
257
0
  return crypto_buffer_new_with_pool(Span(data, len), /*data_is_static=*/true,
258
0
                                     FromOpaque(pool))
259
0
      .release();
260
0
}
261
262
792k
void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
263
792k
  if (buf != nullptr) {
264
792k
    FromOpaque(buf)->DecRefInternal();
265
792k
  }
266
792k
}
267
268
0
int CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER *buf) {
269
0
  FromOpaque(buf)->UpRefInternal();
270
0
  return 1;
271
0
}
272
273
620k
CRYPTO_BUFFER *CRYPTO_BUFFER_dup_ref(const CRYPTO_BUFFER *buf) {
274
620k
  if (buf == nullptr) {
275
179k
    return nullptr;
276
179k
  }
277
440k
  auto *buf_ = const_cast<CRYPTO_BUFFER *>(buf);
278
440k
  FromOpaque(buf_)->UpRefInternal();
279
440k
  return buf_;
280
620k
}
281
282
109k
const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
283
109k
  return FromOpaque(buf)->data_;
284
109k
}
285
286
109k
size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
287
109k
  return FromOpaque(buf)->len_;
288
109k
}
289
290
296k
void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
291
296k
  *out = FromOpaque(buf)->span();
292
296k
}