test_buffers.c 24.9 KB
Newer Older
1
2
/* Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
Nick Mathewson's avatar
Nick Mathewson committed
3
 * Copyright (c) 2007-2019, The Tor Project, Inc. */
4
5
6
/* See LICENSE for licensing information */

#define BUFFERS_PRIVATE
7
#define PROTO_HTTP_PRIVATE
8
#include "core/or/or.h"
9
#include "lib/container/buffers.h"
10
#include "lib/tls/buffers_tls.h"
11
12
#include "lib/tls/tortls.h"
#include "lib/compress/compress.h"
13
#include "lib/crypt_ops/crypto_rand.h"
14
15
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
Nick Mathewson's avatar
Nick Mathewson committed
16
#include "test/test.h"
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

/** Run unit tests for buffers.c */
static void
test_buffers_basic(void *arg)
{
  char str[256];
  char str2[256];

  buf_t *buf = NULL, *buf2 = NULL;
  const char *cp;

  int j;
  size_t r;
  (void) arg;

  /****
   * buf_new
   ****/
  if (!(buf = buf_new()))
36
    TT_DIE(("Assertion failed."));
37
38

  //test_eq(buf_capacity(buf), 4096);
39
  tt_int_op(buf_datalen(buf),OP_EQ, 0);
40
41
42
43
44
45
46

  /****
   * General pointer frobbing
   */
  for (j=0;j<256;++j) {
    str[j] = (char)j;
  }
47
48
  buf_add(buf, str, 256);
  buf_add(buf, str, 256);
49
  tt_int_op(buf_datalen(buf),OP_EQ, 512);
50
  buf_get_bytes(buf, str2, 200);
51
52
  tt_mem_op(str,OP_EQ, str2, 200);
  tt_int_op(buf_datalen(buf),OP_EQ, 312);
53
54
  memset(str2, 0, sizeof(str2));

55
  buf_get_bytes(buf, str2, 256);
56
57
58
  tt_mem_op(str+200,OP_EQ, str2, 56);
  tt_mem_op(str,OP_EQ, str2+56, 200);
  tt_int_op(buf_datalen(buf),OP_EQ, 56);
59
60
61
62
  memset(str2, 0, sizeof(str2));
  /* Okay, now we should be 512 bytes into the 4096-byte buffer.  If we add
   * another 3584 bytes, we hit the end. */
  for (j=0;j<15;++j) {
63
    buf_add(buf, str, 256);
64
  }
65
  buf_assert_ok(buf);
66
  tt_int_op(buf_datalen(buf),OP_EQ, 3896);
67
  buf_get_bytes(buf, str2, 56);
68
69
  tt_int_op(buf_datalen(buf),OP_EQ, 3840);
  tt_mem_op(str+200,OP_EQ, str2, 56);
70
71
  for (j=0;j<15;++j) {
    memset(str2, 0, sizeof(str2));
72
    buf_get_bytes(buf, str2, 256);
73
    tt_mem_op(str,OP_EQ, str2, 256);
74
  }
75
  tt_int_op(buf_datalen(buf),OP_EQ, 0);
76
77
78
79
80
81
  buf_free(buf);
  buf = NULL;

  /* Okay, now make sure growing can work. */
  buf = buf_new_with_capacity(16);
  //test_eq(buf_capacity(buf), 16);
82
  buf_add(buf, str+1, 255);
83
  //test_eq(buf_capacity(buf), 256);
84
  buf_get_bytes(buf, str2, 254);
85
  tt_mem_op(str+1,OP_EQ, str2, 254);
86
  //test_eq(buf_capacity(buf), 256);
87
  buf_assert_ok(buf);
88
  buf_add(buf, str, 32);
89
  //test_eq(buf_capacity(buf), 256);
90
  buf_assert_ok(buf);
91
  buf_add(buf, str, 256);
92
  buf_assert_ok(buf);
93
  //test_eq(buf_capacity(buf), 512);
94
  tt_int_op(buf_datalen(buf),OP_EQ, 33+256);
95
  buf_get_bytes(buf, str2, 33);
96
  tt_int_op(*str2,OP_EQ, str[255]);
97

98
  tt_mem_op(str2+1,OP_EQ, str, 32);
99
  //test_eq(buf_capacity(buf), 512);
100
  tt_int_op(buf_datalen(buf),OP_EQ, 256);
101
  buf_get_bytes(buf, str2, 256);
102
  tt_mem_op(str,OP_EQ, str2, 256);
103
104
105
106
107

  /* now try shrinking: case 1. */
  buf_free(buf);
  buf = buf_new_with_capacity(33668);
  for (j=0;j<67;++j) {
108
    buf_add(buf, str,255);
109
110
  }
  //test_eq(buf_capacity(buf), 33668);
111
  tt_int_op(buf_datalen(buf),OP_EQ, 17085);
112
  for (j=0; j < 40; ++j) {
113
    buf_get_bytes(buf, str2, 255);
114
    tt_mem_op(str2,OP_EQ, str, 255);
115
116
117
118
119
120
  }

  /* now try shrinking: case 2. */
  buf_free(buf);
  buf = buf_new_with_capacity(33668);
  for (j=0;j<67;++j) {
121
    buf_add(buf, str, 255);
122
123
  }
  for (j=0; j < 20; ++j) {
124
    buf_get_bytes(buf, str2, 255);
125
    tt_mem_op(str2,OP_EQ, str, 255);
126
127
  }
  for (j=0;j<80;++j) {
128
    buf_add(buf, str, 255);
129
130
131
  }
  //test_eq(buf_capacity(buf),33668);
  for (j=0; j < 120; ++j) {
132
    buf_get_bytes(buf, str2, 255);
133
    tt_mem_op(str2,OP_EQ, str, 255);
134
135
136
137
138
139
140
  }

  /* Move from buf to buf. */
  buf_free(buf);
  buf = buf_new_with_capacity(4096);
  buf2 = buf_new_with_capacity(4096);
  for (j=0;j<100;++j)
141
    buf_add(buf, str, 255);
142
  tt_int_op(buf_datalen(buf),OP_EQ, 25500);
143
144
  for (j=0;j<100;++j) {
    r = 10;
145
    buf_move_to_buf(buf2, buf, &r);
146
    tt_int_op(r,OP_EQ, 0);
147
  }
148
149
  tt_int_op(buf_datalen(buf),OP_EQ, 24500);
  tt_int_op(buf_datalen(buf2),OP_EQ, 1000);
150
  for (j=0;j<3;++j) {
151
    buf_get_bytes(buf2, str2, 255);
152
    tt_mem_op(str2,OP_EQ, str, 255);
153
154
  }
  r = 8192; /*big move*/
155
  buf_move_to_buf(buf2, buf, &r);
156
  tt_int_op(r,OP_EQ, 0);
157
  r = 30000; /* incomplete move */
158
  buf_move_to_buf(buf2, buf, &r);
159
  tt_int_op(r,OP_EQ, 13692);
160
  for (j=0;j<97;++j) {
161
    buf_get_bytes(buf2, str2, 255);
162
    tt_mem_op(str2,OP_EQ, str, 255);
163
164
165
166
167
168
169
170
  }
  buf_free(buf);
  buf_free(buf2);
  buf = buf2 = NULL;

  buf = buf_new_with_capacity(5);
  cp = "Testing. This is a moderately long Testing string.";
  for (j = 0; cp[j]; j++)
171
    buf_add(buf, cp+j, 1);
172
173
174
175
176
177
178
179
180
181
  tt_int_op(0,OP_EQ, buf_find_string_offset(buf, "Testing", 7));
  tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "esting", 6));
  tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "est", 3));
  tt_int_op(39,OP_EQ, buf_find_string_offset(buf, "ing str", 7));
  tt_int_op(35,OP_EQ, buf_find_string_offset(buf, "Testing str", 11));
  tt_int_op(32,OP_EQ, buf_find_string_offset(buf, "ng ", 3));
  tt_int_op(43,OP_EQ, buf_find_string_offset(buf, "string.", 7));
  tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "shrdlu", 6));
  tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "Testing thing", 13));
  tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "ngx", 3));
