1 /*        $NetBSD: prop_data.c,v 1.20 2025/04/26 17:13:23 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
_prop_data_free(prop_stack_t stack,prop_object_t * obj)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
_prop_data_externalize(struct _prop_object_externalize_context * ctx,void * v)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_object_externalize_empty_tag(ctx,
127                         &_prop_data_type_tags));
128 
129           if (_prop_object_externalize_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_object_externalize_append_char(ctx,
151                                         _prop_data_base64[output[0]]) == false ||
152                         _prop_object_externalize_append_char(ctx,
153                                         _prop_data_base64[output[1]]) == false ||
154                         _prop_object_externalize_append_char(ctx,
155                                         _prop_data_base64[output[2]]) == false ||
156                         _prop_object_externalize_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_object_externalize_append_char(ctx,
176                                         _prop_data_base64[output[0]]) == false ||
177                         _prop_object_externalize_append_char(ctx,
178                                         _prop_data_base64[output[1]]) == false ||
179                         _prop_object_externalize_append_char(ctx,
180                                         srclen == 1 ? _prop_data_pad64
181                                         : _prop_data_base64[output[2]]) == false ||
182                         _prop_object_externalize_append_char(ctx,
183                                         _prop_data_pad64) == false)
184                               return (false);
185           }
186 
187           if (_prop_object_externalize_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
_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)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
_prop_data_alloc(int const flags)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
_prop_data_instantiate(int const flags,const void * const data,size_t const len)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
prop_data_create_data(const void * v,size_t size)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
prop_data_create_data_nocopy(const void * v,size_t size)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
prop_data_create_copy(const void * v,size_t size)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
prop_data_create_nocopy(const void * v,size_t size)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
prop_data_copy(prop_data_t opd)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
prop_data_size(prop_data_t pd)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 *
prop_data_value(prop_data_t pd)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
prop_data_copy_value(prop_data_t pd,void * buf,size_t buflen)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 *
prop_data_data(prop_data_t pd)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 *
prop_data_data_nocopy(prop_data_t pd)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
prop_data_equals(prop_data_t pd1,prop_data_t pd2)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
prop_data_equals_data(prop_data_t pd,const void * v,size_t size)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
_prop_data_internalize_decode(struct _prop_object_internalize_context * ctx,uint8_t * target,size_t targsize,size_t * sizep,const char ** cpp)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
_prop_data_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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_object_internalize_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