xref: /openbsd-src/regress/lib/libcrypto/bio/bio_chain.c (revision 9ab69854c45e266c6359c538338a7374efbb29e8)
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