182
183
184
185
186
  buf_free(buf);
  buf = NULL;

  /* Try adding a string too long for any freelist. */
  {
187
    char *mem = tor_malloc_zero(65536);
188
    buf = buf_new();
189
    buf_add(buf, mem, 65536);
190
    tor_free(mem);
191

192
    tt_int_op(buf_datalen(buf), OP_EQ, 65536);
193
194
195
196
197
198
199
200
201
202
    buf_free(buf);
    buf = NULL;
  }

 done:
  if (buf)
    buf_free(buf);
  if (buf2)
    buf_free(buf2);
}
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217

static void
test_buffer_pullup(void *arg)
{
  buf_t *buf;
  char *stuff, *tmp;
  const char *cp;
  size_t sz;
  (void)arg;
  stuff = tor_malloc(16384);
  tmp = tor_malloc(16384);

  buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */

  tt_assert(buf);
218
  tt_int_op(buf_get_default_chunk_size(buf), OP_EQ, 4096);
219

220
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
221
222
223

  /* There are a bunch of cases for pullup.  One is the trivial case. Let's
     mess around with an empty buffer. */
224
  buf_pullup(buf, 16, &cp, &sz);
225
226
  tt_ptr_op(cp, OP_EQ, NULL);
  tt_uint_op(sz, OP_EQ, 0);
227
228

  /* Let's make sure nothing got allocated */
229
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
230
231
232
233
234

  /* Case 1: everything puts into the first chunk with some moving. */

  /* Let's add some data. */
  crypto_rand(stuff, 16384);
235
236
  buf_add(buf, stuff, 3000);
  buf_add(buf, stuff+3000, 3000);
237
  buf_pullup(buf, 0, &cp, &sz);
238
239
  tt_ptr_op(cp, OP_NE, NULL);
  tt_int_op(sz, OP_LE, 4096);
240
241
242

  /* Make room for 3000 bytes in the first chunk, so that the pullup-move code
   * can get tested. */
243
  tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 3000);
