GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_bio.cc Lines: 240 278 86.3 %
Date: 2022-12-31 04:22:30 Branches: 126 178 70.8 %

Line Branch Exec Source
1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
#include "crypto/crypto_bio.h"
23
#include "base_object-inl.h"
24
#include "memory_tracker-inl.h"
25
#include "util-inl.h"
26
27
#include <openssl/bio.h>
28
29
#include <climits>
30
#include <cstring>
31
32
namespace node {
33
namespace crypto {
34
35
67561
BIOPointer NodeBIO::New(Environment* env) {
36
67561
  BIOPointer bio(BIO_new(GetMethod()));
37

67561
  if (bio && env != nullptr)
38
24810
    NodeBIO::FromBIO(bio.get())->env_ = env;
39
67561
  return bio;
40
}
41
42
43
42751
BIOPointer NodeBIO::NewFixed(const char* data, size_t len, Environment* env) {
44
85502
  BIOPointer bio = New(env);
45
46
85502
  if (!bio ||
47
42751
      len > INT_MAX ||
48


128253
      BIO_write(bio.get(), data, len) != static_cast<int>(len) ||
49
42751
      BIO_set_mem_eof_return(bio.get(), 0) != 1) {
50
    return BIOPointer();
51
  }
52
53
42751
  return bio;
54
}
55
56
57
67561
int NodeBIO::New(BIO* bio) {
58
67561
  BIO_set_data(bio, new NodeBIO());
59
67561
  BIO_set_init(bio, 1);
60
61
67561
  return 1;
62
}
63
64
65
67551
int NodeBIO::Free(BIO* bio) {
66
67551
  if (bio == nullptr)
67
    return 0;
68
69
67551
  if (BIO_get_shutdown(bio)) {
70

67551
    if (BIO_get_init(bio) && BIO_get_data(bio) != nullptr) {
71
67551
      delete FromBIO(bio);
72
67551
      BIO_set_data(bio, nullptr);
73
    }
74
  }
75
76
67551
  return 1;
77
}
78
79
80
37079
int NodeBIO::Read(BIO* bio, char* out, int len) {
81
37079
  BIO_clear_retry_flags(bio);
82
83
37079
  NodeBIO* nbio = FromBIO(bio);
84
37079
  int bytes = nbio->Read(out, len);
85
86
37079
  if (bytes == 0) {
87
7816
    bytes = nbio->eof_return();
88
7816
    if (bytes != 0) {
89
7814
      BIO_set_retry_read(bio);
90
    }
91
  }
92
93
37079
  return bytes;
94
}
95
96
97
20
char* NodeBIO::Peek(size_t* size) {
98
20
  *size = read_head_->write_pos_ - read_head_->read_pos_;
99
20
  return read_head_->data_ + read_head_->read_pos_;
100
}
101
102
103
7548
size_t NodeBIO::PeekMultiple(char** out, size_t* size, size_t* count) {
104
7548
  Buffer* pos = read_head_;
105
7548
  size_t max = *count;
106
7548
  size_t total = 0;
107
108
  size_t i;
109
7681
  for (i = 0; i < max; i++) {
110
7681
    size[i] = pos->write_pos_ - pos->read_pos_;
111
7681
    total += size[i];
112
7681
    out[i] = pos->data_ + pos->read_pos_;
113
114
    /* Don't get past write head */
115
7681
    if (pos == write_head_)
116
7548
      break;
117
    else
118
133
      pos = pos->next_;
119
  }
120
121
7548
  if (i == max)
122
    *count = i;
123
  else
124
7548
    *count = i + 1;
125
126
7548
  return total;
127
}
128
129
130
53194
int NodeBIO::Write(BIO* bio, const char* data, int len) {
131
53194
  BIO_clear_retry_flags(bio);
132
133
53194
  FromBIO(bio)->Write(data, len);
134
135
53194
  return len;
136
}
137
138
139
int NodeBIO::Puts(BIO* bio, const char* str) {
140
  return Write(bio, str, strlen(str));
141
}
142
143
144
988538
int NodeBIO::Gets(BIO* bio, char* out, int size) {
145
988538
  NodeBIO* nbio = FromBIO(bio);
146
147
988538
  if (nbio->Length() == 0)
148
1575
    return 0;
149
150
986963
  int i = nbio->IndexOf('\n', size);
151
152
  // Include '\n', if it's there.  If not, don't read off the end.
153


986963
  if (i < size && i >= 0 && static_cast<size_t>(i) < nbio->Length())
154
946776
    i++;
155
156
  // Shift `i` a bit to nullptr-terminate string later
157
986963
  if (size == i)
158
    i--;
159
160
  // Flush read data
161
986963
  nbio->Read(out, i);
162
163
986963
  out[i] = 0;
164
165
986963
  return i;
166
}
167
168
169
78015
long NodeBIO::Ctrl(BIO* bio, int cmd, long num,  // NOLINT(runtime/int)
170
                   void* ptr) {
171
  NodeBIO* nbio;
172
  long ret;  // NOLINT(runtime/int)
173
174
78015
  nbio = FromBIO(bio);
175
78015
  ret = 1;
176
177



78015
  switch (cmd) {
178
    case BIO_CTRL_RESET:
179
      nbio->Reset();
180
      break;
181
32
    case BIO_CTRL_EOF:
182
32
      ret = nbio->Length() == 0;
183
32
      break;
184
42751
    case BIO_C_SET_BUF_MEM_EOF_RETURN:
185
42751
      nbio->set_eof_return(num);
186
42751
      break;
187
    case BIO_CTRL_INFO:
188
      ret = nbio->Length();
189
      if (ptr != nullptr)
190
        *reinterpret_cast<void**>(ptr) = nullptr;
191
      break;
192
    case BIO_C_SET_BUF_MEM:
193
      CHECK(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
194
      break;
195
    case BIO_C_GET_BUF_MEM_PTR:
196
      CHECK(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
197
      ret = 0;
198
      break;
199
    case BIO_CTRL_GET_CLOSE:
200
      ret = BIO_get_shutdown(bio);
201
      break;
202
    case BIO_CTRL_SET_CLOSE:
203
      BIO_set_shutdown(bio, num);
204
      break;
205
    case BIO_CTRL_WPENDING:
206
      ret = 0;
207
      break;
208
17036
    case BIO_CTRL_PENDING:
209
17036
      ret = nbio->Length();
210
17036
      break;
211
6121
    case BIO_CTRL_DUP:
212
    case BIO_CTRL_FLUSH:
213
6121
      ret = 1;
214
6121
      break;
215
12075
    case BIO_CTRL_PUSH:
216
    case BIO_CTRL_POP:
217
    default:
218
12075
      ret = 0;
219
12075
      break;
220
  }
221
78015
  return ret;
222
}
223
224
225
67561
const BIO_METHOD* NodeBIO::GetMethod() {
226
  // Static initialization ensures that this is safe to use concurrently.
227
288
  static const BIO_METHOD* method = [&]() {
228
288
    BIO_METHOD* method = BIO_meth_new(BIO_TYPE_MEM, "node.js SSL buffer");
229
288
    BIO_meth_set_write(method, Write);
230
288
    BIO_meth_set_read(method, Read);
231
288
    BIO_meth_set_puts(method, Puts);
232
288
    BIO_meth_set_gets(method, Gets);
233
288
    BIO_meth_set_ctrl(method, Ctrl);
234
288
    BIO_meth_set_create(method, New);
235
288
    BIO_meth_set_destroy(method, Free);
236
288
    return method;
237

67561
  }();
238
239
67561
  return method;
240
}
241
242
243
1080557
void NodeBIO::TryMoveReadHead() {
244
  // `read_pos_` and `write_pos_` means the position of the reader and writer
245
  // inside the buffer, respectively. When they're equal - its safe to reset
246
  // them, because both reader and writer will continue doing their stuff
247
  // from new (zero) positions.
248
1080557
  while (read_head_->read_pos_ != 0 &&
249
1023718
         read_head_->read_pos_ == read_head_->write_pos_) {
250
    // Reset positions
251
55995
    read_head_->read_pos_ = 0;
252
55995
    read_head_->write_pos_ = 0;
253
254
    // Move read_head_ forward, just in case if there're still some data to
255
    // read in the next buffer.
256
55995
    if (read_head_ != write_head_)
257
320
      read_head_ = read_head_->next_;
258
  }
259
1024562
}
260
261
262
1031409
size_t NodeBIO::Read(char* out, size_t size) {
263
1031409
  size_t bytes_read = 0;
264
1031409
  size_t expected = Length() > size ? size : Length();
265
1031409
  size_t offset = 0;
266
1031409
  size_t left = size;
267
268
2055127
  while (bytes_read < expected) {
269
1023718
    CHECK_LE(read_head_->read_pos_, read_head_->write_pos_);
270
1023718
    size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
271
1023718
    if (avail > left)
272
967723
      avail = left;
273
274
    // Copy data
275
1023718
    if (out != nullptr)
276
1016226
      memcpy(out + offset, read_head_->data_ + read_head_->read_pos_, avail);
277
1023718
    read_head_->read_pos_ += avail;
278
279
    // Move pointers
280
1023718
    bytes_read += avail;
281
1023718
    offset += avail;
282
1023718
    left -= avail;
283
284
1023718
    TryMoveReadHead();
285
  }
286
1031409
  CHECK_EQ(expected, bytes_read);
287
1031409
  length_ -= bytes_read;
288
289
  // Free all empty buffers, but write_head's child
290
1031409
  FreeEmpty();
291
292
1031409
  return bytes_read;
293
}
294
295
296
1031409
void NodeBIO::FreeEmpty() {
297
1031409
  if (write_head_ == nullptr)
298
1031408
    return;
299
1029642
  Buffer* child = write_head_->next_;
300

1029642
  if (child == write_head_ || child == read_head_)
301
1024473
    return;
302
5169
  Buffer* cur = child->next_;
303

5169
  if (cur == write_head_ || cur == read_head_)
304
5168
    return;
305
306
1
  Buffer* prev = child;
307
2
  while (cur != read_head_) {
308
1
    CHECK_NE(cur, write_head_);
309
1
    CHECK_EQ(cur->write_pos_, cur->read_pos_);
310
311
1
    Buffer* next = cur->next_;
312
1
    delete cur;
313
1
    cur = next;
314
  }
315
1
  prev->next_ = cur;
316
}
317
318
319
986963
size_t NodeBIO::IndexOf(char delim, size_t limit) {
320
986963
  size_t bytes_read = 0;
321
986963
  size_t max = Length() > limit ? limit : Length();
322
986963
  size_t left = limit;
323
986963
  Buffer* current = read_head_;
324
325
1027150
  while (bytes_read < max) {
326
986963
    CHECK_LE(current->read_pos_, current->write_pos_);
327
986963
    size_t avail = current->write_pos_ - current->read_pos_;
328
986963
    if (avail > left)
329
810515
      avail = left;
330
331
    // Walk through data
332
986963
    char* tmp = current->data_ + current->read_pos_;
333
986963
    size_t off = 0;
334

65990222
    while (off < avail && *tmp != delim) {
335
65003259
      off++;
336
65003259
      tmp++;
337
    }
338
339
    // Move pointers
340
986963
    bytes_read += off;
341
986963
    left -= off;
342
343
    // Found `delim`
344
986963
    if (off != avail) {
345
946776
      return bytes_read;
346
    }
347
348
    // Move to next buffer
349
40187
    if (current->read_pos_ + avail == current->len_) {
350
31285
      current = current->next_;
351
    }
352
  }
353
40187
  CHECK_EQ(max, bytes_read);
354
355
40187
  return max;
356
}
357
358
359
53194
void NodeBIO::Write(const char* data, size_t size) {
360
53194
  size_t offset = 0;
361
53194
  size_t left = size;
362
363
  // Allocate initial buffer if the ring is empty
364
53194
  TryAllocateForWrite(left);
365
366
106523
  while (left > 0) {
367
53329
    size_t to_write = left;
368
53329
    CHECK_LE(write_head_->write_pos_, write_head_->len_);
369
53329
    size_t avail = write_head_->len_ - write_head_->write_pos_;
370
371
53329
    if (to_write > avail)
372
135
      to_write = avail;
373
374
    // Copy data
375
53329
    memcpy(write_head_->data_ + write_head_->write_pos_,
376
53329
           data + offset,
377
           to_write);
378
379
    // Move pointers
380
53329
    left -= to_write;
381
53329
    offset += to_write;
382
53329
    length_ += to_write;
383
53329
    write_head_->write_pos_ += to_write;
384
53329
    CHECK_LE(write_head_->write_pos_, write_head_->len_);
385
386
    // Go to next buffer if there still are some bytes to write
387
53329
    if (left != 0) {
388
135
      CHECK_EQ(write_head_->write_pos_, write_head_->len_);
389
135
      TryAllocateForWrite(left);
390
135
      write_head_ = write_head_->next_;
391
392
      // Additionally, since we're moved to the next buffer, read head
393
      // may be moved as well.
394
135
      TryMoveReadHead();
395
    }
396
  }
397
53194
  CHECK_EQ(left, 0);
398
53194
}
399
400
401
6714
char* NodeBIO::PeekWritable(size_t* size) {
402
6714
  TryAllocateForWrite(*size);
403
404
6714
  size_t available = write_head_->len_ - write_head_->write_pos_;
405

6714
  if (*size == 0 || available <= *size)
406
6671
    *size = available;
407
408
6714
  return write_head_->data_ + write_head_->write_pos_;
409
}
410
411
412
6405
void NodeBIO::Commit(size_t size) {
413
6405
  write_head_->write_pos_ += size;
414
6405
  length_ += size;
415
6405
  CHECK_LE(write_head_->write_pos_, write_head_->len_);
416
417
  // Allocate new buffer if write head is full,
418
  // and there're no other place to go
419
6405
  TryAllocateForWrite(0);
420
6405
  if (write_head_->write_pos_ == write_head_->len_) {
421
709
    write_head_ = write_head_->next_;
422
423
    // Additionally, since we're moved to the next buffer, read head
424
    // may be moved as well.
425
709
    TryMoveReadHead();
426
  }
427
6405
}
428
429
430
66448
void NodeBIO::TryAllocateForWrite(size_t hint) {
431
66448
  Buffer* w = write_head_;
432
66448
  Buffer* r = read_head_;
433
  // If write head is full, next buffer is either read head or not empty.
434
66448
  if (w == nullptr ||
435
19012
      (w->write_pos_ == w->len_ &&
436

846
       (w->next_ == r || w->next_->write_pos_ != 0))) {
437
48099
    size_t len = w == nullptr ? initial_ :
438
                             kThroughputBufferLength;
439
48099
    if (len < hint)
440
36823
      len = hint;
441
442
    // If there is a one time allocation size hint, use it.
443
48099
    if (allocate_hint_ > len) {
444
23
      len = allocate_hint_;
445
23
      allocate_hint_ = 0;
446
    }
447
448
48099
    Buffer* next = new Buffer(env_, len);
449
450
48099
    if (w == nullptr) {
451
47436
      next->next_ = next;
452
47436
      write_head_ = next;
453
47436
      read_head_ = next;
454
    } else {
455
663
      next->next_ = w->next_;
456
663
      w->next_ = next;
457
    }
458
  }
459
66448
}
460
461
462
void NodeBIO::Reset() {
463
  if (read_head_ == nullptr)
464
    return;
465
466
  while (read_head_->read_pos_ != read_head_->write_pos_) {
467
    CHECK(read_head_->write_pos_ > read_head_->read_pos_);
468
469
    length_ -= read_head_->write_pos_ - read_head_->read_pos_;
470
    read_head_->write_pos_ = 0;
471
    read_head_->read_pos_ = 0;
472
473
    read_head_ = read_head_->next_;
474
  }
475
  write_head_ = read_head_;
476
  CHECK_EQ(length_, 0);
477
}
478
479
480
270204
NodeBIO::~NodeBIO() {
481
135102
  if (read_head_ == nullptr)
482
40250
    return;
483
484
94852
  Buffer* current = read_head_;
485
1318
  do {
486
96170
    Buffer* next = current->next_;
487
96170
    delete current;
488
96170
    current = next;
489
96170
  } while (current != read_head_);
490
491
94852
  read_head_ = nullptr;
492
94852
  write_head_ = nullptr;
493
270204
}
494
495
496
1291592
NodeBIO* NodeBIO::FromBIO(BIO* bio) {
497
1291592
  CHECK_NOT_NULL(BIO_get_data(bio));
498
1291592
  return static_cast<NodeBIO*>(BIO_get_data(bio));
499
}
500
501
502
}  // namespace crypto
503
}  // namespace node