xref: /netbsd-src/common/lib/libprop/prop_data.c (revision 3f7675e07760d513cf70499f64f13b2522b29e38)
1 /*	$NetBSD: prop_data.c,v 1.18 2022/08/03 21:13:46 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "prop_object_impl.h"
33 #include <prop/prop_data.h>
34 
35 #if defined(_KERNEL)
36 #include <sys/systm.h>
37 #elif defined(_STANDALONE)
38 #include <sys/param.h>
39 #include <lib/libkern/libkern.h>
40 #else
41 #include <errno.h>
42 #include <limits.h>
43 #include <stdlib.h>
44 #endif
45 
46 struct _prop_data {
47 	struct _prop_object	pd_obj;
48 	union {
49 		void *		pdu_mutable;
50 		const void *	pdu_immutable;
51 	} pd_un;
52 #define	pd_mutable		pd_un.pdu_mutable
53 #define	pd_immutable		pd_un.pdu_immutable
54 	size_t			pd_size;
55 	int			pd_flags;
56 };
57 
58 #define	PD_F_NOCOPY		0x01
59 #define	PD_F_MUTABLE		0x02
60 
61 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
62 
63 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
64 		    "property data container object")
65 
66 static _prop_object_free_rv_t
67 		_prop_data_free(prop_stack_t, prop_object_t *);
68 static bool	_prop_data_externalize(
69 				struct _prop_object_externalize_context *,
70 				void *);
71 static _prop_object_equals_rv_t
72 		_prop_data_equals(prop_object_t, prop_object_t,
73 				  void **, void **,
74 				  prop_object_t *, prop_object_t *);
75 
76 static const struct _prop_object_type _prop_object_type_data = {
77 	.pot_type	=	PROP_TYPE_DATA,
78 	.pot_free	=	_prop_data_free,
79 	.pot_extern	=	_prop_data_externalize,
80 	.pot_equals	=	_prop_data_equals,
81 };
82 
83 #define	prop_object_is_data(x)		\
84 	((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
85 
86 /* ARGSUSED */
87 static _prop_object_free_rv_t
_prop_data_free(prop_stack_t stack,prop_object_t * obj)88 _prop_data_free(prop_stack_t stack, prop_object_t *obj)
89 {
90 	prop_data_t pd = *obj;
91 
92 	if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
93 	    	_PROP_FREE(pd->pd_mutable, M_PROP_DATA);
94 	_PROP_POOL_PUT(_prop_data_pool, pd);
95 
96 	return (_PROP_OBJECT_FREE_DONE);
97 }
98 
99 static const char _prop_data_base64[] =
100     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
101 static const char _prop_data_pad64 = '=';
102 
103 static bool
_prop_data_externalize(struct _prop_object_externalize_context * ctx,void * v)104 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
105 {
106 	prop_data_t pd = v;
107 	size_t i, srclen;
108 	const uint8_t *src;
109 	uint8_t output[4];
110 	uint8_t input[3];
111 
112 	if (pd->pd_size == 0)
113 		return (_prop_object_externalize_empty_tag(ctx, "data"));
114 
115 	if (_prop_object_externalize_start_tag(ctx, "data") == false)
116 		return (false);
117 
118 	for (src = pd->pd_immutable, srclen = pd->pd_size;
119 	     srclen > 2; srclen -= 3) {
120 		input[0] = *src++;
121 		input[1] = *src++;
122 		input[2] = *src++;
123 
124 		output[0] = (uint32_t)input[0] >> 2;
125 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
126 		    ((uint32_t)input[1] >> 4);
127 		output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
128 		    ((uint32_t)input[2] >> 6);
129 		output[3] = input[2] & 0x3f;
130 		_PROP_ASSERT(output[0] < 64);
131 		_PROP_ASSERT(output[1] < 64);
132 		_PROP_ASSERT(output[2] < 64);
133 		_PROP_ASSERT(output[3] < 64);
134 
135 		if (_prop_object_externalize_append_char(ctx,
136 				_prop_data_base64[output[0]]) == false ||
137 		    _prop_object_externalize_append_char(ctx,
138 		    		_prop_data_base64[output[1]]) == false ||
139 		    _prop_object_externalize_append_char(ctx,
140 		    		_prop_data_base64[output[2]]) == false ||
141 		    _prop_object_externalize_append_char(ctx,
142 		    		_prop_data_base64[output[3]]) == false)
143 			return (false);
144 	}
145 
146 	if (srclen != 0) {
147 		input[0] = input[1] = input[2] = '\0';
148 		for (i = 0; i < srclen; i++)
149 			input[i] = *src++;
150 
151 		output[0] = (uint32_t)input[0] >> 2;
152 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
153 		    ((uint32_t)input[1] >> 4);
154 		output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
155 		    ((uint32_t)input[2] >> 6);
156 		_PROP_ASSERT(output[0] < 64);
157 		_PROP_ASSERT(output[1] < 64);
158 		_PROP_ASSERT(output[2] < 64);
159 
160 		if (_prop_object_externalize_append_char(ctx,
161 				_prop_data_base64[output[0]]) == false ||
162 		    _prop_object_externalize_append_char(ctx,
163 		    		_prop_data_base64[output[1]]) == false ||
164 		    _prop_object_externalize_append_char(ctx,
165 		    		srclen == 1 ? _prop_data_pad64
166 				: _prop_data_base64[output[2]]) == false ||
167 		    _prop_object_externalize_append_char(ctx,
168 		    		_prop_data_pad64) == false)
169 			return (false);
170 	}
171 
172 	if (_prop_object_externalize_end_tag(ctx, "data") == false)
173 		return (false);
174 
175 	return (true);
176 }
177 
178 /* ARGSUSED */
179 static _prop_object_equals_rv_t
_prop_data_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)180 _prop_data_equals(prop_object_t v1, prop_object_t v2,
181     void **stored_pointer1, void **stored_pointer2,
182     prop_object_t *next_obj1, prop_object_t *next_obj2)
183 {
184 	prop_data_t pd1 = v1;
185 	prop_data_t pd2 = v2;
186 
187 	if (pd1 == pd2)
188 		return (_PROP_OBJECT_EQUALS_TRUE);
189 	if (pd1->pd_size != pd2->pd_size)
190 		return (_PROP_OBJECT_EQUALS_FALSE);
191 	if (pd1->pd_size == 0) {
192 		_PROP_ASSERT(pd1->pd_immutable == NULL);
193 		_PROP_ASSERT(pd2->pd_immutable == NULL);
194 		return (_PROP_OBJECT_EQUALS_TRUE);
195 	}
196 	if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
197 		return _PROP_OBJECT_EQUALS_TRUE;
198 	else
199 		return _PROP_OBJECT_EQUALS_FALSE;
200 }
201 
202 static prop_data_t
_prop_data_alloc(int const flags)203 _prop_data_alloc(int const flags)
204 {
205 	prop_data_t pd;
206 
207 	pd = _PROP_POOL_GET(_prop_data_pool);
208 	if (pd != NULL) {
209 		_prop_object_init(&pd->pd_obj, &_prop_object_type_data);
210 
211 		pd->pd_mutable = NULL;
212 		pd->pd_size = 0;
213 		pd->pd_flags = flags;
214 	}
215 
216 	return (pd);
217 }
218 
219 static prop_data_t
_prop_data_instantiate(int const flags,const void * const data,size_t const len)220 _prop_data_instantiate(int const flags, const void * const data,
221     size_t const len)
222 {
223 	prop_data_t pd;
224 
225 	pd = _prop_data_alloc(flags);
226 	if (pd != NULL) {
227 		pd->pd_immutable = data;
228 		pd->pd_size = len;
229 	}
230 
231 	return (pd);
232 }
233 
234 _PROP_DEPRECATED(prop_data_create_data,
235     "this program uses prop_data_create_data(); all functions "
236     "supporting mutable prop_data objects are deprecated.")
237 prop_data_t
prop_data_create_data(const void * v,size_t size)238 prop_data_create_data(const void *v, size_t size)
239 {
240 	prop_data_t pd;
241 	void *nv;
242 
243 	pd = _prop_data_alloc(PD_F_MUTABLE);
244 	if (pd != NULL && size != 0) {
245 		nv = _PROP_MALLOC(size, M_PROP_DATA);
246 		if (nv == NULL) {
247 			prop_object_release(pd);
248 			return (NULL);
249 		}
250 		memcpy(nv, v, size);
251 		pd->pd_mutable = nv;
252 		pd->pd_size = size;
253 	}
254 	return (pd);
255 }
256 
257 _PROP_DEPRECATED(prop_data_create_data_nocopy,
258     "this program uses prop_data_create_data_nocopy(), "
259     "which is deprecated; use prop_data_create_nocopy() instead.")
260 prop_data_t
prop_data_create_data_nocopy(const void * v,size_t size)261 prop_data_create_data_nocopy(const void *v, size_t size)
262 {
263 	return prop_data_create_nocopy(v, size);
264 }
265 
266 /*
267  * prop_data_create_copy --
268  *	Create a data object with a copy of the provided data.
269  */
270 prop_data_t
prop_data_create_copy(const void * v,size_t size)271 prop_data_create_copy(const void *v, size_t size)
272 {
273 	prop_data_t pd;
274 	void *nv;
275 
276 	/* Tolerate the creation of empty data objects. */
277 	if (v != NULL && size != 0) {
278 		nv = _PROP_MALLOC(size, M_PROP_DATA);
279 		if (nv == NULL)
280 			return (NULL);
281 
282 		memcpy(nv, v, size);
283 	} else {
284 		nv = NULL;
285 		size = 0;
286 	}
287 
288 	pd = _prop_data_instantiate(0, nv, size);
289 	if (pd == NULL && nv == NULL)
290 		_PROP_FREE(nv, M_PROP_DATA);
291 
292 	return (pd);
293 }
294 
295 /*
296  * prop_data_create_nocopy --
297  *	Create a data object using the provided external data reference.
298  */
299 prop_data_t
prop_data_create_nocopy(const void * v,size_t size)300 prop_data_create_nocopy(const void *v, size_t size)
301 {
302 
303 	/* Tolerate the creation of empty data objects. */
304 	if (v == NULL || size == 0) {
305 		v = NULL;
306 		size = 0;
307 	}
308 
309 	return _prop_data_instantiate(PD_F_NOCOPY, v, size);
310 }
311 
312 /*
313  * prop_data_copy --
314  *	Copy a data container.  If the original data is external, then
315  *	the copy is also references the same external data.
316  */
317 prop_data_t
prop_data_copy(prop_data_t opd)318 prop_data_copy(prop_data_t opd)
319 {
320 	prop_data_t pd;
321 
322 	if (! prop_object_is_data(opd))
323 		return (NULL);
324 
325 	if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
326 	    (opd->pd_flags & PD_F_MUTABLE) == 0) {
327 		/* Just retain and return the original. */
328 		prop_object_retain(opd);
329 		return (opd);
330 	}
331 
332 	pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
333 	if (pd != NULL) {
334 		/* Preserve deprecated mutability semantics. */
335 		pd->pd_flags |= PD_F_MUTABLE;
336 	}
337 
338 	return (pd);
339 }
340 
341 /*
342  * prop_data_size --
343  *	Return the size of the data.
344  */
345 size_t
prop_data_size(prop_data_t pd)346 prop_data_size(prop_data_t pd)
347 {
348 
349 	if (! prop_object_is_data(pd))
350 		return (0);
351 
352 	return (pd->pd_size);
353 }
354 
355 /*
356  * prop_data_value --
357  *	Returns a pointer to the data object's value.  This pointer
358  *	remains valid only as long as the data object.
359  */
360 const void *
prop_data_value(prop_data_t pd)361 prop_data_value(prop_data_t pd)
362 {
363 
364 	if (! prop_object_is_data(pd))
365 		return (0);
366 
367 	return (pd->pd_immutable);
368 }
369 
370 /*
371  * prop_data_copy_value --
372  *	Copy the data object's value into the supplied buffer.
373  */
374 bool
prop_data_copy_value(prop_data_t pd,void * buf,size_t buflen)375 prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
376 {
377 
378 	if (! prop_object_is_data(pd))
379 		return (false);
380 
381 	if (buf == NULL || buflen < pd->pd_size)
382 		return (false);
383 
384 	/* Tolerate empty data objects. */
385 	if (pd->pd_immutable == NULL || pd->pd_size == 0)
386 		return (false);
387 
388 	memcpy(buf, pd->pd_immutable, pd->pd_size);
389 
390 	return (true);
391 }
392 
393 _PROP_DEPRECATED(prop_data_data,
394     "this program uses prop_data_data(), "
395     "which is deprecated; use prop_data_copy_value() instead.")
396 void *
prop_data_data(prop_data_t pd)397 prop_data_data(prop_data_t pd)
398 {
399 	void *v;
400 
401 	if (! prop_object_is_data(pd))
402 		return (NULL);
403 
404 	if (pd->pd_size == 0) {
405 		_PROP_ASSERT(pd->pd_immutable == NULL);
406 		return (NULL);
407 	}
408 
409 	_PROP_ASSERT(pd->pd_immutable != NULL);
410 
411 	v = _PROP_MALLOC(pd->pd_size, M_TEMP);
412 	if (v != NULL)
413 		memcpy(v, pd->pd_immutable, pd->pd_size);
414 
415 	return (v);
416 }
417 
418 _PROP_DEPRECATED(prop_data_data_nocopy,
419     "this program uses prop_data_data_nocopy(), "
420     "which is deprecated; use prop_data_value() instead.")
421 const void *
prop_data_data_nocopy(prop_data_t pd)422 prop_data_data_nocopy(prop_data_t pd)
423 {
424 	return prop_data_value(pd);
425 }
426 
427 /*
428  * prop_data_equals --
429  *	Return true if two data objects are equivalent.
430  */
431 bool
prop_data_equals(prop_data_t pd1,prop_data_t pd2)432 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
433 {
434 	if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
435 		return (false);
436 
437 	return (prop_object_equals(pd1, pd2));
438 }
439 
440 /*
441  * prop_data_equals_data --
442  *	Return true if the contained data is equivalent to the specified
443  *	external data.
444  */
445 bool
prop_data_equals_data(prop_data_t pd,const void * v,size_t size)446 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
447 {
448 
449 	if (! prop_object_is_data(pd))
450 		return (false);
451 
452 	if (pd->pd_size != size || v == NULL)
453 		return (false);
454 
455 	return (memcmp(pd->pd_immutable, v, size) == 0);
456 }
457 
458 static bool
_prop_data_internalize_decode(struct _prop_object_internalize_context * ctx,uint8_t * target,size_t targsize,size_t * sizep,const char ** cpp)459 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
460 			     uint8_t *target, size_t targsize, size_t *sizep,
461 			     const char **cpp)
462 {
463 	const char *src;
464 	size_t tarindex;
465 	int state, ch;
466 	const char *pos;
467 
468 	state = 0;
469 	tarindex = 0;
470 	src = ctx->poic_cp;
471 
472 	for (;;) {
473 		ch = (unsigned char) *src++;
474 		if (_PROP_EOF(ch))
475 			return (false);
476 		if (_PROP_ISSPACE(ch))
477 			continue;
478 		if (ch == '<') {
479 			src--;
480 			break;
481 		}
482 		if (ch == _prop_data_pad64)
483 			break;
484 
485 		pos = strchr(_prop_data_base64, ch);
486 		if (pos == NULL)
487 			return (false);
488 
489 		switch (state) {
490 		case 0:
491 			if (target) {
492 				if (tarindex >= targsize)
493 					return (false);
494 				target[tarindex] =
495 				    (uint8_t)((pos - _prop_data_base64) << 2);
496 			}
497 			state = 1;
498 			break;
499 
500 		case 1:
501 			if (target) {
502 				if (tarindex + 1 >= targsize)
503 					return (false);
504 				target[tarindex] |=
505 				    (uint32_t)(pos - _prop_data_base64) >> 4;
506 				target[tarindex + 1] =
507 				    (uint8_t)(((pos - _prop_data_base64) & 0xf)
508 				        << 4);
509 			}
510 			tarindex++;
511 			state = 2;
512 			break;
513 
514 		case 2:
515 			if (target) {
516 				if (tarindex + 1 >= targsize)
517 					return (false);
518 				target[tarindex] |=
519 				    (uint32_t)(pos - _prop_data_base64) >> 2;
520 				target[tarindex + 1] =
521 				    (uint8_t)(((pos - _prop_data_base64)
522 				        & 0x3) << 6);
523 			}
524 			tarindex++;
525 			state = 3;
526 			break;
527 
528 		case 3:
529 			if (target) {
530 				if (tarindex >= targsize)
531 					return (false);
532 				target[tarindex] |= (uint8_t)
533 				    (pos - _prop_data_base64);
534 			}
535 			tarindex++;
536 			state = 0;
537 			break;
538 
539 		default:
540 			_PROP_ASSERT(/*CONSTCOND*/0);
541 		}
542 	}
543 
544 	/*
545 	 * We are done decoding the Base64 characters.  Let's see if we
546 	 * ended up on a byte boundary and/or with unrecognized trailing
547 	 * characters.
548 	 */
549 	if (ch == _prop_data_pad64) {
550 		ch = (unsigned char) *src;	/* src already advanced */
551 		if (_PROP_EOF(ch))
552 			return (false);
553 		switch (state) {
554 		case 0:		/* Invalid = in first position */
555 		case 1:		/* Invalid = in second position */
556 			return (false);
557 
558 		case 2:		/* Valid, one byte of info */
559 			/* Skip whitespace */
560 			for (ch = (unsigned char) *src++;
561 			     ch != '<'; ch = (unsigned char) *src++) {
562 				if (_PROP_EOF(ch))
563 					return (false);
564 				if (!_PROP_ISSPACE(ch))
565 					break;
566 			}
567 			/* Make sure there is another trailing = */
568 			if (ch != _prop_data_pad64)
569 				return (false);
570 			ch = (unsigned char) *src;
571 			/* FALLTHROUGH */
572 
573 		case 3:		/* Valid, two bytes of info */
574 			/*
575 			 * We know this char is a =.  Is there anything but
576 			 * whitespace after it?
577 			 */
578 			for (ch = (unsigned char) *src++;
579 			     ch != '<'; ch = (unsigned char) *src++) {
580 				if (_PROP_EOF(ch))
581 					return (false);
582 				if (!_PROP_ISSPACE(ch))
583 					return (false);
584 			}
585 			/* back up to '<' */
586 			src--;
587 		}
588 	} else {
589 		/*
590 		 * We ended by seeing the end of the Base64 string.  Make
591 		 * sure there are no partial bytes lying around.
592 		 */
593 		if (state != 0)
594 			return (false);
595 	}
596 
597 	_PROP_ASSERT(*src == '<');
598 	if (sizep != NULL)
599 		*sizep = tarindex;
600 	if (cpp != NULL)
601 		*cpp = src;
602 
603 	return (true);
604 }
605 
606 /*
607  * _prop_data_internalize --
608  *	Parse a <data>...</data> and return the object created from the
609  *	external representation.
610  */
611 
612 /* strtoul is used for parsing, enforce. */
613 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
614 
615 /* ARGSUSED */
616 bool
_prop_data_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)617 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
618     struct _prop_object_internalize_context *ctx)
619 {
620 	prop_data_t data;
621 	uint8_t *buf;
622 	size_t len, alen;
623 
624 	/*
625 	 * We don't accept empty elements.
626 	 * This actually only checks for the node to be <data/>
627 	 * (Which actually causes another error if found.)
628 	 */
629 	if (ctx->poic_is_empty_element)
630 		return (true);
631 
632 	/*
633 	 * If we got a "size" attribute, get the size of the data blob
634 	 * from that.  Otherwise, we have to figure it out from the base64.
635 	 */
636 	if (ctx->poic_tagattr != NULL) {
637 		char *cp;
638 
639 		if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
640 		    ctx->poic_tagattrval_len == 0)
641 			return (true);
642 
643 #ifndef _KERNEL
644 		errno = 0;
645 #endif
646 		len = strtoul(ctx->poic_tagattrval, &cp, 0);
647 #ifndef _KERNEL		/* XXX can't check for ERANGE in the kernel */
648 		if (len == ULONG_MAX && errno == ERANGE)
649 			return (true);
650 #endif
651 		if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
652 			return (true);
653 		_PROP_ASSERT(*cp == '\"');
654 	} else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
655 						NULL) == false)
656 		return (true);
657 
658 	/*
659 	 * Always allocate one extra in case we don't land on an even byte
660 	 * boundary during the decode.
661 	 */
662 	buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
663 	if (buf == NULL)
664 		return (true);
665 
666 	if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
667 					  &ctx->poic_cp) == false) {
668 		_PROP_FREE(buf, M_PROP_DATA);
669 		return (true);
670 	}
671 	if (alen != len) {
672 		_PROP_FREE(buf, M_PROP_DATA);
673 		return (true);
674 	}
675 
676 	if (_prop_object_internalize_find_tag(ctx, "data",
677 					      _PROP_TAG_TYPE_END) == false) {
678 		_PROP_FREE(buf, M_PROP_DATA);
679 		return (true);
680 	}
681 
682 	/*
683 	 * Handle alternate type of empty node.
684 	 * XML document could contain open/close tags, yet still be empty.
685 	 */
686 	if (alen == 0) {
687 		_PROP_FREE(buf, M_PROP_DATA);
688 		buf = NULL;
689 	}
690 
691 	data = _prop_data_instantiate(0, buf, len);
692 	if (data == NULL && buf != NULL)
693 		_PROP_FREE(buf, M_PROP_DATA);
694 
695 	*obj = data;
696 	return (true);
697 }
698