244
  tt_mem_op(tmp,OP_EQ, stuff, 3000);
245
  buf_pullup(buf, 2048, &cp, &sz);
246
  buf_assert_ok(buf);
247
248
249
250
  tt_ptr_op(cp, OP_NE, NULL);
  tt_int_op(sz, OP_GE, 2048);
  tt_mem_op(cp,OP_EQ, stuff+3000, 2048);
  tt_int_op(3000, OP_EQ, buf_datalen(buf));
251
  tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 0);
252
  tt_mem_op(tmp,OP_EQ, stuff+3000, 2048);
253
254
255
256
257

  buf_free(buf);

  /* Now try the large-chunk case. */
  buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
258
259
260
261
  buf_add(buf, stuff, 4000);
  buf_add(buf, stuff+4000, 4000);
  buf_add(buf, stuff+8000, 4000);
  buf_add(buf, stuff+12000, 4000);
262
  tt_int_op(buf_datalen(buf), OP_EQ, 16000);
263
  buf_pullup(buf, 0, &cp, &sz);
264
265
  tt_ptr_op(cp, OP_NE, NULL);
  tt_int_op(sz, OP_LE, 4096);
266

267
  buf_pullup(buf, 12500, &cp, &sz);
268
  buf_assert_ok(buf);
269
270
271
272
  tt_ptr_op(cp, OP_NE, NULL);
  tt_int_op(sz, OP_GE, 12500);
  tt_mem_op(cp,OP_EQ, stuff, 12500);
  tt_int_op(buf_datalen(buf), OP_EQ, 16000);
273

274
  buf_get_bytes(buf, tmp, 12400);
275
276
  tt_mem_op(tmp,OP_EQ, stuff, 12400);
  tt_int_op(buf_datalen(buf), OP_EQ, 3600);
277
  buf_get_bytes(buf, tmp, 3500);
278
  tt_mem_op(tmp,OP_EQ, stuff+12400, 3500);
279
  buf_get_bytes(buf, tmp, 100);
280
  tt_mem_op(tmp,OP_EQ, stuff+15900, 10);
281
282
283
284
285

  buf_free(buf);

  /* Make sure that the pull-up-whole-buffer case works */
  buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
286
287
288
  buf_add(buf, stuff, 4000);
  buf_add(buf, stuff+4000, 4000);
  buf_get_bytes(buf, tmp, 100); /* dump 100 bytes from first chunk */
289
  buf_pullup(buf, 16000, &cp, &sz);
290
  buf_assert_ok(buf);
291
292
293
  tt_ptr_op(cp, OP_NE, NULL);
  tt_int_op(sz, OP_EQ, 7900);
  tt_mem_op(cp,OP_EQ, stuff+100, 7900);
294
295
296
297

  buf_free(buf);
  buf = NULL;

