Home | History | Annotate | Line # | Download | only in libprop
      1 /*	$NetBSD: prop_data.c,v 1.23 2025/05/14 03:25:45 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006, 2020, 2025 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 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
     63 		    "property data container object")
     64 
     65 static const struct _prop_object_type_tags _prop_data_type_tags = {
     66 	.xml_tag		=	"data",
     67 };
     68 
     69 static _prop_object_free_rv_t
     70 		_prop_data_free(prop_stack_t, prop_object_t *);
     71 static bool	_prop_data_externalize(
     72 				struct _prop_object_externalize_context *,
     73 				void *);
     74 static _prop_object_equals_rv_t
     75 		_prop_data_equals(prop_object_t, prop_object_t,
     76 				  void **, void **,
     77 				  prop_object_t *, prop_object_t *);
     78 
     79 static const struct _prop_object_type _prop_object_type_data = {
     80 	.pot_type	=	PROP_TYPE_DATA,
     81 	.pot_free	=	_prop_data_free,
     82 	.pot_extern	=	_prop_data_externalize,
     83 	.pot_equals	=	_prop_data_equals,
     84 };
     85 
     86 #define	prop_object_is_data(x)		\
     87 	((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
     88 
     89 /* ARGSUSED */
     90 static _prop_object_free_rv_t
     91 _prop_data_free(prop_stack_t stack, prop_object_t *obj)
     92 {
     93 	prop_data_t pd = *obj;
     94 
     95 	if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
     96 	    	_PROP_FREE(pd->pd_mutable, M_PROP_DATA);
     97 	_PROP_POOL_PUT(_prop_data_pool, pd);
     98 
     99 	return (_PROP_OBJECT_FREE_DONE);
    100 }
    101 
    102 static const char _prop_data_base64[] =
    103     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    104 static const char _prop_data_pad64 = '=';
    105 
    106 static bool
    107 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
    108 {
    109 	prop_data_t pd = v;
    110 	size_t i, srclen;
    111 	const uint8_t *src;
    112 	uint8_t output[4];
    113 	uint8_t input[3];
    114 
    115 	_PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML ||
    116 		     ctx->poec_format == PROP_FORMAT_JSON);
    117 
    118 	/*
    119 	 * JSON does not have a syntax for serialized binary data.
    120 	 */
    121 	if (ctx->poec_format == PROP_FORMAT_JSON) {
    122 		return false;
    123 	}
    124 
    125 	if (pd->pd_size == 0)
    126 		return (_prop_extern_append_empty_tag(ctx,
    127 		    &_prop_data_type_tags));
    128 
    129 	if (_prop_extern_append_start_tag(ctx,
    130 				&_prop_data_type_tags, NULL) == false)
    131 		return (false);
    132 
    133 	for (src = pd->pd_immutable, srclen = pd->pd_size;
    134 	     srclen > 2; srclen -= 3) {
    135 		input[0] = *src++;
    136 		input[1] = *src++;
    137 		input[2] = *src++;
    138 
    139 		output[0] = (uint32_t)input[0] >> 2;
    140 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
    141 		    ((uint32_t)input[1] >> 4);
    142 		output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
    143 		    ((uint32_t)input[2] >> 6);
    144 		output[3] = input[2] & 0x3f;
    145 		_PROP_ASSERT(output[0] < 64);
    146 		_PROP_ASSERT(output[1] < 64);
    147 		_PROP_ASSERT(output[2] < 64);
    148 		_PROP_ASSERT(output[3] < 64);
    149 
    150 		if (_prop_extern_append_char(ctx,
    151 				_prop_data_base64[output[0]]) == false ||
    152 		    _prop_extern_append_char(ctx,
    153 		    		_prop_data_base64[output[1]]) == false ||
    154 		    _prop_extern_append_char(ctx,
    155 		    		_prop_data_base64[output[2]]) == false ||
    156 		    _prop_extern_append_char(ctx,
    157 		    		_prop_data_base64[output[3]]) == false)
    158 			return (false);
    159 	}
    160 
    161 	if (srclen != 0) {
    162 		input[0] = input[1] = input[2] = '\0';
    163 		for (i = 0; i < srclen; i++)
    164 			input[i] = *src++;
    165 
    166 		output[0] = (uint32_t)input[0] >> 2;
    167 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
    168 		    ((uint32_t)input[1] >> 4);
    169 		output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
    170 		    ((uint32_t)input[2] >> 6);
    171 		_PROP_ASSERT(output[0] < 64);
    172 		_PROP_ASSERT(output[1] < 64);
    173 		_PROP_ASSERT(output[2] < 64);
    174 
    175 		if (_prop_extern_append_char(ctx,
    176 				_prop_data_base64[output[0]]) == false ||
    177 		    _prop_extern_append_char(ctx,
    178 		    		_prop_data_base64[output[1]]) == false ||
    179 		    _prop_extern_append_char(ctx,
    180 		    		srclen == 1 ? _prop_data_pad64
    181 				: _prop_data_base64[output[2]]) == false ||
    182 		    _prop_extern_append_char(ctx,
    183 		    		_prop_data_pad64) == false)
    184 			return (false);
    185 	}
    186 
    187 	if (_prop_extern_append_end_tag(ctx,
    188 					&_prop_data_type_tags) == false)
    189 		return (false);
    190 
    191 	return (true);
    192 }
    193 
    194 /* ARGSUSED */
    195 static _prop_object_equals_rv_t
    196 _prop_data_equals(prop_object_t v1, prop_object_t v2,
    197     void **stored_pointer1, void **stored_pointer2,
    198     prop_object_t *next_obj1, prop_object_t *next_obj2)
    199 {
    200 	prop_data_t pd1 = v1;
    201 	prop_data_t pd2 = v2;
    202 
    203 	if (pd1 == pd2)
    204 		return (_PROP_OBJECT_EQUALS_TRUE);
    205 	if (pd1->pd_size != pd2->pd_size)
    206 		return (_PROP_OBJECT_EQUALS_FALSE);
    207 	if (pd1->pd_size == 0) {
    208 		_PROP_ASSERT(pd1->pd_immutable == NULL);
    209 		_PROP_ASSERT(pd2->pd_immutable == NULL);
    210 		return (_PROP_OBJECT_EQUALS_TRUE);
    211 	}
    212 	if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
    213 		return _PROP_OBJECT_EQUALS_TRUE;
    214 	else
    215 		return _PROP_OBJECT_EQUALS_FALSE;
    216 }
    217 
    218 static prop_data_t
    219 _prop_data_alloc(int const flags)
    220 {
    221 	prop_data_t pd;
    222 
    223 	pd = _PROP_POOL_GET(_prop_data_pool);
    224 	if (pd != NULL) {
    225 		_prop_object_init(&pd->pd_obj, &_prop_object_type_data);
    226 
    227 		pd->pd_mutable = NULL;
    228 		pd->pd_size = 0;
    229 		pd->pd_flags = flags;
    230 	}
    231 
    232 	return (pd);
    233 }
    234 
    235 static prop_data_t
    236 _prop_data_instantiate(int const flags, const void * const data,
    237     size_t const len)
    238 {
    239 	prop_data_t pd;
    240 
    241 	pd = _prop_data_alloc(flags);
    242 	if (pd != NULL) {
    243 		pd->pd_immutable = data;
    244 		pd->pd_size = len;
    245 	}
    246 
    247 	return (pd);
    248 }
    249 
    250 _PROP_DEPRECATED(prop_data_create_data,
    251     "this program uses prop_data_create_data(); all functions "
    252     "supporting mutable prop_data objects are deprecated.")
    253 _PROP_EXPORT prop_data_t
    254 prop_data_create_data(const void *v, size_t size)
    255 {
    256 	prop_data_t pd;
    257 	void *nv;
    258 
    259 	pd = _prop_data_alloc(PD_F_MUTABLE);
    260 	if (pd != NULL && size != 0) {
    261 		nv = _PROP_MALLOC(size, M_PROP_DATA);
    262 		if (nv == NULL) {
    263 			prop_object_release(pd);
    264 			return (NULL);
    265 		}
    266 		memcpy(nv, v, size);
    267 		pd->pd_mutable = nv;
    268 		pd->pd_size = size;
    269 	}
    270 	return (pd);
    271 }
    272 
    273 _PROP_DEPRECATED(prop_data_create_data_nocopy,
    274     "this program uses prop_data_create_data_nocopy(), "
    275     "which is deprecated; use prop_data_create_nocopy() instead.")
    276 _PROP_EXPORT prop_data_t
    277 prop_data_create_data_nocopy(const void *v, size_t size)
    278 {
    279 	return prop_data_create_nocopy(v, size);
    280 }
    281 
    282 /*
    283  * prop_data_create_copy --
    284  *	Create a data object with a copy of the provided data.
    285  */
    286 _PROP_EXPORT prop_data_t
    287 prop_data_create_copy(const void *v, size_t size)
    288 {
    289 	prop_data_t pd;
    290 	void *nv;
    291 
    292 	/* Tolerate the creation of empty data objects. */
    293 	if (v != NULL && size != 0) {
    294 		nv = _PROP_MALLOC(size, M_PROP_DATA);
    295 		if (nv == NULL)
    296 			return (NULL);
    297 
    298 		memcpy(nv, v, size);
    299 	} else {
    300 		nv = NULL;
    301 		size = 0;
    302 	}
    303 
    304 	pd = _prop_data_instantiate(0, nv, size);
    305 	if (pd == NULL && nv == NULL)
    306 		_PROP_FREE(nv, M_PROP_DATA);
    307 
    308 	return (pd);
    309 }
    310 
    311 /*
    312  * prop_data_create_nocopy --
    313  *	Create a data object using the provided external data reference.
    314  */
    315 _PROP_EXPORT prop_data_t
    316 prop_data_create_nocopy(const void *v, size_t size)
    317 {
    318 
    319 	/* Tolerate the creation of empty data objects. */
    320 	if (v == NULL || size == 0) {
    321 		v = NULL;
    322 		size = 0;
    323 	}
    324 
    325 	return _prop_data_instantiate(PD_F_NOCOPY, v, size);
    326 }
    327 
    328 /*
    329  * prop_data_copy --
    330  *	Copy a data container.  If the original data is external, then
    331  *	the copy is also references the same external data.
    332  */
    333 _PROP_EXPORT prop_data_t
    334 prop_data_copy(prop_data_t opd)
    335 {
    336 	prop_data_t pd;
    337 
    338 	if (! prop_object_is_data(opd))
    339 		return (NULL);
    340 
    341 	if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
    342 	    (opd->pd_flags & PD_F_MUTABLE) == 0) {
    343 		/* Just retain and return the original. */
    344 		prop_object_retain(opd);
    345 		return (opd);
    346 	}
    347 
    348 	pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
    349 	if (pd != NULL) {
    350 		/* Preserve deprecated mutability semantics. */
    351 		pd->pd_flags |= PD_F_MUTABLE;
    352 	}
    353 
    354 	return (pd);
    355 }
    356 
    357 /*
    358  * prop_data_size --
    359  *	Return the size of the data.
    360  */
    361 _PROP_EXPORT size_t
    362 prop_data_size(prop_data_t pd)
    363 {
    364 
    365 	if (! prop_object_is_data(pd))
    366 		return (0);
    367 
    368 	return (pd->pd_size);
    369 }
    370 
    371 /*
    372  * prop_data_value --
    373  *	Returns a pointer to the data object's value.  This pointer
    374  *	remains valid only as long as the data object.
    375  */
    376 _PROP_EXPORT const void *
    377 prop_data_value(prop_data_t pd)
    378 {
    379 
    380 	if (! prop_object_is_data(pd))
    381 		return (0);
    382 
    383 	return (pd->pd_immutable);
    384 }
    385 
    386 /*
    387  * prop_data_copy_value --
    388  *	Copy the data object's value into the supplied buffer.
    389  */
    390 _PROP_EXPORT bool
    391 prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
    392 {
    393 
    394 	if (! prop_object_is_data(pd))
    395 		return (false);
    396 
    397 	if (buf == NULL || buflen < pd->pd_size)
    398 		return (false);
    399 
    400 	/* Tolerate empty data objects. */
    401 	if (pd->pd_immutable == NULL || pd->pd_size == 0)
    402 		return (false);
    403 
    404 	memcpy(buf, pd->pd_immutable, pd->pd_size);
    405 
    406 	return (true);
    407 }
    408 
    409 _PROP_DEPRECATED(prop_data_data,
    410     "this program uses prop_data_data(), "
    411     "which is deprecated; use prop_data_copy_value() instead.")
    412 _PROP_EXPORT void *
    413 prop_data_data(prop_data_t pd)
    414 {
    415 	void *v;
    416 
    417 	if (! prop_object_is_data(pd))
    418 		return (NULL);
    419 
    420 	if (pd->pd_size == 0) {
    421 		_PROP_ASSERT(pd->pd_immutable == NULL);
    422 		return (NULL);
    423 	}
    424 
    425 	_PROP_ASSERT(pd->pd_immutable != NULL);
    426 
    427 	v = _PROP_MALLOC(pd->pd_size, M_TEMP);
    428 	if (v != NULL)
    429 		memcpy(v, pd->pd_immutable, pd->pd_size);
    430 
    431 	return (v);
    432 }
    433 
    434 _PROP_DEPRECATED(prop_data_data_nocopy,
    435     "this program uses prop_data_data_nocopy(), "
    436     "which is deprecated; use prop_data_value() instead.")
    437 _PROP_EXPORT const void *
    438 prop_data_data_nocopy(prop_data_t pd)
    439 {
    440 	return prop_data_value(pd);
    441 }
    442 
    443 /*
    444  * prop_data_equals --
    445  *	Return true if two data objects are equivalent.
    446  */
    447 _PROP_EXPORT bool
    448 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
    449 {
    450 	if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
    451 		return (false);
    452 
    453 	return (prop_object_equals(pd1, pd2));
    454 }
    455 
    456 /*
    457  * prop_data_equals_data --
    458  *	Return true if the contained data is equivalent to the specified
    459  *	external data.
    460  */
    461 _PROP_EXPORT bool
    462 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
    463 {
    464 
    465 	if (! prop_object_is_data(pd))
    466 		return (false);
    467 
    468 	if (pd->pd_size != size || v == NULL)
    469 		return (false);
    470 
    471 	return (memcmp(pd->pd_immutable, v, size) == 0);
    472 }
    473 
    474 static bool
    475 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
    476 			     uint8_t *target, size_t targsize, size_t *sizep,
    477 			     const char **cpp)
    478 {
    479 	const char *src;
    480 	size_t tarindex;
    481 	int state, ch;
    482 	const char *pos;
    483 
    484 	state = 0;
    485 	tarindex = 0;
    486 	src = ctx->poic_cp;
    487 
    488 	for (;;) {
    489 		ch = (unsigned char) *src++;
    490 		if (_PROP_EOF(ch))
    491 			return (false);
    492 		if (_PROP_ISSPACE(ch))
    493 			continue;
    494 		if (ch == '<') {
    495 			src--;
    496 			break;
    497 		}
    498 		if (ch == _prop_data_pad64)
    499 			break;
    500 
    501 		pos = strchr(_prop_data_base64, ch);
    502 		if (pos == NULL)
    503 			return (false);
    504 
    505 		switch (state) {
    506 		case 0:
    507 			if (target) {
    508 				if (tarindex >= targsize)
    509 					return (false);
    510 				target[tarindex] =
    511 				    (uint8_t)((pos - _prop_data_base64) << 2);
    512 			}
    513 			state = 1;
    514 			break;
    515 
    516 		case 1:
    517 			if (target) {
    518 				if (tarindex + 1 >= targsize)
    519 					return (false);
    520 				target[tarindex] |=
    521 				    (uint32_t)(pos - _prop_data_base64) >> 4;
    522 				target[tarindex + 1] =
    523 				    (uint8_t)(((pos - _prop_data_base64) & 0xf)
    524 				        << 4);
    525 			}
    526 			tarindex++;
    527 			state = 2;
    528 			break;
    529 
    530 		case 2:
    531 			if (target) {
    532 				if (tarindex + 1 >= targsize)
    533 					return (false);
    534 				target[tarindex] |=
    535 				    (uint32_t)(pos - _prop_data_base64) >> 2;
    536 				target[tarindex + 1] =
    537 				    (uint8_t)(((pos - _prop_data_base64)
    538 				        & 0x3) << 6);
    539 			}
    540 			tarindex++;
    541 			state = 3;
    542 			break;
    543 
    544 		case 3:
    545 			if (target) {
    546 				if (tarindex >= targsize)
    547 					return (false);
    548 				target[tarindex] |= (uint8_t)
    549 				    (pos - _prop_data_base64);
    550 			}
    551 			tarindex++;
    552 			state = 0;
    553 			break;
    554 
    555 		default:
    556 			_PROP_ASSERT(/*CONSTCOND*/0);
    557 		}
    558 	}
    559 
    560 	/*
    561 	 * We are done decoding the Base64 characters.  Let's see if we
    562 	 * ended up on a byte boundary and/or with unrecognized trailing
    563 	 * characters.
    564 	 */
    565 	if (ch == _prop_data_pad64) {
    566 		ch = (unsigned char) *src;	/* src already advanced */
    567 		if (_PROP_EOF(ch))
    568 			return (false);
    569 		switch (state) {
    570 		case 0:		/* Invalid = in first position */
    571 		case 1:		/* Invalid = in second position */
    572 			return (false);
    573 
    574 		case 2:		/* Valid, one byte of info */
    575 			/* Skip whitespace */
    576 			for (ch = (unsigned char) *src++;
    577 			     ch != '<'; ch = (unsigned char) *src++) {
    578 				if (_PROP_EOF(ch))
    579 					return (false);
    580 				if (!_PROP_ISSPACE(ch))
    581 					break;
    582 			}
    583 			/* Make sure there is another trailing = */
    584 			if (ch != _prop_data_pad64)
    585 				return (false);
    586 			ch = (unsigned char) *src;
    587 			/* FALLTHROUGH */
    588 
    589 		case 3:		/* Valid, two bytes of info */
    590 			/*
    591 			 * We know this char is a =.  Is there anything but
    592 			 * whitespace after it?
    593 			 */
    594 			for (ch = (unsigned char) *src++;
    595 			     ch != '<'; ch = (unsigned char) *src++) {
    596 				if (_PROP_EOF(ch))
    597 					return (false);
    598 				if (!_PROP_ISSPACE(ch))
    599 					return (false);
    600 			}
    601 			/* back up to '<' */
    602 			src--;
    603 		}
    604 	} else {
    605 		/*
    606 		 * We ended by seeing the end of the Base64 string.  Make
    607 		 * sure there are no partial bytes lying around.
    608 		 */
    609 		if (state != 0)
    610 			return (false);
    611 	}
    612 
    613 	_PROP_ASSERT(*src == '<');
    614 	if (sizep != NULL)
    615 		*sizep = tarindex;
    616 	if (cpp != NULL)
    617 		*cpp = src;
    618 
    619 	return (true);
    620 }
    621 
    622 /*
    623  * _prop_data_internalize --
    624  *	Parse a <data>...</data> and return the object created from the
    625  *	external representation.
    626  */
    627 
    628 /* strtoul is used for parsing, enforce. */
    629 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
    630 
    631 /* ARGSUSED */
    632 bool
    633 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
    634     struct _prop_object_internalize_context *ctx)
    635 {
    636 	prop_data_t data;
    637 	uint8_t *buf;
    638 	size_t len, alen;
    639 
    640 	/* No JSON binary data object representation. */
    641 	if (ctx->poic_format == PROP_FORMAT_JSON) {
    642 		return true;
    643 	}
    644 
    645 	/*
    646 	 * We don't accept empty elements.
    647 	 * This actually only checks for the node to be <data/>
    648 	 * (Which actually causes another error if found.)
    649 	 */
    650 	if (ctx->poic_is_empty_element)
    651 		return (true);
    652 
    653 	/*
    654 	 * If we got a "size" attribute, get the size of the data blob
    655 	 * from that.  Otherwise, we have to figure it out from the base64.
    656 	 */
    657 	if (ctx->poic_tagattr != NULL) {
    658 		char *cp;
    659 
    660 		if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
    661 		    ctx->poic_tagattrval_len == 0)
    662 			return (true);
    663 
    664 #ifndef _KERNEL
    665 		errno = 0;
    666 #endif
    667 		len = strtoul(ctx->poic_tagattrval, &cp, 0);
    668 #ifndef _KERNEL		/* XXX can't check for ERANGE in the kernel */
    669 		if (len == ULONG_MAX && errno == ERANGE)
    670 			return (true);
    671 #endif
    672 		if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
    673 			return (true);
    674 		_PROP_ASSERT(*cp == '\"');
    675 	} else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
    676 						NULL) == false)
    677 		return (true);
    678 
    679 	/*
    680 	 * Always allocate one extra in case we don't land on an even byte
    681 	 * boundary during the decode.
    682 	 */
    683 	buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
    684 	if (buf == NULL)
    685 		return (true);
    686 
    687 	if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
    688 					  &ctx->poic_cp) == false) {
    689 		_PROP_FREE(buf, M_PROP_DATA);
    690 		return (true);
    691 	}
    692 	if (alen != len) {
    693 		_PROP_FREE(buf, M_PROP_DATA);
    694 		return (true);
    695 	}
    696 
    697 	if (_prop_xml_intern_find_tag(ctx, "data",
    698 				      _PROP_TAG_TYPE_END) == false) {
    699 		_PROP_FREE(buf, M_PROP_DATA);
    700 		return (true);
    701 	}
    702 
    703 	/*
    704 	 * Handle alternate type of empty node.
    705 	 * XML document could contain open/close tags, yet still be empty.
    706 	 */
    707 	if (alen == 0) {
    708 		_PROP_FREE(buf, M_PROP_DATA);
    709 		buf = NULL;
    710 	}
    711 
    712 	data = _prop_data_instantiate(0, buf, len);
    713 	if (data == NULL && buf != NULL)
    714 		_PROP_FREE(buf, M_PROP_DATA);
    715 
    716 	*obj = data;
    717 	return (true);
    718 }
    719