@@ -170,9 +170,16 @@ typedef struct ConversionStatus {
170
170
static CborError value_to_json (FILE * out , CborValue * it , int flags , CborType type ,
171
171
int nestingLevel , ConversionStatus * status );
172
172
173
- static CborError dump_bytestring_base16 ( char * * result , CborValue * it )
173
+ static void append_hex ( void * buffer , uint8_t byte )
174
174
{
175
175
static const char characters [] = "0123456789abcdef" ;
176
+ char * str = buffer ;
177
+ str [0 ] = characters [byte >> 4 ];
178
+ str [1 ] = characters [byte & 0xf ];
179
+ }
180
+
181
+ static CborError dump_bytestring_base16 (char * * result , CborValue * it )
182
+ {
176
183
size_t i ;
177
184
size_t n = 0 ;
178
185
uint8_t * buffer ;
@@ -195,8 +202,7 @@ static CborError dump_bytestring_base16(char **result, CborValue *it)
195
202
196
203
for (i = 0 ; i < n ; ++ i ) {
197
204
uint8_t byte = buffer [n + i ];
198
- buffer [2 * i ] = characters [byte >> 4 ];
199
- buffer [2 * i + 1 ] = characters [byte & 0xf ];
205
+ append_hex (buffer + 2 * i , byte );
200
206
}
201
207
return CborNoError ;
202
208
}
@@ -293,6 +299,96 @@ static CborError dump_bytestring_base64url(char **result, CborValue *it)
293
299
return generic_dump_base64 (result , it , alphabet );
294
300
}
295
301
302
+ static CborError escape_text_string (char * * str , size_t * alloc , size_t * offsetp , const char * input , size_t len )
303
+ {
304
+ /* JSON requires escaping some characters in strings, so we iterate and
305
+ * escape as necessary
306
+ * https://www.rfc-editor.org/rfc/rfc8259#section-7:
307
+ * All Unicode characters may be placed within the
308
+ * quotation marks, except for the characters that MUST be escaped:
309
+ * quotation mark, reverse solidus, and the control characters (U+0000
310
+ * through U+001F).
311
+ * We additionally choose to escape BS, HT, CR, LF and FF.
312
+ */
313
+ char * buf = * str ;
314
+
315
+ /* Ensure we have enough space for this chunk. In the worst case, we
316
+ * have 6 escaped characters per input character.
317
+ *
318
+ * The overflow checking here is only practically useful for 32-bit
319
+ * machines, as SIZE_MAX/6 for a 64-bit machine is 2.6667 exabytes.
320
+ * That is much more than any current architecture can even address and
321
+ * cbor_value_get_text_string_chunk() only works for data already
322
+ * loaded into memory.
323
+ */
324
+ size_t needed ;
325
+ size_t offset = offsetp ? * offsetp : 0 ;
326
+ if (mul_check_overflow (len , 6 , & needed ) || add_check_overflow (needed , offset , & needed )
327
+ || add_check_overflow (needed , 1 , & needed )) {
328
+ return CborErrorDataTooLarge ;
329
+ }
330
+ if (!alloc || needed > * alloc ) {
331
+ buf = cbor_realloc (buf , needed );
332
+ if (!buf )
333
+ return CborErrorOutOfMemory ;
334
+ if (alloc )
335
+ * alloc = needed ;
336
+ }
337
+
338
+ for (size_t i = 0 ; i < len ; ++ i ) {
339
+ static const char escapeChars [] = "\b\t\n\r\f\"\\" ;
340
+ static const char escapedChars [] = "btnrf\"\\" ;
341
+ unsigned char c = input [i ];
342
+
343
+ char * esc = c > 0 ? strchr (escapeChars , c ) : NULL ;
344
+ if (esc ) {
345
+ buf [offset ++ ] = '\\' ;
346
+ buf [offset ++ ] = escapedChars [esc - escapeChars ];
347
+ } else if (c <= 0x1F ) {
348
+ buf [offset ++ ] = '\\' ;
349
+ buf [offset ++ ] = 'u' ;
350
+ buf [offset ++ ] = '0' ;
351
+ buf [offset ++ ] = '0' ;
352
+ append_hex (buf + offset , c );
353
+ offset += 2 ;
354
+ } else {
355
+ buf [offset ++ ] = c ;
356
+ }
357
+ }
358
+ buf [offset ] = '\0' ;
359
+ * str = buf ;
360
+ if (offsetp )
361
+ * offsetp = offset ;
362
+ return CborNoError ;
363
+ }
364
+
365
+ static CborError text_string_to_escaped (char * * str , CborValue * it )
366
+ {
367
+ size_t alloc = 0 , offset = 0 ;
368
+ CborError err ;
369
+
370
+ * str = NULL ;
371
+ err = cbor_value_begin_string_iteration (it );
372
+ while (err == CborNoError ) {
373
+ const char * chunk ;
374
+ size_t len ;
375
+ err = cbor_value_get_text_string_chunk (it , & chunk , & len , it );
376
+ if (err == CborNoError )
377
+ err = escape_text_string (str , & alloc , & offset , chunk , len );
378
+ }
379
+
380
+ if (likely (err == CborErrorNoMoreStringChunks )) {
381
+ /* success */
382
+ if (!* str )
383
+ * str = strdup ("" ); // wasteful, but very atypical
384
+ return cbor_value_finish_string_iteration (it );
385
+ }
386
+
387
+ cbor_free (* str );
388
+ * str = NULL ;
389
+ return err ;
390
+ }
391
+
296
392
static CborError add_value_metadata (FILE * out , CborType type , const ConversionStatus * status )
297
393
{
298
394
int flags = status -> flags ;
@@ -420,14 +516,20 @@ static CborError stringify_map_key(char **key, CborValue *it, int flags, CborTyp
420
516
return CborErrorJsonNotImplemented ;
421
517
#else
422
518
size_t size ;
519
+ char * stringified ;
423
520
424
- FILE * memstream = open_memstream (key , & size );
521
+ FILE * memstream = open_memstream (& stringified , & size );
425
522
if (memstream == NULL )
426
523
return CborErrorOutOfMemory ; /* could also be EMFILE, but it's unlikely */
427
524
CborError err = cbor_value_to_pretty_advance (memstream , it );
428
525
429
- if (unlikely (fclose (memstream ) < 0 || * key == NULL ))
526
+ if (unlikely (fclose (memstream ) < 0 || stringified == NULL ))
430
527
return CborErrorInternalError ;
528
+ if (err == CborNoError ) {
529
+ /* escape the stringified CBOR stream */
530
+ err = escape_text_string (key , NULL , NULL , stringified , size );
531
+ }
532
+ cbor_free (stringified );
431
533
return err ;
432
534
#endif
433
535
}
@@ -452,15 +554,14 @@ static CborError map_to_json(FILE *out, CborValue *it, int flags, int nestingLev
452
554
const char * comma = "" ;
453
555
CborError err ;
454
556
while (!cbor_value_at_end (it )) {
455
- char * key ;
557
+ char * key = NULL ;
456
558
if (fprintf (out , "%s" , comma ) < 0 )
457
559
return CborErrorIO ;
458
560
comma = "," ;
459
561
460
562
CborType keyType = cbor_value_get_type (it );
461
563
if (likely (keyType == CborTextStringType )) {
462
- size_t n = 0 ;
463
- err = cbor_value_dup_text_string (it , & key , & n , it );
564
+ err = text_string_to_escaped (& key , it );
464
565
} else if (flags & CborConvertStringifyMapKeys ) {
465
566
err = stringify_map_key (& key , it , flags , keyType );
466
567
} else {
@@ -570,8 +671,7 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
570
671
err = dump_bytestring_base64url (& str , it );
571
672
status -> flags = TypeWasNotNative ;
572
673
} else {
573
- size_t n = 0 ;
574
- err = cbor_value_dup_text_string (it , & str , & n , it );
674
+ err = text_string_to_escaped (& str , it );
575
675
}
576
676
if (err )
577
677
return err ;
0 commit comments