298
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
299
300
301
302
303
304
 done:
  buf_free(buf);
  tor_free(stuff);
  tor_free(tmp);
}

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
static void
test_buffers_move_all(void *arg)
{
  (void)arg;
  buf_t *input = buf_new();
  buf_t *output = buf_new();
  char *s = NULL;

  /* Move from empty buffer to nonempty buffer. (This is a regression test for
   * #40076) */
  buf_add(output, "abc", 3);
  buf_assert_ok(input);
  buf_assert_ok(output);
  buf_move_all(output, input);
  buf_assert_ok(input);
  buf_assert_ok(output);
  tt_int_op(buf_datalen(output), OP_EQ, 3);
  s = buf_extract(output, NULL);
  tt_str_op(s, OP_EQ, "abc");
  buf_free(output);
  buf_free(input);
  tor_free(s);

  /* Move from empty to empty. */
  output = buf_new();
  input = buf_new();
  buf_move_all(output, input);
  buf_assert_ok(input);
  buf_assert_ok(output);
  tt_int_op(buf_datalen(output), OP_EQ, 0);
  buf_free(output);
  buf_free(input);

  /* Move from nonempty to empty. */
  output = buf_new();
  input = buf_new();
  buf_add(input, "longstanding bugs", 17);
  buf_move_all(output, input);
  buf_assert_ok(input);
  buf_assert_ok(output);
  s = buf_extract(output, NULL);
  tt_str_op(s, OP_EQ, "longstanding bugs");
  buf_free(output);
  buf_free(input);
  tor_free(s);

  /* Move from nonempty to nonempty. */
  output = buf_new();
  input = buf_new();
  buf_add(output, "the start of", 12);
  buf_add(input, " a string", 9);
  buf_move_all(output, input);
  buf_assert_ok(input);
  buf_assert_ok(output);
  s = buf_extract(output, NULL);
  tt_str_op(s, OP_EQ, "the start of a string");

 done:
  buf_free(output);
  buf_free(input);
  tor_free(s);
}

368
369
370
static void
test_buffer_copy(void *arg)
{
371
  buf_t *buf=NULL, *buf2=NULL;
372
373
374
375
376
377
  const char *s;
  size_t len;
  char b[256];
  int i;
  (void)arg;

378
  buf = buf_new();
379
380
381
  tt_assert(buf);

  /* Copy an empty buffer. */
382
  tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
383
  tt_assert(buf2);
384
  tt_int_op(0, OP_EQ, buf_datalen(buf2));
385
386
387
388

  /* Now try with a short buffer. */
  s = "And now comes an act of enormous enormance!";
  len = strlen(s);
389
  buf_add(buf, s, len);
390
  tt_int_op(len, OP_EQ, buf_datalen(buf));
391
  /* Add junk to buf2 so we can test replacing.*/
392
  buf_add(buf2, "BLARG", 5);
393
  tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
394
  tt_int_op(len, OP_EQ, buf_datalen(buf2));
395
  buf_get_bytes(buf2, b, len);
396
  tt_mem_op(b, OP_EQ, s, len);
397
  /* Now free buf2 and retry so we can test allocating */
398
  buf_free(buf2);
399
  buf2 = NULL;
400
  tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
401
  tt_int_op(len, OP_EQ, buf_datalen(buf2));
402
  buf_get_bytes(buf2, b, len);
403
  tt_mem_op(b, OP_EQ, s, len);
404
  /* Clear buf for next test */
405
  buf_get_bytes(buf, b, len);
406
  tt_int_op(buf_datalen(buf),OP_EQ,0);
407
408
409
410
411
412
413
414

  /* Okay, now let's try a bigger buffer. */
  s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
    "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
    "fugiat quo voluptas nulla pariatur?";
  len = strlen(s);
  for (i = 0; i < 256; ++i) {
    b[0]=i;
415
416
    buf_add(buf, b, 1);
    buf_add(buf, s, len);
417
  }
418
  tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
419
  tt_int_op(buf_datalen(buf2), OP_EQ, buf_datalen(buf));
420
  for (i = 0; i < 256; ++i) {
421
    buf_get_bytes(buf2, b, len+1);
422
423
    tt_int_op((unsigned char)b[0],OP_EQ,i);
    tt_mem_op(b+1, OP_EQ, s, len);
424
425
426
427
  }

 done:
  if (buf)
428
    buf_free(buf);
429
  if (buf2)
430
    buf_free(buf2);
431
432
}

