Coverage Report

Created: 2026-03-19 06:22

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() {
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
  Delete(FromOpaque(pool));
89
0
}
90
91
540k
void CryptoBuffer::UpRefInternal() {
92
  // This is safe in the case that |buf->pool| is NULL because it's just
93
  // standard reference counting in that case.
94
  //
95
  // This is also safe if |buf->pool| is non-NULL because, if it were racing
96
  // with |CRYPTO_BUFFER_free| then the two callers must have independent
97
  // references already and so the reference count will never hit zero.
98
540k
  CRYPTO_refcount_inc(&references_);
99
540k
}
100
101
867k
void CryptoBuffer::DecRefInternal() {
102
  // If there is a pool, decrementing the refcount must synchronize with it.
103
867k
  if (pool_handle_ == nullptr) {
104
867k
    if (!CRYPTO_refcount_dec_and_test_zero(&references_)) {
105
540k
      return;
106
540k
    }
107
867k
  } else {
108
0
    MutexWriteLock lock(&pool_handle_->lock_);
109
0
    if (!CRYPTO_refcount_dec_and_test_zero(&references_)) {
110
0
      return;
111
0
    }
112
113
    // We have an exclusive lock on the pool handle, therefore no concurrent
114
    // lookups can find this buffer and increment the reference count. Thus, if
115
    // the count is zero there are and can never be any more references and thus
116
    // we can free this buffer. It is possible the pool was already destroyed,
117
    // but it cannot be destroyed concurrently.
118
    //
119
    // Note it is possible |buf| is no longer in the pool, if it was replaced by
120
    // a static version. If that static version was since removed, it is even
121
    // possible for |found| to be NULL.
122
0
    if (CryptoBufferPool *pool = pool_handle_->pool_; pool != nullptr) {
123
0
      CryptoBuffer *found = lh_CryptoBuffer_retrieve(pool->bufs_, this);
124
0
      if (found == this) {
125
0
        found = lh_CryptoBuffer_delete(pool->bufs_, this);
126
0
        assert(found == this);
127
0
        (void)found;
128
0
      }
129
0
    }
130
0
  }
131
132
326k
  this->~CryptoBuffer();
133
326k
  OPENSSL_free(this);
134
326k
}
135
136
326k
CryptoBuffer::~CryptoBuffer() {
137
326k
  if (!data_is_static_) {
138
326k
    OPENSSL_free(data_);
139
326k
  }
140
326k
}
141
142
static UniquePtr<CryptoBuffer> crypto_buffer_new(Span<const uint8_t> data,
143
326k
                                                 bool data_is_static) {
144
326k
  UniquePtr<CryptoBuffer> buf = MakeUnique<CryptoBuffer>();
145
326k
  if (buf == nullptr) {
146
0
    return nullptr;
147
0
  }
148
149
326k
  if (data_is_static) {
150
0
    buf->data_ = const_cast<uint8_t *>(data.data());
151
0
    buf->data_is_static_ = true;
152
326k
  } else {
153
326k
    buf->data_ =
154
326k
        static_cast<uint8_t *>(OPENSSL_memdup(data.data(), data.size()));
155
326k
    if (!data.empty() && buf->data_ == nullptr) {
156
0
      return nullptr;
157
0
    }
158
326k
  }
159
160
326k
  buf->len_ = data.size();
161
326k
  return buf;
162
326k
}
163
164
static UniquePtr<CryptoBuffer> crypto_buffer_new_with_pool(
165
326k
    Span<const uint8_t> data, bool data_is_static, CryptoBufferPool *pool) {
166
326k
  if (pool == nullptr) {
167
326k
    return crypto_buffer_new(data, data_is_static);
168
326k
  }
169
170
0
  const uint32_t hash = pool->Hash(data);
171
0
  {
172
    // Look for a matching buffer in the pool.
173
0
    MutexReadLock lock(&pool->handle_->lock_);
174
0
    CryptoBuffer *duplicate = pool->FindBufferLocked(hash, data);
175
0
    if (data_is_static && duplicate != nullptr && !duplicate->data_is_static_) {
176
      // If the new |CRYPTO_BUFFER| would have static data, but the duplicate
177
      // does not, we replace the old one with the new static version.
178
0
      duplicate = nullptr;
179
0
    }
180
0
    if (duplicate != nullptr) {
181
0
      return UpRef(duplicate);
182
0
    }
183
0
  }
184
185
0
  UniquePtr<CryptoBuffer> buf = crypto_buffer_new(data, data_is_static);
186
0
  if (buf == nullptr) {
187
0
    return nullptr;
188
0
  }
189
190
0
  MutexWriteLock lock(&pool->handle_->lock_);
191
0
  CryptoBuffer *duplicate = pool->FindBufferLocked(hash, data);
192
0
  if (data_is_static && duplicate != nullptr && !duplicate->data_is_static_) {
193
    // If the new |CRYPTO_BUFFER| would have static data, but the duplicate does
194
    // not, we replace the old one with the new static version.
195
0
    duplicate = nullptr;
196
0
  }
197
0
  if (duplicate != nullptr) {
198
0
    return UpRef(duplicate);
199
0
  }
200
201
  // Insert |buf| into the pool. Note |old| may be non-NULL if a match was found
202
  // but ignored. |pool->bufs_| does not increment refcounts, so there is no
203
  // need to clean up after the replacement.
204
0
  buf->pool_handle_ = UpRef(pool->handle_);
205
0
  CryptoBuffer *old = nullptr;
206
0
  if (!lh_CryptoBuffer_insert(pool->bufs_, &old, buf.get())) {
207
0
    buf->pool_handle_ = nullptr;  // No need to synchronize with the pool.
208
0
    return nullptr;
209
0
  }
210
0
  return buf;
211
0
}
212
213
CRYPTO_BUFFER *CRYPTO_BUFFER_new(const uint8_t *data, size_t len,
214
326k
                                 CRYPTO_BUFFER_POOL *pool) {
215
326k
  return crypto_buffer_new_with_pool(Span(data, len), /*data_is_static=*/false,
216
326k
                                     FromOpaque(pool))
217
326k
      .release();
218
326k
}
219
220
0
CRYPTO_BUFFER *CRYPTO_BUFFER_alloc(uint8_t **out_data, size_t len) {
221
0
  auto buf = MakeUnique<CryptoBuffer>();
222
0
  if (buf == nullptr) {
223
0
    return nullptr;
224
0
  }
225
226
0
  buf->data_ = reinterpret_cast<uint8_t *>(OPENSSL_malloc(len));
227
0
  if (len != 0 && buf->data_ == nullptr) {
228
0
    return nullptr;
229
0
  }
230
0
  buf->len_ = len;
231
232
0
  *out_data = buf->data_;
233
0
  return buf.release();
234
0
}
235
236
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_CBS(const CBS *cbs,
237
282k
                                          CRYPTO_BUFFER_POOL *pool) {
238
282k
  return CRYPTO_BUFFER_new(CBS_data(cbs), CBS_len(cbs), pool);
239
282k
}
240
241
CRYPTO_BUFFER *CRYPTO_BUFFER_new_from_static_data_unsafe(
242
0
    const uint8_t *data, size_t len, CRYPTO_BUFFER_POOL *pool) {
243
0
  return crypto_buffer_new_with_pool(Span(data, len), /*data_is_static=*/true,
244
0
                                     FromOpaque(pool))
245
0
      .release();
246
0
}
247
248
891k
void CRYPTO_BUFFER_free(CRYPTO_BUFFER *buf) {
249
891k
  if (buf != nullptr) {
250
867k
    FromOpaque(buf)->DecRefInternal();
251
867k
  }
252
891k
}
253
254
540k
int CRYPTO_BUFFER_up_ref(CRYPTO_BUFFER *buf) {
255
540k
  FromOpaque(buf)->UpRefInternal();
256
540k
  return 1;
257
540k
}
258
259
100k
const uint8_t *CRYPTO_BUFFER_data(const CRYPTO_BUFFER *buf) {
260
100k
  return FromOpaque(buf)->data_;
261
100k
}
262
263
100k
size_t CRYPTO_BUFFER_len(const CRYPTO_BUFFER *buf) {
264
100k
  return FromOpaque(buf)->len_;
265
100k
}
266
267
243k
void CRYPTO_BUFFER_init_CBS(const CRYPTO_BUFFER *buf, CBS *out) {
268
243k
  *out = FromOpaque(buf)->span();
269
243k
}