Home | History | Annotate | Line # | Download | only in libprop
prop_data.c revision 1.16
      1 /*	$NetBSD: prop_data.c,v 1.16 2020/06/06 21:25:59 thorpej 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
     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
    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
    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
    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
    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
    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
    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
    271 prop_data_create_copy(const void *v, size_t size)
    272 {
    273 	prop_data_t pd;
    274 	void *nv;
    275 
    276 	if (v == NULL || size == 0)
    277 		return (NULL);
    278 
    279 	/* Tolerate the creation of empty data objects. */
    280 	if (v != NULL && size != 0) {
    281 		nv = _PROP_MALLOC(size, M_PROP_DATA);
    282 		if (nv == NULL)
    283 			return (NULL);
    284 
    285 		memcpy(nv, v, size);
    286 	} else {
    287 		nv = NULL;
    288 		size = 0;
    289 	}
    290 
    291 	pd = _prop_data_instantiate(0, nv, size);
    292 	if (pd == NULL && nv == NULL)
    293 		_PROP_FREE(nv, M_PROP_DATA);
    294 
    295 	return (pd);
    296 }
    297 
    298 /*
    299  * prop_data_create_nocopy --
    300  *	Create a data object using the provided external data reference.
    301  */
    302 prop_data_t
    303 prop_data_create_nocopy(const void *v, size_t size)
    304 {
    305 
    306 	/* Tolerate the creation of empty data objects. */
    307 	if (v == NULL || size == 0) {
    308 		v = NULL;
    309 		size = 0;
    310 	}
    311 
    312 	return _prop_data_instantiate(PD_F_NOCOPY, v, size);
    313 }
    314 
    315 /*
    316  * prop_data_copy --
    317  *	Copy a data container.  If the original data is external, then
    318  *	the copy is also references the same external data.
    319  */
    320 prop_data_t
    321 prop_data_copy(prop_data_t opd)
    322 {
    323 	prop_data_t pd;
    324 
    325 	if (! prop_object_is_data(opd))
    326 		return (NULL);
    327 
    328 	if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
    329 	    (opd->pd_flags & PD_F_MUTABLE) == 0) {
    330 		/* Just retain and return the original. */
    331 		prop_object_retain(opd);
    332 		return (opd);
    333 	}
    334 
    335 	pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
    336 	if (pd != NULL) {
    337 		/* Preserve deprecated mutability semantics. */
    338 		pd->pd_flags |= PD_F_MUTABLE;
    339 	}
    340 
    341 	return (pd);
    342 }
    343 
    344 /*
    345  * prop_data_size --
    346  *	Return the size of the data.
    347  */
    348 size_t
    349 prop_data_size(prop_data_t pd)
    350 {
    351 
    352 	if (! prop_object_is_data(pd))
    353 		return (0);
    354 
    355 	return (pd->pd_size);
    356 }
    357 
    358 /*
    359  * prop_data_value --
    360  *	Returns a pointer to the data object's value.  This pointer
    361  *	remains valid only as long as the data object.
    362  */
    363 const void *
    364 prop_data_value(prop_data_t pd)
    365 {
    366 
    367 	if (! prop_object_is_data(pd))
    368 		return (0);
    369 
    370 	return (pd->pd_immutable);
    371 }
    372 
    373 /*
    374  * prop_data_copy_value --
    375  *	Copy the data object's value into the supplied buffer.
    376  */
    377 bool
    378 prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
    379 {
    380 
    381 	if (! prop_object_is_data(pd))
    382 		return (false);
    383 
    384 	if (buf == NULL || buflen < pd->pd_size)
    385 		return (false);
    386 
    387 	/* Tolerate empty data objects. */
    388 	if (pd->pd_immutable == NULL || pd->pd_size == 0)
    389 		return (false);
    390 
    391 	memcpy(buf, pd->pd_immutable, pd->pd_size);
    392 
    393 	return (true);
    394 }
    395 
    396 _PROP_DEPRECATED(prop_data_data,
    397     "this program uses prop_data_data(), "
    398     "which is deprecated; use prop_data_copy_value() instead.")
    399 void *
    400 prop_data_data(prop_data_t pd)
    401 {
    402 	void *v;
    403 
    404 	if (! prop_object_is_data(pd))
    405 		return (NULL);
    406 
    407 	if (pd->pd_size == 0) {
    408 		_PROP_ASSERT(pd->pd_immutable == NULL);
    409 		return (NULL);
    410 	}
    411 
    412 	_PROP_ASSERT(pd->pd_immutable != NULL);
    413 
    414 	v = _PROP_MALLOC(pd->pd_size, M_TEMP);
    415 	if (v != NULL)
    416 		memcpy(v, pd->pd_immutable, pd->pd_size);
    417 
    418 	return (v);
    419 }
    420 
    421 _PROP_DEPRECATED(prop_data_data_nocopy,
    422     "this program uses prop_data_data_nocopy(), "
    423     "which is deprecated; use prop_data_value() instead.")
    424 const void *
    425 prop_data_data_nocopy(prop_data_t pd)
    426 {
    427 	return prop_data_value(pd);
    428 }
    429 
    430 /*
    431  * prop_data_equals --
    432  *	Return true if two data objects are equivalent.
    433  */
    434 bool
    435 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
    436 {
    437 	if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
    438 		return (false);
    439 
    440 	return (prop_object_equals(pd1, pd2));
    441 }
    442 
    443 /*
    444  * prop_data_equals_data --
    445  *	Return true if the contained data is equivalent to the specified
    446  *	external data.
    447  */
    448 bool
    449 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
    450 {
    451 
    452 	if (! prop_object_is_data(pd))
    453 		return (false);
    454 
    455 	if (pd->pd_size != size || v == NULL)
    456 		return (false);
    457 
    458 	return (memcmp(pd->pd_immutable, v, size) == 0);
    459 }
    460 
    461 static bool
    462 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
    463 			     uint8_t *target, size_t targsize, size_t *sizep,
    464 			     const char **cpp)
    465 {
    466 	const char *src;
    467 	size_t tarindex;
    468 	int state, ch;
    469 	const char *pos;
    470 
    471 	state = 0;
    472 	tarindex = 0;
    473 	src = ctx->poic_cp;
    474 
    475 	for (;;) {
    476 		ch = (unsigned char) *src++;
    477 		if (_PROP_EOF(ch))
    478 			return (false);
    479 		if (_PROP_ISSPACE(ch))
    480 			continue;
    481 		if (ch == '<') {
    482 			src--;
    483 			break;
    484 		}
    485 		if (ch == _prop_data_pad64)
    486 			break;
    487 
    488 		pos = strchr(_prop_data_base64, ch);
    489 		if (pos == NULL)
    490 			return (false);
    491 
    492 		switch (state) {
    493 		case 0:
    494 			if (target) {
    495 				if (tarindex >= targsize)
    496 					return (false);
    497 				target[tarindex] =
    498 				    (uint8_t)((pos - _prop_data_base64) << 2);
    499 			}
    500 			state = 1;
    501 			break;
    502 
    503 		case 1:
    504 			if (target) {
    505 				if (tarindex + 1 >= targsize)
    506 					return (false);
    507 				target[tarindex] |=
    508 				    (uint32_t)(pos - _prop_data_base64) >> 4;
    509 				target[tarindex + 1] =
    510 				    (uint8_t)(((pos - _prop_data_base64) & 0xf)
    511 				        << 4);
    512 			}
    513 			tarindex++;
    514 			state = 2;
    515 			break;
    516 
    517 		case 2:
    518 			if (target) {
    519 				if (tarindex + 1 >= targsize)
    520 					return (false);
    521 				target[tarindex] |=
    522 				    (uint32_t)(pos - _prop_data_base64) >> 2;
    523 				target[tarindex + 1] =
    524 				    (uint8_t)(((pos - _prop_data_base64)
    525 				        & 0x3) << 6);
    526 			}
    527 			tarindex++;
    528 			state = 3;
    529 			break;
    530 
    531 		case 3:
    532 			if (target) {
    533 				if (tarindex >= targsize)
    534 					return (false);
    535 				target[tarindex] |= (uint8_t)
    536 				    (pos - _prop_data_base64);
    537 			}
    538 			tarindex++;
    539 			state = 0;
    540 			break;
    541 
    542 		default:
    543 			_PROP_ASSERT(/*CONSTCOND*/0);
    544 		}
    545 	}
    546 
    547 	/*
    548 	 * We are done decoding the Base64 characters.  Let's see if we
    549 	 * ended up on a byte boundary and/or with unrecognized trailing
    550 	 * characters.
    551 	 */
    552 	if (ch == _prop_data_pad64) {
    553 		ch = (unsigned char) *src;	/* src already advanced */
    554 		if (_PROP_EOF(ch))
    555 			return (false);
    556 		switch (state) {
    557 		case 0:		/* Invalid = in first position */
    558 		case 1:		/* Invalid = in second position */
    559 			return (false);
    560 
    561 		case 2:		/* Valid, one byte of info */
    562 			/* Skip whitespace */
    563 			for (ch = (unsigned char) *src++;
    564 			     ch != '<'; ch = (unsigned char) *src++) {
    565 				if (_PROP_EOF(ch))
    566 					return (false);
    567 				if (!_PROP_ISSPACE(ch))
    568 					break;
    569 			}
    570 			/* Make sure there is another trailing = */
    571 			if (ch != _prop_data_pad64)
    572 				return (false);
    573 			ch = (unsigned char) *src;
    574 			/* FALLTHROUGH */
    575 
    576 		case 3:		/* Valid, two bytes of info */
    577 			/*
    578 			 * We know this char is a =.  Is there anything but
    579 			 * whitespace after it?
    580 			 */
    581 			for (ch = (unsigned char) *src++;
    582 			     ch != '<'; ch = (unsigned char) *src++) {
    583 				if (_PROP_EOF(ch))
    584 					return (false);
    585 				if (!_PROP_ISSPACE(ch))
    586 					return (false);
    587 			}
    588 			/* back up to '<' */
    589 			src--;
    590 		}
    591 	} else {
    592 		/*
    593 		 * We ended by seeing the end of the Base64 string.  Make
    594 		 * sure there are no partial bytes lying around.
    595 		 */
    596 		if (state != 0)
    597 			return (false);
    598 	}
    599 
    600 	_PROP_ASSERT(*src == '<');
    601 	if (sizep != NULL)
    602 		*sizep = tarindex;
    603 	if (cpp != NULL)
    604 		*cpp = src;
    605 
    606 	return (true);
    607 }
    608 
    609 /*
    610  * _prop_data_internalize --
    611  *	Parse a <data>...</data> and return the object created from the
    612  *	external representation.
    613  */
    614 
    615 /* strtoul is used for parsing, enforce. */
    616 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
    617 
    618 /* ARGSUSED */
    619 bool
    620 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
    621     struct _prop_object_internalize_context *ctx)
    622 {
    623 	prop_data_t data;
    624 	uint8_t *buf;
    625 	size_t len, alen;
    626 
    627 	/*
    628 	 * We don't accept empty elements.
    629 	 * This actually only checks for the node to be <data/>
    630 	 * (Which actually causes another error if found.)
    631 	 */
    632 	if (ctx->poic_is_empty_element)
    633 		return (true);
    634 
    635 	/*
    636 	 * If we got a "size" attribute, get the size of the data blob
    637 	 * from that.  Otherwise, we have to figure it out from the base64.
    638 	 */
    639 	if (ctx->poic_tagattr != NULL) {
    640 		char *cp;
    641 
    642 		if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
    643 		    ctx->poic_tagattrval_len == 0)
    644 			return (true);
    645 
    646 #ifndef _KERNEL
    647 		errno = 0;
    648 #endif
    649 		len = strtoul(ctx->poic_tagattrval, &cp, 0);
    650 #ifndef _KERNEL		/* XXX can't check for ERANGE in the kernel */
    651 		if (len == ULONG_MAX && errno == ERANGE)
    652 			return (true);
    653 #endif
    654 		if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
    655 			return (true);
    656 		_PROP_ASSERT(*cp == '\"');
    657 	} else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
    658 						NULL) == false)
    659 		return (true);
    660 
    661 	/*
    662 	 * Always allocate one extra in case we don't land on an even byte
    663 	 * boundary during the decode.
    664 	 */
    665 	buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
    666 	if (buf == NULL)
    667 		return (true);
    668 
    669 	if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
    670 					  &ctx->poic_cp) == false) {
    671 		_PROP_FREE(buf, M_PROP_DATA);
    672 		return (true);
    673 	}
    674 	if (alen != len) {
    675 		_PROP_FREE(buf, M_PROP_DATA);
    676 		return (true);
    677 	}
    678 
    679 	if (_prop_object_internalize_find_tag(ctx, "data",
    680 					      _PROP_TAG_TYPE_END) == false) {
    681 		_PROP_FREE(buf, M_PROP_DATA);
    682 		return (true);
    683 	}
    684 
    685 	/*
    686 	 * Handle alternate type of empty node.
    687 	 * XML document could contain open/close tags, yet still be empty.
    688 	 */
    689 	if (alen == 0) {
    690 		_PROP_FREE(buf, M_PROP_DATA);
    691 		buf = NULL;
    692 	}
    693 
    694 	data = _prop_data_instantiate(0, buf, len);
    695 	if (data == NULL && buf != NULL)
    696 		_PROP_FREE(buf, M_PROP_DATA);
    697 
    698 	*obj = data;
    699 	return (true);
    700 }
    701