433
434
435
static void
test_buffer_allocation_tracking(void *arg)
{
436
  char *junk = tor_malloc(16384);
437
  buf_t *buf1 = NULL, *buf2 = NULL;
438
  int i;
439
440
441

  (void)arg;

442
  crypto_rand(junk, 16384);
443
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
444
445
446
447
448
449

  buf1 = buf_new();
  tt_assert(buf1);
  buf2 = buf_new();
  tt_assert(buf2);

450
451
  tt_int_op(buf_allocation(buf1), OP_EQ, 0);
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
452

453
454
455
456
  buf_add(buf1, junk, 4000);
  buf_add(buf1, junk, 4000);
  buf_add(buf1, junk, 4000);
  buf_add(buf1, junk, 4000);
457
  tt_int_op(buf_allocation(buf1), OP_EQ, 16384);
458
  buf_get_bytes(buf1, junk, 100);
459
  tt_int_op(buf_allocation(buf1), OP_EQ, 16384); /* still 4 4k chunks */
460

461
  tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
462

463
  buf_get_bytes(buf1, junk, 4096); /* drop a 1k chunk... */
464
  tt_int_op(buf_allocation(buf1), OP_EQ, 3*4096); /* now 3 4k chunks */
465

466
  tt_int_op(buf_get_total_allocation(), OP_EQ, 12288); /* that chunk was really
467
                                                       freed. */
468

469
  buf_add(buf2, junk, 4000);
470
  tt_int_op(buf_allocation(buf2), OP_EQ, 4096); /* another 4k chunk. */
471
  /*
472
   * We bounce back up to 16384 by allocating a new chunk.
473
   */
474
  tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
475
  buf_add(buf2, junk, 4000);
476
  tt_int_op(buf_allocation(buf2), OP_EQ, 8192); /* another 4k chunk. */
Nick Mathewson's avatar
Nick Mathewson committed
477
478
  tt_int_op(buf_get_total_allocation(),
            OP_EQ, 5*4096); /* that chunk was new. */
479
480
481

  /* Make a really huge buffer */
  for (i = 0; i < 1000; ++i) {
482
    buf_add(buf2, junk, 4000);
483
  }
484
485
  tt_int_op(buf_allocation(buf2), OP_GE, 4008000);
  tt_int_op(buf_get_total_allocation(), OP_GE, 4008000);
486
487
488
  buf_free(buf2);
  buf2 = NULL;

489
490
  tt_int_op(buf_get_total_allocation(), OP_LT, 4008000);
  tt_int_op(buf_get_total_allocation(), OP_EQ, buf_allocation(buf1));
491
492
  buf_free(buf1);
  buf1 = NULL;
493
  tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
494
495
496
497

 done:
  buf_free(buf1);
  buf_free(buf2);
498
  tor_free(junk);
499
500
}

501
502
503
504
505
static void
test_buffer_time_tracking(void *arg)
{
  buf_t *buf=NULL, *buf2=NULL;
  const time_t START = 1389288246;
506
  const uint64_t START_NSEC = ((uint64_t)START) * 1000000000;
507
508
509
510
511
512
  int i;
  char tmp[4096];
  (void)arg;

  crypto_rand(tmp, sizeof(tmp));

513
  monotime_enable_test_mocking();
514
515
516
517

  buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
  tt_assert(buf);

518
  monotime_coarse_set_mock_time_nsec(START_NSEC);
519
  const uint32_t START_TS = monotime_coarse_get_stamp();
520

521
  /* Empty buffer means the timestamp is 0. */
522
523
  tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS));
  tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
524

525
  buf_add(buf, "ABCDEFG", 7);
526
  tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
527
528
529

  buf2 = buf_copy(buf);
  tt_assert(buf2);
Nick Mathewson's avatar
Nick Mathewson committed
530
  tt_int_op(1234, OP_EQ,
531
            buf_get_oldest_chunk_timestamp(buf2, START_TS+1234));
532
533

  /* Now add more bytes; enough to overflow the first chunk. */
534
  monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000);
535
  const uint32_t TS2 = monotime_coarse_get_stamp();
536
  for (i = 0; i < 600; ++i)
537
    buf_add(buf, "ABCDEFG", 7);
538
  tt_int_op(4207, OP_EQ, buf_datalen(buf));
539
540

  /* The oldest bytes are still in the front. */
541
  tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
542
543
544

  /* Once those bytes are dropped, the chunk is still on the first
   * timestamp. */
545
  buf_get_bytes(buf, tmp, 100);
546
  tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
