1*9ab69854Stb /* $OpenBSD: bio_chain.c,v 1.16 2023/08/07 11:00:54 tb Exp $ */
2720505cfStb /*
3720505cfStb * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4720505cfStb *
5720505cfStb * Permission to use, copy, modify, and distribute this software for any
6720505cfStb * purpose with or without fee is hereby granted, provided that the above
7720505cfStb * copyright notice and this permission notice appear in all copies.
8720505cfStb *
9720505cfStb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10720505cfStb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11720505cfStb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12720505cfStb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13720505cfStb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14720505cfStb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15720505cfStb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16720505cfStb */
17720505cfStb
18720505cfStb #include <err.h>
19720505cfStb #include <stdio.h>
20720505cfStb #include <string.h>
21720505cfStb
22720505cfStb #include <openssl/bio.h>
23720505cfStb
24720505cfStb #include "bio_local.h"
25720505cfStb
26000361f4Stb #ifndef nitems
27000361f4Stb #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
28000361f4Stb #endif
29000361f4Stb
30000361f4Stb #define CHAIN_POP_LEN 5
31000361f4Stb #define LINK_CHAIN_A_LEN 8
32000361f4Stb #define LINK_CHAIN_B_LEN 5
33720505cfStb
34720505cfStb static BIO *
BIO_prev(BIO * bio)35720505cfStb BIO_prev(BIO *bio)
36720505cfStb {
37720505cfStb if (bio == NULL)
38720505cfStb return NULL;
39720505cfStb
40720505cfStb return bio->prev_bio;
41720505cfStb }
42720505cfStb
43000361f4Stb static void bio_chain_destroy(BIO **, size_t);
44000361f4Stb
45000361f4Stb static int
bio_chain_create(const BIO_METHOD * meth,BIO * chain[],size_t len)46000361f4Stb bio_chain_create(const BIO_METHOD *meth, BIO *chain[], size_t len)
47000361f4Stb {
48000361f4Stb BIO *prev;
49000361f4Stb size_t i;
50000361f4Stb
51000361f4Stb memset(chain, 0, len * sizeof(BIO *));
52000361f4Stb
53000361f4Stb prev = NULL;
54000361f4Stb for (i = 0; i < len; i++) {
55000361f4Stb if ((chain[i] = BIO_new(meth)) == NULL) {
56000361f4Stb fprintf(stderr, "BIO_new failed\n");
57000361f4Stb goto err;
58000361f4Stb }
59000361f4Stb if ((prev = BIO_push(prev, chain[i])) == NULL) {
60000361f4Stb fprintf(stderr, "BIO_push failed\n");
61000361f4Stb goto err;
62000361f4Stb }
63000361f4Stb }
64000361f4Stb
65000361f4Stb return 1;
66000361f4Stb
67000361f4Stb err:
68000361f4Stb bio_chain_destroy(chain, len);
69000361f4Stb
70000361f4Stb return 0;
71000361f4Stb }
72000361f4Stb
73000361f4Stb static void
bio_chain_destroy(BIO * chain[],size_t len)74000361f4Stb bio_chain_destroy(BIO *chain[], size_t len)
75000361f4Stb {
76000361f4Stb size_t i;
77000361f4Stb
78000361f4Stb for (i = 0; i < len; i++)
79000361f4Stb BIO_free(chain[i]);
80000361f4Stb
81000361f4Stb memset(chain, 0, len * sizeof(BIO *));
82000361f4Stb }
83000361f4Stb
84720505cfStb static int
bio_chain_pop_test(void)858a7db614Stb bio_chain_pop_test(void)
86720505cfStb {
87000361f4Stb BIO *bio[CHAIN_POP_LEN];
88720505cfStb BIO *prev, *next;
89720505cfStb size_t i, j;
90720505cfStb int failed = 1;
91720505cfStb
92000361f4Stb for (i = 0; i < nitems(bio); i++) {
93720505cfStb memset(bio, 0, sizeof(bio));
94720505cfStb prev = NULL;
95720505cfStb
96000361f4Stb if (!bio_chain_create(BIO_s_null(), bio, nitems(bio)))
97000361f4Stb goto err;
98720505cfStb
99720505cfStb /* Check that the doubly-linked list was set up as expected. */
100720505cfStb if (BIO_prev(bio[0]) != NULL) {
101720505cfStb fprintf(stderr,
102720505cfStb "i = %zu: first BIO has predecessor\n", i);
103720505cfStb goto err;
104720505cfStb }
105000361f4Stb if (BIO_next(bio[nitems(bio) - 1]) != NULL) {
106720505cfStb fprintf(stderr, "i = %zu: last BIO has successor\n", i);
107720505cfStb goto err;
108720505cfStb }
109000361f4Stb for (j = 0; j < nitems(bio); j++) {
110720505cfStb if (j > 0) {
111720505cfStb if (BIO_prev(bio[j]) != bio[j - 1]) {
112720505cfStb fprintf(stderr, "i = %zu: "
113720505cfStb "BIO_prev(bio[%zu]) != bio[%zu]\n",
114720505cfStb i, j, j - 1);
115720505cfStb goto err;
116720505cfStb }
117720505cfStb }
118000361f4Stb if (j < nitems(bio) - 1) {
119720505cfStb if (BIO_next(bio[j]) != bio[j + 1]) {
120720505cfStb fprintf(stderr, "i = %zu: "
121720505cfStb "BIO_next(bio[%zu]) != bio[%zu]\n",
122720505cfStb i, j, j + 1);
123720505cfStb goto err;
124720505cfStb }
125720505cfStb }
126720505cfStb }
127720505cfStb
128720505cfStb /* Drop the ith bio from the chain. */
129720505cfStb next = BIO_pop(bio[i]);
130720505cfStb
131720505cfStb if (BIO_prev(bio[i]) != NULL || BIO_next(bio[i]) != NULL) {
132720505cfStb fprintf(stderr,
133720505cfStb "BIO_pop() didn't isolate bio[%zu]\n", i);
134720505cfStb goto err;
135720505cfStb }
136720505cfStb
137000361f4Stb if (i < nitems(bio) - 1) {
138720505cfStb if (next != bio[i + 1]) {
139720505cfStb fprintf(stderr, "BIO_pop(bio[%zu]) did not "
140720505cfStb "return bio[%zu]\n", i, i + 1);
141720505cfStb goto err;
142720505cfStb }
143720505cfStb } else {
144720505cfStb if (next != NULL) {
145720505cfStb fprintf(stderr, "i = %zu: "
146720505cfStb "BIO_pop(last) != NULL\n", i);
147720505cfStb goto err;
148720505cfStb }
149720505cfStb }
150720505cfStb
151720505cfStb /*
152720505cfStb * Walk the remainder of the chain and see if the doubly linked
153720505cfStb * list checks out.
154720505cfStb */
155720505cfStb if (i == 0) {
156720505cfStb prev = bio[1];
157720505cfStb j = 2;
158720505cfStb } else {
159720505cfStb prev = bio[0];
160720505cfStb j = 1;
161720505cfStb }
162720505cfStb
163000361f4Stb for (; j < nitems(bio); j++) {
164720505cfStb if (j == i)
165720505cfStb continue;
166720505cfStb if (BIO_next(prev) != bio[j]) {
167720505cfStb fprintf(stderr, "i = %zu, j = %zu: "
168720505cfStb "BIO_next(prev) != bio[%zu]\n", i, j, j);
169720505cfStb goto err;
170720505cfStb }
171720505cfStb if (BIO_prev(bio[j]) != prev) {
172720505cfStb fprintf(stderr, "i = %zu, j = %zu: "
173720505cfStb "BIO_prev(bio[%zu]) != prev\n", i, j, j);
174720505cfStb goto err;
175720505cfStb }
176720505cfStb prev = bio[j];
177720505cfStb }
178720505cfStb
179720505cfStb if (BIO_next(prev) != NULL) {
180720505cfStb fprintf(stderr, "i = %zu: BIO_next(prev) != NULL\n", i);
181720505cfStb goto err;
182720505cfStb }
183720505cfStb
184000361f4Stb bio_chain_destroy(bio, nitems(bio));
185720505cfStb }
186720505cfStb
187720505cfStb failed = 0;
188720505cfStb
189720505cfStb err:
190000361f4Stb bio_chain_destroy(bio, nitems(bio));
191720505cfStb
192720505cfStb return failed;
193720505cfStb }
194720505cfStb
195532843a0Stb static void
walk(BIO * (* step)(BIO *),BIO * start,BIO ** end,size_t * len)196532843a0Stb walk(BIO *(*step)(BIO *), BIO *start, BIO **end, size_t *len)
197b4f546d1Stb {
198532843a0Stb BIO *current = NULL;
199532843a0Stb BIO *next = start;
200b4f546d1Stb
201532843a0Stb *len = 0;
202d022d5a9Stb while (next != NULL) {
203532843a0Stb current = next;
204532843a0Stb next = step(current);
205532843a0Stb (*len)++;
206d022d5a9Stb }
207532843a0Stb *end = current;
208b4f546d1Stb }
209b4f546d1Stb
210b4f546d1Stb static int
walk_report(BIO * last,BIO * expected_last,size_t len,size_t expected_len,size_t i,size_t j,const char * fn,const char * description,const char * direction,const char * last_name)211532843a0Stb walk_report(BIO *last, BIO *expected_last, size_t len, size_t expected_len,
212532843a0Stb size_t i, size_t j, const char *fn, const char *description,
213532843a0Stb const char *direction, const char *last_name)
214b4f546d1Stb {
215532843a0Stb if (last != expected_last) {
21632204974Stb fprintf(stderr, "%s case (%zu, %zu) %s %s has unexpected %s\n",
21732204974Stb fn, i, j, description, direction, last_name);
218532843a0Stb return 0;
219b4f546d1Stb }
220b4f546d1Stb
22123423e6fStb if (len != expected_len) {
22232204974Stb fprintf(stderr, "%s case (%zu, %zu) %s %s want %zu, got %zu\n",
223532843a0Stb fn, i, j, description, direction, expected_len, len);
224532843a0Stb return 0;
225b4f546d1Stb }
226b4f546d1Stb
227532843a0Stb return 1;
228532843a0Stb }
229b4f546d1Stb
230532843a0Stb static int
walk_forward(BIO * start,BIO * expected_end,size_t expected_len,size_t i,size_t j,const char * fn,const char * description)231532843a0Stb walk_forward(BIO *start, BIO *expected_end, size_t expected_len,
232532843a0Stb size_t i, size_t j, const char *fn, const char *description)
233532843a0Stb {
234532843a0Stb BIO *end;
235532843a0Stb size_t len;
236532843a0Stb
237532843a0Stb walk(BIO_next, start, &end, &len);
238532843a0Stb
239532843a0Stb return walk_report(end, expected_end, len, expected_len,
240532843a0Stb i, j, fn, description, "forward", "end");
241532843a0Stb }
242532843a0Stb
243532843a0Stb static int
walk_backward(BIO * expected_start,BIO * end,size_t expected_len,size_t i,size_t j,const char * fn,const char * description)244532843a0Stb walk_backward(BIO *expected_start, BIO *end, size_t expected_len,
245532843a0Stb size_t i, size_t j, const char *fn, const char *description)
246532843a0Stb {
247532843a0Stb BIO *start;
248532843a0Stb size_t len;
249532843a0Stb
250532843a0Stb walk(BIO_prev, end, &start, &len);
251532843a0Stb
252532843a0Stb return walk_report(start, expected_start, len, expected_len,
253532843a0Stb i, j, fn, description, "backward", "start");
254b4f546d1Stb }
255b4f546d1Stb
256b4f546d1Stb static int
check_chain(BIO * start,BIO * end,size_t expected_len,size_t i,size_t j,const char * fn,const char * description)25723423e6fStb check_chain(BIO *start, BIO *end, size_t expected_len, size_t i, size_t j,
25823423e6fStb const char *fn, const char *description)
259b4f546d1Stb {
26023423e6fStb if (!walk_forward(start, end, expected_len, i, j, fn, description))
261b4f546d1Stb return 0;
26207b5c626Stb
26323423e6fStb if (!walk_backward(start, end, expected_len, i, j, fn, description))
264b4f546d1Stb return 0;
265b4f546d1Stb
266b4f546d1Stb return 1;
267b4f546d1Stb }
268b4f546d1Stb
269720505cfStb /*
270000361f4Stb * Link two linear chains of BIOs A[] and B[] together using either
271000361f4Stb * BIO_push(A[i], B[j]) or BIO_set_next(A[i], B[j]).
272720505cfStb *
273720505cfStb * BIO_push() first walks the chain A[] to its end and then appends the tail
274720505cfStb * of chain B[] starting at B[j]. If j > 0, we get two chains
275720505cfStb *
276000361f4Stb * A[0] -- ... -- A[nitems(A) - 1] -- B[j] -- ... -- B[nitems(B) - 1]
277720505cfStb * `- link created by BIO_push()
278720505cfStb * B[0] -- ... -- B[j-1]
279720505cfStb * |<-- oldhead -->|
280720505cfStb *
281000361f4Stb * of lengths nitems(A) + nitems(B) - j and j, respectively.
282720505cfStb * If j == 0, the second chain (oldhead) is empty. One quirk of BIO_push() is
283720505cfStb * that the outcome of BIO_push(A[i], B[j]) apart from the return value is
284720505cfStb * independent of i.
285720505cfStb *
286720505cfStb * Prior to bio_lib.c r1.41, BIO_push(A[i], B[j]) would fail to dissociate the
287000361f4Stb * two chains and leave B[j] with two parents for 0 < j < nitems(B).
288000361f4Stb * B[j]->prev_bio would point at A[nitems(A) - 1], while both B[j - 1] and
289000361f4Stb * A[nitems(A) - 1] would point at B[j]. In particular, BIO_free_all(A[0])
290720505cfStb * followed by BIO_free_all(B[0]) results in a double free of B[j].
291720505cfStb *
292720505cfStb * The result for BIO_set_next() is different: three chains are created.
293720505cfStb *
294720505cfStb * |--- oldtail -->
295720505cfStb * ... -- A[i-1] -- A[i] -- A[i+1] -- ...
296720505cfStb * \
297720505cfStb * \ link created by BIO_set_next()
298720505cfStb * --- oldhead -->| \
299720505cfStb * ... -- B[j-1] -- B[j] -- B[j+1] -- ...
300720505cfStb *
301000361f4Stb * After creating a new link, the new chain has length i + 1 + nitems(B) - j,
302000361f4Stb * oldtail has length nitems(A) - i - 1 and oldhead has length j.
303720505cfStb *
30407b5c626Stb * Prior to bio_lib.c r1.40, BIO_set_next(A[i], B[j]) would result in both A[i]
30507b5c626Stb * and B[j - 1] pointing at B[j] while B[j] would point back at A[i]. Calling
30607b5c626Stb * BIO_free_all(A[0]) and BIO_free_all(B[0]) results in a double free of B[j].
307720505cfStb *
308720505cfStb * XXX: Should check that the callback is called on BIO_push() as expected.
309720505cfStb */
310720505cfStb
311720505cfStb static int
link_chains_at(size_t i,size_t j,int use_bio_push)3128a7db614Stb link_chains_at(size_t i, size_t j, int use_bio_push)
313720505cfStb {
314b4f546d1Stb const char *fn = use_bio_push ? "BIO_push" : "BIO_set_next";
315000361f4Stb BIO *A[LINK_CHAIN_A_LEN], *B[LINK_CHAIN_B_LEN];
316000361f4Stb BIO *new_start, *new_end;
317720505cfStb BIO *oldhead_start, *oldhead_end, *oldtail_start, *oldtail_end;
31823423e6fStb size_t new_len, oldhead_len, oldtail_len;
319720505cfStb int failed = 1;
320720505cfStb
321720505cfStb memset(A, 0, sizeof(A));
322720505cfStb memset(B, 0, sizeof(B));
323720505cfStb
3247bb2cb9aStb if (i >= nitems(A) || j >= nitems(B))
325000361f4Stb goto err;
326000361f4Stb
327720505cfStb /* Create two linear chains of BIOs. */
328000361f4Stb if (!bio_chain_create(BIO_s_null(), A, nitems(A)))
329000361f4Stb goto err;
330000361f4Stb if (!bio_chain_create(BIO_s_null(), B, nitems(B)))
331000361f4Stb goto err;
332720505cfStb
333720505cfStb /*
334720505cfStb * Set our expectations. ... it's complicated.
335720505cfStb */
336720505cfStb
337b4f546d1Stb new_start = A[0];
338000361f4Stb new_end = B[nitems(B) - 1];
339d022d5a9Stb /* new_len depends on use_bio_push. It is set a few lines down. */
340b4f546d1Stb
341720505cfStb oldhead_start = B[0];
342720505cfStb oldhead_end = BIO_prev(B[j]);
34307b5c626Stb oldhead_len = j;
344720505cfStb
345b4f546d1Stb /* If we push B[0] or set next to B[0], the oldhead chain is empty. */
346720505cfStb if (j == 0) {
347720505cfStb oldhead_start = NULL;
3483441085dStb oldhead_end = NULL;
3493441085dStb oldhead_len = 0;
350720505cfStb }
351720505cfStb
352720505cfStb if (use_bio_push) {
35323423e6fStb new_len = nitems(A) + nitems(B) - j;
354720505cfStb
355720505cfStb /* oldtail doesn't exist in the BIO_push() case. */
356720505cfStb oldtail_start = NULL;
357720505cfStb oldtail_end = NULL;
35823423e6fStb oldtail_len = 0;
359720505cfStb } else {
36023423e6fStb new_len = i + 1 + nitems(B) - j;
361720505cfStb
362720505cfStb oldtail_start = BIO_next(A[i]);
363000361f4Stb oldtail_end = A[nitems(A) - 1];
36423423e6fStb oldtail_len = nitems(A) - i - 1;
365d022d5a9Stb
366d022d5a9Stb /* If we set next on end of A[], the oldtail chain is empty. */
367d022d5a9Stb if (i == nitems(A) - 1) {
368d022d5a9Stb oldtail_start = NULL;
369d022d5a9Stb oldtail_end = NULL;
370d022d5a9Stb oldtail_len = 0;
371d022d5a9Stb }
372720505cfStb }
373720505cfStb
374be79d765Stb /* The two chains A[] and B[] are split into three disjoint pieces. */
375be79d765Stb if (nitems(A) + nitems(B) != new_len + oldtail_len + oldhead_len) {
376be79d765Stb fprintf(stderr, "%s case (%zu, %zu) inconsistent lengths: "
377abbe4cfaStb "%zu + %zu != %zu + %zu + %zu\n", fn, i, j,
378be79d765Stb nitems(A), nitems(B), new_len, oldtail_len, oldhead_len);
379be79d765Stb goto err;
380be79d765Stb }
381be79d765Stb
382720505cfStb /*
383720505cfStb * Now actually push or set next.
384720505cfStb */
385720505cfStb
386720505cfStb if (use_bio_push) {
387720505cfStb if (BIO_push(A[i], B[j]) != A[i]) {
388b4f546d1Stb fprintf(stderr, "BIO_push(A[%zu], B[%zu]) != A[%zu]\n",
389b4f546d1Stb i, j, i);
390720505cfStb goto err;
391720505cfStb }
392720505cfStb } else {
393720505cfStb BIO_set_next(A[i], B[j]);
394720505cfStb }
395720505cfStb
396720505cfStb /*
397b4f546d1Stb * Check that all the chains match our expectations.
398720505cfStb */
399720505cfStb
40023423e6fStb if (!check_chain(new_start, new_end, new_len, i, j, fn, "new chain"))
401720505cfStb goto err;
402720505cfStb
40323423e6fStb if (!check_chain(oldhead_start, oldhead_end, oldhead_len, i, j, fn,
404b4f546d1Stb "oldhead"))
405720505cfStb goto err;
406720505cfStb
40723423e6fStb if (!check_chain(oldtail_start, oldtail_end, oldtail_len, i, j, fn,
408b4f546d1Stb "oldtail"))
409720505cfStb goto err;
410720505cfStb
411720505cfStb /*
4123441085dStb * All sanity checks passed. We can now free the chains
413720505cfStb * with the BIO API without risk of leaks or double frees.
414720505cfStb */
415720505cfStb
4163441085dStb BIO_free_all(new_start);
417720505cfStb BIO_free_all(oldhead_start);
418720505cfStb BIO_free_all(oldtail_start);
419720505cfStb
420720505cfStb memset(A, 0, sizeof(A));
421720505cfStb memset(B, 0, sizeof(B));
422720505cfStb
423720505cfStb failed = 0;
424720505cfStb
425720505cfStb err:
426000361f4Stb bio_chain_destroy(A, nitems(A));
427000361f4Stb bio_chain_destroy(B, nitems(B));
428720505cfStb
429720505cfStb return failed;
430720505cfStb }
431720505cfStb
432720505cfStb static int
link_chains(int use_bio_push)4338a7db614Stb link_chains(int use_bio_push)
434720505cfStb {
435720505cfStb size_t i, j;
436720505cfStb int failure = 0;
437720505cfStb
438000361f4Stb for (i = 0; i < LINK_CHAIN_A_LEN; i++) {
439000361f4Stb for (j = 0; j < LINK_CHAIN_B_LEN; j++) {
4408a7db614Stb failure |= link_chains_at(i, j, use_bio_push);
441720505cfStb }
442720505cfStb }
443720505cfStb
444720505cfStb return failure;
445720505cfStb }
446720505cfStb
447720505cfStb static int
bio_push_link_test(void)4488a7db614Stb bio_push_link_test(void)
449720505cfStb {
450720505cfStb int use_bio_push = 1;
451720505cfStb
4528a7db614Stb return link_chains(use_bio_push);
453720505cfStb }
454720505cfStb
455720505cfStb static int
bio_set_next_link_test(void)4568a7db614Stb bio_set_next_link_test(void)
457720505cfStb {
458720505cfStb int use_bio_push = 0;
459720505cfStb
4608a7db614Stb return link_chains(use_bio_push);
461720505cfStb }
462720505cfStb
463*9ab69854Stb static long
dup_leak_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)464*9ab69854Stb dup_leak_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
465*9ab69854Stb {
466*9ab69854Stb if (argi == BIO_CTRL_DUP)
467*9ab69854Stb return 0;
468*9ab69854Stb
469*9ab69854Stb return ret;
470*9ab69854Stb }
471*9ab69854Stb
472*9ab69854Stb static int
bio_dup_chain_leak(void)473*9ab69854Stb bio_dup_chain_leak(void)
474*9ab69854Stb {
475*9ab69854Stb BIO *bio[CHAIN_POP_LEN];
476*9ab69854Stb BIO *dup;
477*9ab69854Stb int failed = 1;
478*9ab69854Stb
479*9ab69854Stb if (!bio_chain_create(BIO_s_null(), bio, nitems(bio)))
480*9ab69854Stb goto err;
481*9ab69854Stb
482*9ab69854Stb if ((dup = BIO_dup_chain(bio[0])) == NULL) {
483*9ab69854Stb fprintf(stderr, "BIO_set_callback() failed\n");
484*9ab69854Stb goto err;
485*9ab69854Stb }
486*9ab69854Stb
487*9ab69854Stb BIO_set_callback(bio[CHAIN_POP_LEN - 1], dup_leak_cb);
488*9ab69854Stb
489*9ab69854Stb BIO_free_all(dup);
490*9ab69854Stb if ((dup = BIO_dup_chain(bio[0])) != NULL) {
491*9ab69854Stb fprintf(stderr, "BIO_dup_chain() succeeded unexpectedly\n");
492*9ab69854Stb BIO_free_all(dup);
493*9ab69854Stb goto err;
494*9ab69854Stb }
495*9ab69854Stb
496*9ab69854Stb failed = 0;
497*9ab69854Stb
498*9ab69854Stb err:
499*9ab69854Stb bio_chain_destroy(bio, nitems(bio));
500*9ab69854Stb
501*9ab69854Stb return failed;
502*9ab69854Stb }
503*9ab69854Stb
504720505cfStb int
main(int argc,char ** argv)505720505cfStb main(int argc, char **argv)
506720505cfStb {
507720505cfStb int failed = 0;
508720505cfStb
5098a7db614Stb failed |= bio_chain_pop_test();
5108a7db614Stb failed |= bio_push_link_test();
5118a7db614Stb failed |= bio_set_next_link_test();
512*9ab69854Stb failed |= bio_dup_chain_leak();
513720505cfStb
514720505cfStb return failed;
515720505cfStb }
516