prop_data.c revision 1.24 1 /* $NetBSD: prop_data.c,v 1.24 2025/09/23 22:37:13 rillig 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, pos;
481 int state, ch;
482 const char *posptr;
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 posptr = strchr(_prop_data_base64, ch);
502 if (posptr == NULL)
503 return (false);
504 pos = posptr - _prop_data_base64;
505
506 switch (state) {
507 case 0:
508 if (target) {
509 if (tarindex >= targsize)
510 return (false);
511 target[tarindex] = (uint8_t)(pos << 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] |= (uint8_t)(pos >> 4);
521 target[tarindex + 1] = (pos & 0xf) << 4;
522 }
523 tarindex++;
524 state = 2;
525 break;
526
527 case 2:
528 if (target) {
529 if (tarindex + 1 >= targsize)
530 return (false);
531 target[tarindex] |= (uint8_t)(pos >> 2);
532 target[tarindex + 1] = (pos & 0x3) << 6;
533 }
534 tarindex++;
535 state = 3;
536 break;
537
538 case 3:
539 if (target) {
540 if (tarindex >= targsize)
541 return (false);
542 target[tarindex] |= (uint8_t)pos;
543 }
544 tarindex++;
545 state = 0;
546 break;
547
548 default:
549 _PROP_ASSERT(0);
550 }
551 }
552
553 /*
554 * We are done decoding the Base64 characters. Let's see if we
555 * ended up on a byte boundary and/or with unrecognized trailing
556 * characters.
557 */
558 if (ch == _prop_data_pad64) {
559 ch = (unsigned char) *src; /* src already advanced */
560 if (_PROP_EOF(ch))
561 return (false);
562 switch (state) {
563 case 0: /* Invalid = in first position */
564 case 1: /* Invalid = in second position */
565 return (false);
566
567 case 2: /* Valid, one byte of info */
568 /* Skip whitespace */
569 for (ch = (unsigned char) *src++;
570 ch != '<'; ch = (unsigned char) *src++) {
571 if (_PROP_EOF(ch))
572 return (false);
573 if (!_PROP_ISSPACE(ch))
574 break;
575 }
576 /* Make sure there is another trailing = */
577 if (ch != _prop_data_pad64)
578 return (false);
579 ch = (unsigned char) *src;
580 /* FALLTHROUGH */
581
582 case 3: /* Valid, two bytes of info */
583 /*
584 * We know this char is a =. Is there anything but
585 * whitespace after it?
586 */
587 for (ch = (unsigned char) *src++;
588 ch != '<'; ch = (unsigned char) *src++) {
589 if (_PROP_EOF(ch))
590 return (false);
591 if (!_PROP_ISSPACE(ch))
592 return (false);
593 }
594 /* back up to '<' */
595 src--;
596 }
597 } else {
598 /*
599 * We ended by seeing the end of the Base64 string. Make
600 * sure there are no partial bytes lying around.
601 */
602 if (state != 0)
603 return (false);
604 }
605
606 _PROP_ASSERT(*src == '<');
607 if (sizep != NULL)
608 *sizep = tarindex;
609 if (cpp != NULL)
610 *cpp = src;
611
612 return (true);
613 }
614
615 /*
616 * _prop_data_internalize --
617 * Parse a <data>...</data> and return the object created from the
618 * external representation.
619 */
620
621 /* strtoul is used for parsing, enforce. */
622 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
623
624 /* ARGSUSED */
625 bool
626 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
627 struct _prop_object_internalize_context *ctx)
628 {
629 prop_data_t data;
630 uint8_t *buf;
631 size_t len, alen;
632
633 /* No JSON binary data object representation. */
634 if (ctx->poic_format == PROP_FORMAT_JSON) {
635 return true;
636 }
637
638 /*
639 * We don't accept empty elements.
640 * This actually only checks for the node to be <data/>
641 * (Which actually causes another error if found.)
642 */
643 if (ctx->poic_is_empty_element)
644 return (true);
645
646 /*
647 * If we got a "size" attribute, get the size of the data blob
648 * from that. Otherwise, we have to figure it out from the base64.
649 */
650 if (ctx->poic_tagattr != NULL) {
651 char *cp;
652
653 if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
654 ctx->poic_tagattrval_len == 0)
655 return (true);
656
657 #ifndef _KERNEL
658 errno = 0;
659 #endif
660 len = strtoul(ctx->poic_tagattrval, &cp, 0);
661 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
662 if (len == ULONG_MAX && errno == ERANGE)
663 return (true);
664 #endif
665 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
666 return (true);
667 _PROP_ASSERT(*cp == '\"');
668 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
669 NULL) == false)
670 return (true);
671
672 /*
673 * Always allocate one extra in case we don't land on an even byte
674 * boundary during the decode.
675 */
676 buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
677 if (buf == NULL)
678 return (true);
679
680 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
681 &ctx->poic_cp) == false) {
682 _PROP_FREE(buf, M_PROP_DATA);
683 return (true);
684 }
685 if (alen != len) {
686 _PROP_FREE(buf, M_PROP_DATA);
687 return (true);
688 }
689
690 if (_prop_xml_intern_find_tag(ctx, "data",
691 _PROP_TAG_TYPE_END) == false) {
692 _PROP_FREE(buf, M_PROP_DATA);
693 return (true);
694 }
695
696 /*
697 * Handle alternate type of empty node.
698 * XML document could contain open/close tags, yet still be empty.
699 */
700 if (alen == 0) {
701 _PROP_FREE(buf, M_PROP_DATA);
702 buf = NULL;
703 }
704
705 data = _prop_data_instantiate(0, buf, len);
706 if (data == NULL && buf != NULL)
707 _PROP_FREE(buf, M_PROP_DATA);
708
709 *obj = data;
710 return (true);
711 }
712