547
548
549

  /* But once we discard the whole first chunk, we get the data in the second
   * chunk. */
550
  buf_get_bytes(buf, tmp, 4000);
551
  tt_int_op(107, OP_EQ, buf_datalen(buf));
552
  tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
553
554
555

  /* This time we'll be grabbing a chunk from the freelist, and making sure
     its time gets updated */
556
  monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000);
557
  const uint32_t TS3 = monotime_coarse_get_stamp();
558
  for (i = 0; i < 600; ++i)
559
    buf_add(buf, "ABCDEFG", 7);
560
  tt_int_op(4307, OP_EQ, buf_datalen(buf));
561

562
  tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
563
564
  buf_get_bytes(buf, tmp, 4000);
  buf_get_bytes(buf, tmp, 306);
565
566
  tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3));
  tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3+383));
567
568
569
570

 done:
  buf_free(buf);
  buf_free(buf2);
571
  monotime_disable_test_mocking();
572
573
}

574
static void
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
test_buffers_compress_fin_at_chunk_end_impl(compress_method_t method,
                                            compression_level_t level)
{
  char *msg = NULL;
  char *contents = NULL;
  char *expanded = NULL;
  buf_t *buf = NULL;
  tor_compress_state_t *compress_state = NULL;
  size_t out_len, in_len;
  size_t sz, headerjunk;

  buf = buf_new_with_capacity(128); /* will round up */
  sz = buf_get_default_chunk_size(buf);
  msg = tor_malloc_zero(sz);

590
  buf_add(buf, msg, 1);
591
592
593
594
595
  tt_assert(buf->head);

  /* Fill up the chunk so the compression stuff won't fit in one chunk. */
  tt_uint_op(buf->head->memlen, OP_LT, sz);
  headerjunk = buf->head->memlen - 7;
596
  buf_add(buf, msg, headerjunk-1);
597
598
599
600
  tt_uint_op(buf->head->datalen, OP_EQ, headerjunk);
  tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk);
  /* Write an empty string, with finalization on. */
  compress_state = tor_compress_new(1, method, level);
601
  tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
602
603
604
605

  in_len = buf_datalen(buf);
  contents = tor_malloc(in_len);

606
  tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
607

608
609
610
611
612
  if (method == NO_METHOD) {
    tt_uint_op(in_len, OP_EQ, headerjunk);
  } else {
    tt_uint_op(in_len, OP_GT, headerjunk);
  }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

  tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len,
                                     contents + headerjunk,
                                     in_len - headerjunk,
                                     method, 1,
                                     LOG_WARN));

  tt_int_op(out_len, OP_EQ, 0);
  tt_assert(expanded);

 done:
  buf_free(buf);
  tor_compress_free(compress_state);
  tor_free(contents);
  tor_free(expanded);
  tor_free(msg);
}

static void
test_buffers_compress_impl(compress_method_t method,
                           compression_level_t level,
                           int finalize_with_nil)
635
636
637
638
639
{
  char *msg = NULL;
  char *contents = NULL;
  char *expanded = NULL;
  buf_t *buf = NULL;
640
  tor_compress_state_t *compress_state = NULL;
641
  size_t out_len, in_len;
642
  int done;
643
644

  buf = buf_new_with_capacity(128); /* will round up */
645
  compress_state = tor_compress_new(1, method, level);
646
647
648

  msg = tor_malloc(512);
  crypto_rand(msg, 512);
649
  tt_int_op(buf_add_compress(buf, compress_state,
650
                                  msg, 128, 0), OP_EQ, 0);
651
  tt_int_op(buf_add_compress(buf, compress_state,
652
                                  msg+128, 128, 0), OP_EQ, 0);
653
  tt_int_op(buf_add_compress(buf, compress_state,
654
                                  msg+256, 256, 0), OP_EQ, 0);
655
  done = !finalize_with_nil;
656
  tt_int_op(buf_add_compress(buf, compress_state,
657
                                  "all done", 9, done), OP_EQ, 0);
658
  if (finalize_with_nil) {
659
    tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
660
  }
661
662
663
664

  in_len = buf_datalen(buf);
  contents = tor_malloc(in_len);

665
  tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
666

667
668
  tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len,
                                     contents, in_len,
669
                                     method, 1,
670
                                     LOG_WARN));
671

