xref: /openbmc/libcper/cper-parse.c (revision 3e72806517cfd324023e6e6268000eb5dc783678)
1 /**
2  * Describes high level functions for converting an entire CPER log, and functions for parsing
3  * CPER headers and section descriptions into an intermediate JSON format.
4  *
5  * Author: Lawrence.Tang@arm.com
6  **/
7 
8 #include <limits.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <json.h>
12 
13 #include <libcper/base64.h>
14 #include <libcper/Cper.h>
15 #include <libcper/log.h>
16 #include <libcper/cper-parse.h>
17 #include <libcper/cper-parse-str.h>
18 #include <libcper/cper-utils.h>
19 #include <libcper/sections/cper-section.h>
20 
21 //Private pre-definitions.
22 json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header);
23 json_object *
24 cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor);
25 
26 json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
27 				    EFI_ERROR_SECTION_DESCRIPTOR *descriptor);
28 
header_signature_valid(EFI_COMMON_ERROR_RECORD_HEADER * header)29 static int header_signature_valid(EFI_COMMON_ERROR_RECORD_HEADER *header)
30 {
31 	if (header->SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
32 		cper_print_log(
33 			"Invalid CPER file: Invalid header (incorrect signature). %x\n",
34 			header->SignatureStart);
35 		return 0;
36 	}
37 	if (header->SignatureEnd != EFI_ERROR_RECORD_SIGNATURE_END) {
38 		cper_print_log(
39 			"Invalid CPER file: Invalid header (incorrect signature end). %x\n",
40 			header->SignatureEnd);
41 		return 0;
42 	}
43 	if (header->SectionCount == 0) {
44 		cper_print_log(
45 			"Invalid CPER file: Invalid section count (0).\n");
46 		return 0;
47 	}
48 	return 1;
49 }
50 
header_valid(const char * cper_buf,size_t size)51 int header_valid(const char *cper_buf, size_t size)
52 {
53 	if (size < sizeof(EFI_COMMON_ERROR_RECORD_HEADER)) {
54 		return 0;
55 	}
56 	EFI_COMMON_ERROR_RECORD_HEADER *header =
57 		(EFI_COMMON_ERROR_RECORD_HEADER *)cper_buf;
58 	if (!header_signature_valid(header)) {
59 		return 0;
60 	}
61 	return header_signature_valid(header);
62 }
63 
cper_buf_to_ir(const unsigned char * cper_buf,size_t size)64 json_object *cper_buf_to_ir(const unsigned char *cper_buf, size_t size)
65 {
66 	json_object *parent = NULL;
67 	json_object *header_ir = NULL;
68 	json_object *section_descriptors_ir = NULL;
69 	json_object *sections_ir = NULL;
70 
71 	const unsigned char *pos = cper_buf;
72 	unsigned int remaining = size;
73 	if (size < sizeof(EFI_COMMON_ERROR_RECORD_HEADER)) {
74 		goto fail;
75 	}
76 	EFI_COMMON_ERROR_RECORD_HEADER *header =
77 		(EFI_COMMON_ERROR_RECORD_HEADER *)cper_buf;
78 	if (!header_signature_valid(header)) {
79 		goto fail;
80 	}
81 	pos += sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
82 	remaining -= sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
83 
84 	if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
85 		cper_print_log(
86 			"Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
87 		goto fail;
88 	}
89 
90 	//Create the header JSON object from the read bytes.
91 	parent = json_object_new_object();
92 	header_ir = cper_header_to_ir(header);
93 
94 	json_object_object_add(parent, "header", header_ir);
95 
96 	//Read the appropriate number of section descriptors & sections, and convert them into IR format.
97 	section_descriptors_ir = json_object_new_array();
98 	sections_ir = json_object_new_array();
99 	for (int i = 0; i < header->SectionCount; i++) {
100 		//Create the section descriptor.
101 		if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
102 			cper_print_log(
103 				"Invalid number of section headers: Header states %d sections, could not read section %d.\n",
104 				header->SectionCount, i + 1);
105 			goto fail;
106 		}
107 
108 		EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
109 		section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)(pos);
110 		pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
111 		remaining -= sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
112 
113 		if (section_descriptor->SectionOffset > size) {
114 			cper_print_log(
115 				"Invalid section descriptor: Section offset > size.\n");
116 			goto fail;
117 		}
118 
119 		if (section_descriptor->SectionLength <= 0) {
120 			cper_print_log(
121 				"Invalid section descriptor: Section length <= 0.\n");
122 			goto fail;
123 		}
124 
125 		if (section_descriptor->SectionOffset >
126 		    UINT_MAX - section_descriptor->SectionLength) {
127 			cper_print_log(
128 				"Invalid section descriptor: Section offset + length would overflow.\n");
129 			goto fail;
130 		}
131 
132 		if (section_descriptor->SectionOffset +
133 			    section_descriptor->SectionLength >
134 		    size) {
135 			cper_print_log(
136 				"Invalid section descriptor: Section offset + length > size.\n");
137 			goto fail;
138 		}
139 
140 		const unsigned char *section_begin =
141 			cper_buf + section_descriptor->SectionOffset;
142 		json_object *section_descriptor_ir =
143 			cper_section_descriptor_to_ir(section_descriptor);
144 
145 		json_object_array_add(section_descriptors_ir,
146 				      section_descriptor_ir);
147 
148 		//Read the section itself.
149 		json_object *section_ir = cper_buf_section_to_ir(
150 			section_begin, section_descriptor->SectionLength,
151 			section_descriptor);
152 		json_object_array_add(sections_ir, section_ir);
153 	}
154 
155 	//Add the header, section descriptors, and sections to a parent object.
156 	json_object_object_add(parent, "sectionDescriptors",
157 			       section_descriptors_ir);
158 	json_object_object_add(parent, "sections", sections_ir);
159 
160 	return parent;
161 
162 fail:
163 	json_object_put(sections_ir);
164 	json_object_put(section_descriptors_ir);
165 	json_object_put(parent);
166 	cper_print_log("Failed to parse CPER file.\n");
167 	return NULL;
168 }
169 
170 //Reads a CPER log file at the given file location, and returns an intermediate
171 //JSON representation of this CPER record.
cper_to_ir(FILE * cper_file)172 json_object *cper_to_ir(FILE *cper_file)
173 {
174 	//Ensure this is really a CPER log.
175 	EFI_COMMON_ERROR_RECORD_HEADER header;
176 	if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
177 		  cper_file) != 1) {
178 		cper_print_log(
179 			"Invalid CPER file: Invalid length (log too short).\n");
180 		return NULL;
181 	}
182 
183 	//Check if the header contains the magic bytes ("CPER").
184 	if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
185 		cper_print_log(
186 			"Invalid CPER file: Invalid header (incorrect signature).\n");
187 		return NULL;
188 	}
189 	fseek(cper_file, -sizeof(EFI_COMMON_ERROR_RECORD_HEADER), SEEK_CUR);
190 	unsigned char *cper_buf = malloc(header.RecordLength);
191 	int bytes_read = fread(cper_buf, 1, header.RecordLength, cper_file);
192 	if (bytes_read < 0) {
193 		cper_print_log("File read failed with code %u\n", bytes_read);
194 		free(cper_buf);
195 		return NULL;
196 	}
197 	if ((UINT32)bytes_read != header.RecordLength) {
198 		int position = ftell(cper_file);
199 		cper_print_log(
200 			"File read failed file was %u bytes, expecting %u bytes from header.\n",
201 			position, header.RecordLength);
202 		free(cper_buf);
203 		return NULL;
204 	}
205 
206 	json_object *ir = cper_buf_to_ir(cper_buf, bytes_read);
207 	free(cper_buf);
208 	return ir;
209 }
210 
cper_to_str_ir(FILE * cper_file)211 char *cper_to_str_ir(FILE *cper_file)
212 {
213 	json_object *jobj = cper_to_ir(cper_file);
214 	char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
215 
216 	json_object_put(jobj);
217 	return str;
218 }
219 
cperbuf_to_str_ir(const unsigned char * cper,size_t size)220 char *cperbuf_to_str_ir(const unsigned char *cper, size_t size)
221 {
222 	FILE *cper_file = fmemopen((void *)cper, size, "r");
223 
224 	return cper_file ? cper_to_str_ir(cper_file) : NULL;
225 }
226 
227 //Converts a parsed CPER record header into intermediate JSON object format.
cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER * header)228 json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
229 {
230 	json_object *header_ir = json_object_new_object();
231 
232 	//Revision/version information.
233 	json_object_object_add(header_ir, "revision",
234 			       revision_to_ir(header->Revision));
235 
236 	//Section count.
237 	json_object_object_add(header_ir, "sectionCount",
238 			       json_object_new_int(header->SectionCount));
239 
240 	//Error severity (with interpreted string version).
241 	json_object *error_severity = json_object_new_object();
242 	json_object_object_add(error_severity, "code",
243 			       json_object_new_uint64(header->ErrorSeverity));
244 	json_object_object_add(error_severity, "name",
245 			       json_object_new_string(severity_to_string(
246 				       header->ErrorSeverity)));
247 	json_object_object_add(header_ir, "severity", error_severity);
248 
249 	//Total length of the record (including headers) in bytes.
250 	json_object_object_add(header_ir, "recordLength",
251 			       json_object_new_uint64(header->RecordLength));
252 
253 	//If a timestamp exists according to validation bits, then add it.
254 	if (header->ValidationBits & 0x2) {
255 		char timestamp_string[TIMESTAMP_LENGTH];
256 		if (timestamp_to_string(timestamp_string, TIMESTAMP_LENGTH,
257 					&header->TimeStamp) >= 0) {
258 			json_object_object_add(
259 				header_ir, "timestamp",
260 				json_object_new_string(timestamp_string));
261 
262 			json_object_object_add(header_ir, "timestampIsPrecise",
263 					       json_object_new_boolean(
264 						       header->TimeStamp.Flag));
265 		}
266 	}
267 
268 	//If a platform ID exists according to the validation bits, then add it.
269 	if (header->ValidationBits & 0x1) {
270 		add_guid(header_ir, "platformID", &header->PlatformID);
271 	}
272 
273 	//If a partition ID exists according to the validation bits, then add it.
274 	if (header->ValidationBits & 0x4) {
275 		add_guid(header_ir, "partitionID", &header->PartitionID);
276 	}
277 
278 	//Creator ID of the header.
279 	add_guid(header_ir, "creatorID", &header->CreatorID);
280 	//Notification type for the header. Some defined types are available.
281 	json_object *notification_type = json_object_new_object();
282 	add_guid(notification_type, "guid", &header->NotificationType);
283 
284 	//Add the human readable notification type if possible.
285 	const char *notification_type_readable = "Unknown";
286 
287 	EFI_GUID *guids[] = {
288 		&gEfiEventNotificationTypeCmcGuid,
289 		&gEfiEventNotificationTypeCpeGuid,
290 		&gEfiEventNotificationTypeMceGuid,
291 		&gEfiEventNotificationTypePcieGuid,
292 		&gEfiEventNotificationTypeInitGuid,
293 		&gEfiEventNotificationTypeNmiGuid,
294 		&gEfiEventNotificationTypeBootGuid,
295 		&gEfiEventNotificationTypeDmarGuid,
296 		&gEfiEventNotificationTypeSeaGuid,
297 		&gEfiEventNotificationTypeSeiGuid,
298 		&gEfiEventNotificationTypePeiGuid,
299 		&gEfiEventNotificationTypeCxlGuid,
300 	};
301 
302 	const char *readable_names[] = {
303 		"CMC",	"CPE",	"MCE", "PCIe", "INIT", "NMI",
304 		"Boot", "DMAr", "SEA", "SEI",  "PEI",  "CXL Component"
305 	};
306 
307 	int index = select_guid_from_list(&header->NotificationType, guids,
308 					  sizeof(guids) / sizeof(EFI_GUID *));
309 	if (index < (int)(sizeof(readable_names) / sizeof(char *))) {
310 		notification_type_readable = readable_names[index];
311 	}
312 
313 	json_object_object_add(
314 		notification_type, "type",
315 		json_object_new_string(notification_type_readable));
316 	json_object_object_add(header_ir, "notificationType",
317 			       notification_type);
318 
319 	//The record ID for this record, unique on a given system.
320 	json_object_object_add(header_ir, "recordID",
321 			       json_object_new_uint64(header->RecordID));
322 
323 	//Flag for the record, and a human readable form.
324 	json_object *flags = integer_to_readable_pair(
325 		header->Flags,
326 		sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
327 		CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
328 		"Unknown");
329 	json_object_object_add(header_ir, "flags", flags);
330 
331 	//Persistence information. Outside the scope of specification, so just a uint32 here.
332 	json_object_object_add(header_ir, "persistenceInfo",
333 			       json_object_new_uint64(header->PersistenceInfo));
334 	return header_ir;
335 }
336 
337 //Converts the given EFI section descriptor into JSON IR format.
338 json_object *
cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR * section_descriptor)339 cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
340 {
341 	json_object *section_descriptor_ir = json_object_new_object();
342 
343 	//The offset of the section from the base of the record header, length.
344 	json_object_object_add(
345 		section_descriptor_ir, "sectionOffset",
346 		json_object_new_uint64(section_descriptor->SectionOffset));
347 	json_object_object_add(
348 		section_descriptor_ir, "sectionLength",
349 		json_object_new_uint64(section_descriptor->SectionLength));
350 
351 	//Revision.
352 	json_object_object_add(section_descriptor_ir, "revision",
353 			       revision_to_ir(section_descriptor->Revision));
354 
355 	//Flag bits.
356 	json_object *flags =
357 		bitfield_to_ir(section_descriptor->SectionFlags, 8,
358 			       CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
359 	json_object_object_add(section_descriptor_ir, "flags", flags);
360 
361 	//Section type (GUID).
362 	json_object *section_type = json_object_new_object();
363 
364 	add_guid(section_type, "data", &section_descriptor->SectionType);
365 	//Readable section type, if possible.
366 	const char *section_type_readable = "Unknown";
367 
368 	CPER_SECTION_DEFINITION *section =
369 		select_section_by_guid(&section_descriptor->SectionType);
370 	if (section != NULL) {
371 		section_type_readable = section->ReadableName;
372 	}
373 
374 	json_object_object_add(section_type, "type",
375 			       json_object_new_string(section_type_readable));
376 	json_object_object_add(section_descriptor_ir, "sectionType",
377 			       section_type);
378 
379 	//If validation bits indicate it exists, add FRU ID.
380 	if (section_descriptor->SecValidMask & 0x1) {
381 		add_guid(section_descriptor_ir, "fruID",
382 			 &section_descriptor->FruId);
383 	}
384 
385 	//If validation bits indicate it exists, add FRU text.
386 	if ((section_descriptor->SecValidMask & 0x2) >> 1) {
387 		add_untrusted_string(section_descriptor_ir, "fruText",
388 				     section_descriptor->FruString,
389 				     sizeof(section_descriptor->FruString));
390 	}
391 
392 	//Section severity.
393 	json_object *section_severity = json_object_new_object();
394 	json_object_object_add(
395 		section_severity, "code",
396 		json_object_new_uint64(section_descriptor->Severity));
397 	json_object_object_add(section_severity, "name",
398 			       json_object_new_string(severity_to_string(
399 				       section_descriptor->Severity)));
400 	json_object_object_add(section_descriptor_ir, "severity",
401 			       section_severity);
402 
403 	return section_descriptor_ir;
404 }
405 
read_section(const unsigned char * cper_section_buf,size_t size,CPER_SECTION_DEFINITION * definition)406 json_object *read_section(const unsigned char *cper_section_buf, size_t size,
407 			  CPER_SECTION_DEFINITION *definition)
408 {
409 	if (definition->ToIR == NULL) {
410 		return NULL;
411 	}
412 	json_object *section_ir = definition->ToIR(cper_section_buf, size);
413 	if (section_ir == NULL) {
414 		return NULL;
415 	}
416 	json_object *result = json_object_new_object();
417 	json_object_object_add(result, definition->ShortName, section_ir);
418 	return result;
419 }
420 
select_section_by_guid(EFI_GUID * guid)421 CPER_SECTION_DEFINITION *select_section_by_guid(EFI_GUID *guid)
422 {
423 	size_t i = 0;
424 	for (; i < section_definitions_len; i++) {
425 		if (guid_equal(guid, section_definitions[i].Guid)) {
426 			break;
427 		}
428 	}
429 	// It's unlikely fuzzing can reliably come up with a correct guid, given how
430 	// much entropy there is.  If we're in fuzzing mode, and if we haven't found
431 	// a match, try to force a match so we get some coverage.  Note, we still
432 	// want coverage of the section failed to convert code, so treat index ==
433 	// size as section failed to convert.
434 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
435 	if (i == section_definitions_len) {
436 		i = guid->Data1 % (section_definitions_len + 1);
437 	}
438 #endif
439 	if (i < section_definitions_len) {
440 		return &section_definitions[i];
441 	}
442 
443 	return NULL;
444 }
445 
446 //Converts the section described by a single given section descriptor.
cper_buf_section_to_ir(const void * cper_section_buf,size_t size,EFI_ERROR_SECTION_DESCRIPTOR * descriptor)447 json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
448 				    EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
449 {
450 	if (descriptor->SectionLength > size) {
451 		cper_print_log(
452 			"Invalid CPER file: Invalid header (incorrect signature).\n");
453 		return NULL;
454 	}
455 
456 	//Parse section to IR based on GUID.
457 	json_object *result = NULL;
458 	json_object *section_ir = NULL;
459 
460 	CPER_SECTION_DEFINITION *section =
461 		select_section_by_guid(&descriptor->SectionType);
462 	if (section == NULL) {
463 		cper_print_log("Unknown section type guid\n");
464 	} else {
465 		result = read_section(cper_section_buf, size, section);
466 	}
467 
468 	//Was it an unknown GUID/failed read?
469 	if (result == NULL) {
470 		//Output the data as formatted base64.
471 		int32_t encoded_len = 0;
472 		char *encoded = base64_encode(cper_section_buf,
473 					      descriptor->SectionLength,
474 					      &encoded_len);
475 		if (encoded == NULL) {
476 			cper_print_log(
477 				"Failed to allocate encode output buffer. \n");
478 		} else {
479 			section_ir = json_object_new_object();
480 			json_object_object_add(section_ir, "data",
481 					       json_object_new_string_len(
482 						       encoded, encoded_len));
483 			free(encoded);
484 
485 			result = json_object_new_object();
486 			json_object_object_add(result, "Unknown", section_ir);
487 		}
488 	}
489 	if (result == NULL) {
490 		cper_print_log("RETURNING NULL!! !!\n");
491 	}
492 	return result;
493 }
494 
cper_buf_single_section_to_ir(const unsigned char * cper_buf,size_t size)495 json_object *cper_buf_single_section_to_ir(const unsigned char *cper_buf,
496 					   size_t size)
497 {
498 	const unsigned char *cper_end;
499 	const unsigned char *section_begin;
500 	json_object *ir;
501 
502 	cper_end = cper_buf + size;
503 
504 	//Read the section descriptor out.
505 	EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
506 	if (sizeof(EFI_ERROR_SECTION_DESCRIPTOR) > size) {
507 		cper_print_log(
508 			"Size of cper buffer was too small to read section descriptor %zu\n",
509 			size);
510 		return NULL;
511 	}
512 
513 	ir = json_object_new_object();
514 	section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)cper_buf;
515 	//Convert the section descriptor to IR.
516 	json_object *section_descriptor_ir =
517 		cper_section_descriptor_to_ir(section_descriptor);
518 	json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
519 	section_begin = cper_buf + section_descriptor->SectionOffset;
520 
521 	if (section_begin + section_descriptor->SectionLength >= cper_end) {
522 		json_object_put(ir);
523 		//cper_print_log("Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
524 		return NULL;
525 	}
526 
527 	const unsigned char *section =
528 		cper_buf + section_descriptor->SectionOffset;
529 
530 	//Parse the single section.
531 	json_object *section_ir = cper_buf_section_to_ir(
532 		section, section_descriptor->SectionLength, section_descriptor);
533 	if (section_ir == NULL) {
534 		cper_print_log("RETURNING NULL2!! !!\n");
535 	}
536 	json_object_object_add(ir, "section", section_ir);
537 	return ir;
538 }
539 
540 //Converts a single CPER section, without a header but with a section descriptor, to JSON.
cper_single_section_to_ir(FILE * cper_section_file)541 json_object *cper_single_section_to_ir(FILE *cper_section_file)
542 {
543 	json_object *ir = json_object_new_object();
544 
545 	//Read the current file pointer location as base record position.
546 	long base_pos = ftell(cper_section_file);
547 
548 	//Read the section descriptor out.
549 	EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
550 	if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
551 		  cper_section_file) != 1) {
552 		cper_print_log(
553 			"Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
554 		json_object_put(ir);
555 		return NULL;
556 	}
557 
558 	//Convert the section descriptor to IR.
559 	json_object *section_descriptor_ir =
560 		cper_section_descriptor_to_ir(&section_descriptor);
561 	json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
562 
563 	//Save our current position in the stream.
564 	long position = ftell(cper_section_file);
565 
566 	//Read section as described by the section descriptor.
567 	fseek(cper_section_file, base_pos + section_descriptor.SectionOffset,
568 	      SEEK_SET);
569 	void *section = malloc(section_descriptor.SectionLength);
570 	if (fread(section, section_descriptor.SectionLength, 1,
571 		  cper_section_file) != 1) {
572 		cper_print_log(
573 			"Section read failed: Could not read %u bytes from global offset %d.\n",
574 			section_descriptor.SectionLength,
575 			section_descriptor.SectionOffset);
576 		json_object_put(ir);
577 		free(section);
578 		return NULL;
579 	}
580 
581 	//Seek back to our original position.
582 	fseek(cper_section_file, position, SEEK_SET);
583 
584 	//Parse the single section.
585 	json_object *section_ir = cper_buf_section_to_ir(
586 		section, section_descriptor.SectionLength, &section_descriptor);
587 	json_object_object_add(ir, "section", section_ir);
588 	free(section);
589 	return ir;
590 }
591 
cperbuf_single_section_to_str_ir(const unsigned char * cper_section,size_t size)592 char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section,
593 				       size_t size)
594 {
595 	json_object *jobj = cper_buf_single_section_to_ir(cper_section, size);
596 	char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
597 
598 	json_object_put(jobj);
599 	return str;
600 }
601