1/*	$NetBSD: memory.c,v 1.3 2021/08/14 16:14:55 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17
18#include <sys/cdefs.h>
19__RCSID("$NetBSD: memory.c,v 1.3 2021/08/14 16:14:55 christos Exp $");
20
21#include "portable.h"
22
23#include <ac/stdlib.h>
24#include <ac/string.h>
25
26#include "lber-int.h"
27
28#ifdef LDAP_MEMORY_TRACE
29#include <stdio.h>
30#endif
31
32#ifdef LDAP_MEMORY_DEBUG
33/*
34 * LDAP_MEMORY_DEBUG should only be enabled for the purposes of
35 * debugging memory management within OpenLDAP libraries and slapd.
36 *
37 * It should only be enabled by an experienced developer as it causes
38 * the inclusion of numerous assert()'s, many of which may be triggered
39 * by a perfectly valid program.  If LDAP_MEMORY_DEBUG & 2 is true,
40 * that includes asserts known to break both slapd and current clients.
41 *
42 * The code behind this macro is subject to change as needed to
43 * support this testing.
44 */
45
46struct ber_mem_hdr {
47	ber_int_t	bm_top;	/* Pattern to detect buf overrun from prev buffer */
48	ber_int_t	bm_length; /* Length of user allocated area */
49#ifdef LDAP_MEMORY_TRACE
50	ber_int_t	bm_sequence; /* Allocation sequence number */
51#endif
52	union bmu_align_u {	/* Force alignment, pattern to detect back clobber */
53		ber_len_t	bmu_len_t;
54		ber_tag_t	bmu_tag_t;
55		ber_int_t	bmu_int_t;
56
57		size_t	bmu_size_t;
58		void *	bmu_voidp;
59		double	bmu_double;
60		long	bmu_long;
61		long	(*bmu_funcp)( double );
62		unsigned char	bmu_char[4];
63	} ber_align;
64#define bm_junk	ber_align.bmu_len_t
65#define bm_data	ber_align.bmu_char[1]
66#define bm_char	ber_align.bmu_char
67};
68
69/* Pattern at top of allocated space */
70#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada)
71
72static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK };
73
74/* Note sequence and ber_int_meminuse are counters, but are not
75 * thread safe.  If you want to use these values for multithreaded applications,
76 * you must put mutexes around them, otherwise they will have incorrect values.
77 * When debugging, if you sort the debug output, the sequence number will
78 * put allocations/frees together.  It is then a simple matter to write a script
79 * to find any allocations that don't have a buffer free function.
80 */
81long ber_int_meminuse = 0;
82#ifdef LDAP_MEMORY_TRACE
83static ber_int_t sequence = 0;
84#endif
85
86/* Pattern placed just before user data */
87static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde };
88/* Pattern placed just after user data */
89static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca };
90
91#define mbu_len sizeof(ber_int_mem_hdr.ber_align)
92
93/* Test if pattern placed just before user data is good */
94#define testdatatop(val) ( \
95	*(val->bm_char+mbu_len-4)==toppattern[0] && \
96	*(val->bm_char+mbu_len-3)==toppattern[1] && \
97	*(val->bm_char+mbu_len-2)==toppattern[2] && \
98	*(val->bm_char+mbu_len-1)==toppattern[3] )
99
100/* Place pattern just before user data */
101#define setdatatop(val)	*(val->bm_char+mbu_len-4)=toppattern[0]; \
102	*(val->bm_char+mbu_len-3)=toppattern[1]; \
103	*(val->bm_char+mbu_len-2)=toppattern[2]; \
104	*(val->bm_char+mbu_len-1)=toppattern[3];
105
106/* Test if pattern placed just after user data is good */
107#define testend(val) ( 	*((unsigned char *)val+0)==endpattern[0] && \
108	*((unsigned char *)val+1)==endpattern[1] && \
109	*((unsigned char *)val+2)==endpattern[2] && \
110	*((unsigned char *)val+3)==endpattern[3] )
111
112/* Place pattern just after user data */
113#define setend(val)  	*((unsigned char *)val+0)=endpattern[0]; \
114	*((unsigned char *)val+1)=endpattern[1]; \
115	*((unsigned char *)val+2)=endpattern[2]; \
116	*((unsigned char *)val+3)=endpattern[3];
117
118#define BER_MEM_BADADDR	((void *) &ber_int_mem_hdr.bm_data)
119#define BER_MEM_VALID(p)	do { \
120		assert( (p) != BER_MEM_BADADDR );	\
121		assert( (p) != (void *) &ber_int_mem_hdr );	\
122	} while(0)
123
124#else
125#define BER_MEM_VALID(p)	/* no-op */
126#endif
127
128BerMemoryFunctions *ber_int_memory_fns = NULL;
129
130void
131ber_memfree_x( void *p, void *ctx )
132{
133	if( p == NULL ) {
134		return;
135	}
136
137	BER_MEM_VALID( p );
138
139	if( ber_int_memory_fns == NULL || ctx == NULL ) {
140#ifdef LDAP_MEMORY_DEBUG
141		struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
142			((char *)p - sizeof(struct ber_mem_hdr));
143		assert( mh->bm_top == LBER_MEM_JUNK);
144		assert( testdatatop( mh));
145		assert( testend( (char *)&mh[1] + mh->bm_length) );
146		ber_int_meminuse -= mh->bm_length;
147
148#ifdef LDAP_MEMORY_TRACE
149		fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n",
150			(long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
151			ber_int_meminuse);
152#endif
153		/* Fill the free space with poison */
154		memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t));
155		free( mh );
156#else
157		free( p );
158#endif
159		return;
160	}
161
162	assert( ber_int_memory_fns->bmf_free != 0 );
163
164	(*ber_int_memory_fns->bmf_free)( p, ctx );
165}
166
167void
168ber_memfree( void *p )
169{
170	ber_memfree_x(p, NULL);
171}
172
173void
174ber_memvfree_x( void **vec, void *ctx )
175{
176	int	i;
177
178	if( vec == NULL ) {
179		return;
180	}
181
182	BER_MEM_VALID( vec );
183
184	for ( i = 0; vec[i] != NULL; i++ ) {
185		ber_memfree_x( vec[i], ctx );
186	}
187
188	ber_memfree_x( vec, ctx );
189}
190
191void
192ber_memvfree( void **vec )
193{
194	ber_memvfree_x( vec, NULL );
195}
196
197void *
198ber_memalloc_x( ber_len_t s, void *ctx )
199{
200	void *new;
201
202	if( s == 0 ) {
203		LDAP_MEMORY_DEBUG_ASSERT( s != 0 );
204		return NULL;
205	}
206
207	if( ber_int_memory_fns == NULL || ctx == NULL ) {
208#ifdef LDAP_MEMORY_DEBUG
209		new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t));
210		if( new )
211		{
212		struct ber_mem_hdr *mh = new;
213		mh->bm_top = LBER_MEM_JUNK;
214		mh->bm_length = s;
215		setdatatop( mh);
216		setend( (char *)&mh[1] + mh->bm_length );
217
218		ber_int_meminuse += mh->bm_length;	/* Count mem inuse */
219
220#ifdef LDAP_MEMORY_TRACE
221		mh->bm_sequence = sequence++;
222		fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n",
223			(long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
224			ber_int_meminuse);
225#endif
226		/* poison new memory */
227		memset( (char *)&mh[1], 0xff, s);
228
229		BER_MEM_VALID( &mh[1] );
230		new = &mh[1];
231		}
232#else
233		new = malloc( s );
234#endif
235	} else {
236		new = (*ber_int_memory_fns->bmf_malloc)( s, ctx );
237	}
238
239	if( new == NULL ) {
240		ber_errno = LBER_ERROR_MEMORY;
241	}
242
243	return new;
244}
245
246void *
247ber_memalloc( ber_len_t s )
248{
249	return ber_memalloc_x( s, NULL );
250}
251
252void *
253ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx )
254{
255	void *new;
256
257	if( n == 0 || s == 0 ) {
258		LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0);
259		return NULL;
260	}
261
262	if( ber_int_memory_fns == NULL || ctx == NULL ) {
263#ifdef LDAP_MEMORY_DEBUG
264		new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s
265			? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t))
266			: NULL;
267		if( new )
268		{
269		struct ber_mem_hdr *mh = new;
270
271		mh->bm_top = LBER_MEM_JUNK;
272		mh->bm_length = n*s;
273		setdatatop( mh);
274		setend( (char *)&mh[1] + mh->bm_length );
275
276		ber_int_meminuse += mh->bm_length;
277
278#ifdef LDAP_MEMORY_TRACE
279		mh->bm_sequence = sequence++;
280		fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n",
281			(long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
282			ber_int_meminuse);
283#endif
284		BER_MEM_VALID( &mh[1] );
285		new = &mh[1];
286		}
287#else
288		new = calloc( n, s );
289#endif
290
291	} else {
292		new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx );
293	}
294
295	if( new == NULL ) {
296		ber_errno = LBER_ERROR_MEMORY;
297	}
298
299	return new;
300}
301
302void *
303ber_memcalloc( ber_len_t n, ber_len_t s )
304{
305	return ber_memcalloc_x( n, s, NULL );
306}
307
308void *
309ber_memrealloc_x( void* p, ber_len_t s, void *ctx )
310{
311	void *new = NULL;
312
313	/* realloc(NULL,s) -> malloc(s) */
314	if( p == NULL ) {
315		return ber_memalloc_x( s, ctx );
316	}
317
318	/* realloc(p,0) -> free(p) */
319	if( s == 0 ) {
320		ber_memfree_x( p, ctx );
321		return NULL;
322	}
323
324	BER_MEM_VALID( p );
325
326	if( ber_int_memory_fns == NULL || ctx == NULL ) {
327#ifdef LDAP_MEMORY_DEBUG
328		ber_int_t oldlen;
329		struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
330			((char *)p - sizeof(struct ber_mem_hdr));
331		assert( mh->bm_top == LBER_MEM_JUNK);
332		assert( testdatatop( mh));
333		assert( testend( (char *)&mh[1] + mh->bm_length) );
334		oldlen = mh->bm_length;
335
336		p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) );
337		if( p == NULL ) {
338			ber_errno = LBER_ERROR_MEMORY;
339			return NULL;
340		}
341
342			mh = p;
343		mh->bm_length = s;
344		setend( (char *)&mh[1] + mh->bm_length );
345		if( s > oldlen ) {
346			/* poison any new memory */
347			memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen);
348		}
349
350		assert( mh->bm_top == LBER_MEM_JUNK);
351		assert( testdatatop( mh));
352
353		ber_int_meminuse += s - oldlen;
354#ifdef LDAP_MEMORY_TRACE
355		fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n",
356			(long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
357			ber_int_meminuse);
358#endif
359			BER_MEM_VALID( &mh[1] );
360		return &mh[1];
361#else
362		new = realloc( p, s );
363#endif
364	} else {
365		new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx );
366	}
367
368	if( new == NULL ) {
369		ber_errno = LBER_ERROR_MEMORY;
370	}
371
372	return new;
373}
374
375void *
376ber_memrealloc( void* p, ber_len_t s )
377{
378	return ber_memrealloc_x( p, s, NULL );
379}
380
381void
382ber_bvfree_x( struct berval *bv, void *ctx )
383{
384	if( bv == NULL ) {
385		return;
386	}
387
388	BER_MEM_VALID( bv );
389
390	if ( bv->bv_val != NULL ) {
391		ber_memfree_x( bv->bv_val, ctx );
392	}
393
394	ber_memfree_x( (char *) bv, ctx );
395}
396
397void
398ber_bvfree( struct berval *bv )
399{
400	ber_bvfree_x( bv, NULL );
401}
402
403void
404ber_bvecfree_x( struct berval **bv, void *ctx )
405{
406	int	i;
407
408	if( bv == NULL ) {
409		return;
410	}
411
412	BER_MEM_VALID( bv );
413
414	/* count elements */
415	for ( i = 0; bv[i] != NULL; i++ ) ;
416
417	/* free in reverse order */
418	for ( i--; i >= 0; i-- ) {
419		ber_bvfree_x( bv[i], ctx );
420	}
421
422	ber_memfree_x( (char *) bv, ctx );
423}
424
425void
426ber_bvecfree( struct berval **bv )
427{
428	ber_bvecfree_x( bv, NULL );
429}
430
431int
432ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx )
433{
434	ber_len_t i;
435	struct berval **new;
436
437	if( *bvec == NULL ) {
438		if( bv == NULL ) {
439			/* nothing to add */
440			return 0;
441		}
442
443		*bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx );
444
445		if( *bvec == NULL ) {
446			return -1;
447		}
448
449		(*bvec)[0] = bv;
450		(*bvec)[1] = NULL;
451
452		return 1;
453	}
454
455	BER_MEM_VALID( bvec );
456
457	/* count entries */
458	for ( i = 0; (*bvec)[i] != NULL; i++ ) {
459		/* EMPTY */;
460	}
461
462	if( bv == NULL ) {
463		return i;
464	}
465
466	new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx);
467
468	if( new == NULL ) {
469		return -1;
470	}
471
472	*bvec = new;
473
474	(*bvec)[i++] = bv;
475	(*bvec)[i] = NULL;
476
477	return i;
478}
479
480int
481ber_bvecadd( struct berval ***bvec, struct berval *bv )
482{
483	return ber_bvecadd_x( bvec, bv, NULL );
484}
485
486struct berval *
487ber_dupbv_x(
488	struct berval *dst, struct berval *src, void *ctx )
489{
490	struct berval *new, tmp;
491
492	if( src == NULL ) {
493		ber_errno = LBER_ERROR_PARAM;
494		return NULL;
495	}
496
497	if ( dst ) {
498		new = &tmp;
499	} else {
500		if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
501			return NULL;
502		}
503	}
504
505	if ( src->bv_val == NULL ) {
506		new->bv_val = NULL;
507		new->bv_len = 0;
508	} else {
509
510		if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) {
511			if ( !dst )
512				ber_memfree_x( new, ctx );
513			return NULL;
514		}
515
516		AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len );
517		new->bv_val[src->bv_len] = '\0';
518		new->bv_len = src->bv_len;
519	}
520
521	if ( dst ) {
522		*dst = *new;
523		new = dst;
524	}
525
526	return new;
527}
528
529struct berval *
530ber_dupbv(
531	struct berval *dst, struct berval *src )
532{
533	return ber_dupbv_x( dst, src, NULL );
534}
535
536struct berval *
537ber_bvdup(
538	struct berval *src )
539{
540	return ber_dupbv_x( NULL, src, NULL );
541}
542
543struct berval *
544ber_str2bv_x(
545	LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
546	void *ctx)
547{
548	struct berval *new;
549
550	if( s == NULL ) {
551		ber_errno = LBER_ERROR_PARAM;
552		return NULL;
553	}
554
555	if( bv ) {
556		new = bv;
557	} else {
558		if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
559			return NULL;
560		}
561	}
562
563	new->bv_len = len ? len : strlen( s );
564	if ( dup ) {
565		if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
566			if ( !bv )
567				ber_memfree_x( new, ctx );
568			return NULL;
569		}
570
571		AC_MEMCPY( new->bv_val, s, new->bv_len );
572		new->bv_val[new->bv_len] = '\0';
573	} else {
574		new->bv_val = (char *) s;
575	}
576
577	return( new );
578}
579
580struct berval *
581ber_str2bv(
582	LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
583{
584	return ber_str2bv_x( s, len, dup, bv, NULL );
585}
586
587struct berval *
588ber_mem2bv_x(
589	LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
590	void *ctx)
591{
592	struct berval *new;
593
594	if( s == NULL ) {
595		ber_errno = LBER_ERROR_PARAM;
596		return NULL;
597	}
598
599	if( bv ) {
600		new = bv;
601	} else {
602		if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
603			return NULL;
604		}
605	}
606
607	new->bv_len = len;
608	if ( dup ) {
609		if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
610			if ( !bv ) {
611				ber_memfree_x( new, ctx );
612			}
613			return NULL;
614		}
615
616		AC_MEMCPY( new->bv_val, s, new->bv_len );
617		new->bv_val[new->bv_len] = '\0';
618	} else {
619		new->bv_val = (char *) s;
620	}
621
622	return( new );
623}
624
625struct berval *
626ber_mem2bv(
627	LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
628{
629	return ber_mem2bv_x( s, len, dup, bv, NULL );
630}
631
632char *
633ber_strdup_x( LDAP_CONST char *s, void *ctx )
634{
635	char    *p;
636	size_t	len;
637
638#ifdef LDAP_MEMORY_DEBUG
639	assert(s != NULL);			/* bv damn better point to something */
640#endif
641
642	if( s == NULL ) {
643		ber_errno = LBER_ERROR_PARAM;
644		return NULL;
645	}
646
647	len = strlen( s ) + 1;
648	if ( (p = ber_memalloc_x( len, ctx )) != NULL ) {
649		AC_MEMCPY( p, s, len );
650	}
651
652	return p;
653}
654
655char *
656ber_strdup( LDAP_CONST char *s )
657{
658	return ber_strdup_x( s, NULL );
659}
660
661ber_len_t
662ber_strnlen( LDAP_CONST char *s, ber_len_t len )
663{
664	ber_len_t l;
665
666	for ( l = 0; l < len && s[l] != '\0'; l++ ) ;
667
668	return l;
669}
670
671char *
672ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx )
673{
674	char    *p;
675	size_t	len;
676
677#ifdef LDAP_MEMORY_DEBUG
678	assert(s != NULL);			/* bv damn better point to something */
679#endif
680
681	if( s == NULL ) {
682		ber_errno = LBER_ERROR_PARAM;
683		return NULL;
684	}
685
686	len = ber_strnlen( s, l );
687	if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) {
688		AC_MEMCPY( p, s, len );
689		p[len] = '\0';
690	}
691
692	return p;
693}
694
695char *
696ber_strndup( LDAP_CONST char *s, ber_len_t l )
697{
698	return ber_strndup_x( s, l, NULL );
699}
700
701/*
702 * dst is resized as required by src and the value of src is copied into dst
703 * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be
704 * alloc'ed with the context ctx
705 */
706struct berval *
707ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx )
708{
709	assert( dst != NULL );
710	assert( !BER_BVISNULL( src ) );
711
712	if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) {
713		dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx );
714	}
715
716	AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 );
717	dst->bv_len = src->bv_len;
718
719	return dst;
720}
721
722struct berval *
723ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src )
724{
725	return ber_bvreplace_x( dst, src, NULL );
726}
727
728void
729ber_bvarray_free_x( BerVarray a, void *ctx )
730{
731	int i;
732
733	if (a) {
734		BER_MEM_VALID( a );
735
736		/* count elements */
737		for (i=0; a[i].bv_val; i++) ;
738
739		/* free in reverse order */
740		for (i--; i>=0; i--) {
741			ber_memfree_x(a[i].bv_val, ctx);
742		}
743
744		ber_memfree_x(a, ctx);
745	}
746}
747
748void
749ber_bvarray_free( BerVarray a )
750{
751	ber_bvarray_free_x(a, NULL);
752}
753
754int
755ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx )
756{
757	int i, j;
758	BerVarray new;
759
760	if ( !src ) {
761		*dst = NULL;
762		return 0;
763	}
764
765	for (i=0; !BER_BVISNULL( &src[i] ); i++) ;
766	new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx );
767	if ( !new )
768		return -1;
769	for (j=0; j<i; j++) {
770		ber_dupbv_x( &new[j], &src[j], ctx );
771		if ( BER_BVISNULL( &new[j] )) {
772			ber_bvarray_free_x( new, ctx );
773			return -1;
774		}
775	}
776	BER_BVZERO( &new[j] );
777	*dst = new;
778	return 0;
779}
780
781int
782ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx )
783{
784	int	n;
785
786	if ( *a == NULL ) {
787		if (bv == NULL) {
788			return 0;
789		}
790		n = 0;
791
792		*a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx );
793		if ( *a == NULL ) {
794			return -1;
795		}
796
797	} else {
798		BerVarray atmp;
799		BER_MEM_VALID( a );
800
801		for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) {
802			;	/* just count them */
803		}
804
805		if (bv == NULL) {
806			return n;
807		}
808
809		atmp = (BerValue *) ber_memrealloc_x( (char *) *a,
810		    (n + 2) * sizeof(BerValue), ctx );
811
812		if( atmp == NULL ) {
813			return -1;
814		}
815
816		*a = atmp;
817	}
818
819	(*a)[n++] = *bv;
820	(*a)[n].bv_val = NULL;
821	(*a)[n].bv_len = 0;
822
823	return n;
824}
825
826int
827ber_bvarray_add( BerVarray *a, BerValue *bv )
828{
829	return ber_bvarray_add_x( a, bv, NULL );
830}
831