672
673
674
675
676
677
  tt_int_op(out_len, OP_GE, 128);
  tt_mem_op(msg, OP_EQ, expanded, 128);
  tt_int_op(out_len, OP_GE, 512);
  tt_mem_op(msg, OP_EQ, expanded, 512);
  tt_int_op(out_len, OP_EQ, 512+9);
  tt_mem_op("all done", OP_EQ, expanded+512, 9);
678
679
680

 done:
  buf_free(buf);
681
  tor_compress_free(compress_state);
682
683
684
685
686
  tor_free(contents);
  tor_free(expanded);
  tor_free(msg);
}

687
static void
688
test_buffers_compress(void *arg)
689
{
690
691
692
693
694
695
696
697
698
  const char *methodname = arg;
  tt_assert(methodname);

  compress_method_t method = compression_method_get_by_name(methodname);
  tt_int_op(method, OP_NE, UNKNOWN_METHOD);

  if (! tor_compress_supports_method(method)) {
    tt_skip();
  }
699

700
701
702
703
704
705
  compression_level_t levels[] = {
    BEST_COMPRESSION,
    HIGH_COMPRESSION,
    MEDIUM_COMPRESSION,
    LOW_COMPRESSION
  };
706

707
708
  for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) {
    compression_level_t level = levels[l];
709

710
711
712
    test_buffers_compress_impl(method, level, 0);
    test_buffers_compress_impl(method, level, 1);
    test_buffers_compress_fin_at_chunk_end_impl(method, level);
713
  }
714
715
716

 done:
  ;
717
718
}

719
720
721
static const uint8_t *tls_read_ptr;
static int n_remaining;
static int next_reply_val[16];
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758

static int
mock_tls_read(tor_tls_t *tls, char *cp, size_t len)
{
  (void)tls;
  int rv = next_reply_val[0];
  if (rv > 0) {
    int max = rv > (int)len ? (int)len : rv;
    if (max > n_remaining)
      max = n_remaining;
    memcpy(cp, tls_read_ptr, max);
    rv = max;
    n_remaining -= max;
    tls_read_ptr += max;
  }

  memmove(next_reply_val, next_reply_val + 1, 15*sizeof(int));
  return rv;
}

static void
test_buffers_tls_read_mocked(void *arg)
{
  uint8_t *mem;
  buf_t *buf;
  (void)arg;

  mem = tor_malloc(64*1024);
  crypto_rand((char*)mem, 64*1024);
  tls_read_ptr = mem;
  n_remaining = 64*1024;

  MOCK(tor_tls_read, mock_tls_read);

  buf = buf_new();

  next_reply_val[0] = 1024;
759
  tt_int_op(128, OP_EQ, buf_read_from_tls(buf, NULL, 128));
760
761
762

  next_reply_val[0] = 5000;
  next_reply_val[1] = 5000;
763
  tt_int_op(6000, OP_EQ, buf_read_from_tls(buf, NULL, 6000));
764
765
766
767
768
769
770

 done:
  UNMOCK(tor_tls_read);
  tor_free(mem);
  buf_free(buf);
}

771
772
773
774
775
776
static void
test_buffers_chunk_size(void *arg)
{
  (void)arg;
  const int min = 256;
  const int max = 65536;
777
778
779
780
781
  tt_uint_op(buf_preferred_chunk_size(3), OP_EQ, min);
  tt_uint_op(buf_preferred_chunk_size(25), OP_EQ, min);
  tt_uint_op(buf_preferred_chunk_size(0), OP_EQ, min);
  tt_uint_op(buf_preferred_chunk_size(256), OP_EQ, 512);
  tt_uint_op(buf_preferred_chunk_size(65400), OP_EQ, max);
782
783
  /* Here, we're implicitly saying that the chunk header overhead is
   * between 1 and 100 bytes. 24..48 would probably be more accurate. */
784
785
786
787
  tt_uint_op(buf_preferred_chunk_size(65536), OP_GT, 65536);
  tt_uint_op(buf_preferred_chunk_size(65536), OP_LT, 65536+100);
  tt_uint_op(buf_preferred_chunk_size(165536), OP_GT, 165536);
  tt_uint_op(buf_preferred_chunk_size(165536), OP_LT, 165536+100);
788
789
790
791
 done:
  ;
}

792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
static void
test_buffers_find_contentlen(void *arg)
{
  static const struct {
    const char *headers;
    int r;
    int contentlen;
  } results[] = {
    { "Blah blah\r\nContent-Length: 1\r\n\r\n", 1, 1 },
    { "Blah blah\r\n\r\n", 0, 0 }, /* no content-len */
    { "Blah blah Content-Length: 1\r\n", 0, 0 }, /* no content-len. */
    { "Blah blah\r\nContent-Length: 100000\r\n", 1, 100000},
    { "Blah blah\r\nContent-Length: 1000000000000000000000000\r\n", -1, 0},
    { "Blah blah\r\nContent-Length: 0\r\n", 1, 0},
    { "Blah blah\r\nContent-Length: -1\r\n", -1, 0},
    { "Blah blah\r\nContent-Length: 1x\r\n", -1, 0},
    { "Blah blah\r\nContent-Length: 1 x\r\n", -1, 0},
    { "Blah blah\r\nContent-Length: 1 \r\n", 1, 1},
    { "Blah blah\r\nContent-Length:  \r\n", -1, 0},
    { "Blah blah\r\nContent-Length: ", -1, 0},
    { "Blah blah\r\nContent-Length: 5050", -1, 0},
    { NULL, 0, 0 }
  };
  int i;

  (void)arg;

  for (i = 0; results[i].headers; ++i) {
    int r;
    size_t sz;
    size_t headerlen = strlen(results[i].headers);
    char * tmp = tor_memdup(results[i].headers, headerlen);/* ensure no eos */
    sz = 999; /* to ensure it gets set */
    r = buf_http_find_content_length(tmp, headerlen, &sz);
    tor_free(tmp);
    log_debug(LD_DIR, "%d: %s", i, escaped(results[i].headers));
828
829
    tt_int_op(r, OP_EQ, results[i].r);
    tt_int_op(sz, OP_EQ, results[i].contentlen);
830
831
832
833
834
  }
 done:
  ;
}

835
836
837
838
839
840
841
842
static void
test_buffer_peek_startswith(void *arg)
{
  (void)arg;
  buf_t *buf;
  buf = buf_new();
  tt_ptr_op(buf, OP_NE, NULL);

843
844
  tt_assert(buf_peek_startswith(buf, ""));
  tt_assert(! buf_peek_startswith(buf, "X"));
845

846
  buf_add(buf, "Tor", 3);
847

848
849
850
851
852
853
854
855
  tt_assert(buf_peek_startswith(buf, ""));
  tt_assert(buf_peek_startswith(buf, "T"));
  tt_assert(buf_peek_startswith(buf, "To"));
  tt_assert(buf_peek_startswith(buf, "Tor"));
  tt_assert(! buf_peek_startswith(buf, "Top"));
  tt_assert(! buf_peek_startswith(buf, "For"));
  tt_assert(! buf_peek_startswith(buf, "Tork"));
  tt_assert(! buf_peek_startswith(buf, "Torpor"));
856
857
858
859
860

 done:
  buf_free(buf);
}

861
struct testcase_t buffer_tests[] = {
862
863
864
  { "basic", test_buffers_basic, TT_FORK, NULL, NULL },
  { "copy", test_buffer_copy, TT_FORK, NULL, NULL },
  { "pullup", test_buffer_pullup, TT_FORK, NULL, NULL },
865
  { "move_all", test_buffers_move_all, 0, NULL, NULL },
866
  { "startswith", test_buffer_peek_startswith, 0, NULL, NULL },
867
868
  { "allocation_tracking", test_buffer_allocation_tracking, TT_FORK,
    NULL, NULL },
869
  { "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL },
870
871
  { "tls_read_mocked", test_buffers_tls_read_mocked, 0,
    NULL, NULL },
872
  { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL },
873
  { "find_contentlen", test_buffers_find_contentlen, 0, NULL, NULL },
874
875
876
877
878
879
880
881

  { "compress/zlib", test_buffers_compress, TT_FORK,
    &passthrough_setup, (char*)"deflate" },
  { "compress/gzip", test_buffers_compress, TT_FORK,
    &passthrough_setup, (char*)"gzip" },
  { "compress/zstd", test_buffers_compress, TT_FORK,
    &passthrough_setup, (char*)"x-zstd" },
  { "compress/lzma", test_buffers_compress, TT_FORK,
Nick Mathewson's avatar
Nick Mathewson committed
882
    &passthrough_setup, (char*)"x-tor-lzma" },
883
884
  { "compress/none", test_buffers_compress, TT_FORK,
    &passthrough_setup, (char*)"identity" },
885

886
887
  END_OF_TESTCASES
};