oss-fuzz/projects/libyaml/libyaml_emitter_fuzzer.c

299 lines
9.8 KiB
C

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "yaml.h"
#include "yaml_write_handler.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef NDEBUG
#undef NDEBUG
#endif
#define MAX_EVENTS 1024
bool events_equal(yaml_event_t *event1, yaml_event_t *event2) {
const bool equal = true;
if (event1->type != event2->type)
return equal;
switch (event1->type) {
case YAML_STREAM_START_EVENT:
return !equal;
case YAML_DOCUMENT_START_EVENT:
if ((event1->data.document_start.version_directive &&
!event2->data.document_start.version_directive) ||
(!event1->data.document_start.version_directive &&
event2->data.document_start.version_directive) ||
(event1->data.document_start.version_directive &&
event2->data.document_start.version_directive &&
(event1->data.document_start.version_directive->major !=
event2->data.document_start.version_directive->major ||
event1->data.document_start.version_directive->minor !=
event2->data.document_start.version_directive->minor)))
return equal;
if ((event1->data.document_start.tag_directives.end -
event1->data.document_start.tag_directives.start) !=
(event2->data.document_start.tag_directives.end -
event2->data.document_start.tag_directives.start))
return equal;
for (int k = 0; k < (event1->data.document_start.tag_directives.end -
event1->data.document_start.tag_directives.start);
k++) {
if ((strcmp((char *)event1->data.document_start.tag_directives.start[k]
.handle,
(char *)event2->data.document_start.tag_directives.start[k]
.handle) != 0) ||
(strcmp((char *)event1->data.document_start.tag_directives.start[k]
.prefix,
(char *)event2->data.document_start.tag_directives.start[k]
.prefix) != 0))
return equal;
}
return !equal;
case YAML_DOCUMENT_END_EVENT:
return !equal;
case YAML_ALIAS_EVENT:
return (strcmp((char *)event1->data.alias.anchor,
(char *)event2->data.alias.anchor) == 0);
case YAML_SCALAR_EVENT:
if ((event1->data.scalar.anchor && !event2->data.scalar.anchor) ||
(!event1->data.scalar.anchor && event2->data.scalar.anchor) ||
(event1->data.scalar.anchor && event2->data.scalar.anchor &&
strcmp((char *)event1->data.scalar.anchor,
(char *)event2->data.scalar.anchor) != 0))
return equal;
if ((event1->data.scalar.tag && !event2->data.scalar.tag &&
strcmp((char *)event1->data.scalar.tag, "!") != 0) ||
(!event1->data.scalar.tag && event2->data.scalar.tag &&
strcmp((char *)event2->data.scalar.tag, "!") != 0) ||
(event1->data.scalar.tag && event2->data.scalar.tag &&
strcmp((char *)event1->data.scalar.tag,
(char *)event2->data.scalar.tag) != 0))
return equal;
if ((event1->data.scalar.length != event2->data.scalar.length) ||
memcmp(event1->data.scalar.value, event2->data.scalar.value,
event1->data.scalar.length) != 0)
return equal;
if ((event1->data.scalar.plain_implicit !=
event2->data.scalar.plain_implicit) ||
(event1->data.scalar.quoted_implicit !=
event2->data.scalar.quoted_implicit))
return equal;
return !equal;
case YAML_SEQUENCE_START_EVENT:
if ((event1->data.sequence_start.anchor &&
!event2->data.sequence_start.anchor) ||
(!event1->data.sequence_start.anchor &&
event2->data.sequence_start.anchor) ||
(event1->data.sequence_start.anchor &&
event2->data.sequence_start.anchor &&
strcmp((char *)event1->data.sequence_start.anchor,
(char *)event2->data.sequence_start.anchor) != 0))
return equal;
if ((event1->data.sequence_start.tag && !event2->data.sequence_start.tag) ||
(!event1->data.sequence_start.tag && event2->data.sequence_start.tag) ||
(event1->data.sequence_start.tag && event2->data.sequence_start.tag &&
strcmp((char *)event1->data.sequence_start.tag,
(char *)event2->data.sequence_start.tag) != 0))
return equal;
if ((event1->data.sequence_start.implicit !=
event2->data.sequence_start.implicit))
return equal;
return !equal;
case YAML_MAPPING_START_EVENT:
if ((event1->data.mapping_start.anchor &&
!event2->data.mapping_start.anchor) ||
(!event1->data.mapping_start.anchor &&
event2->data.mapping_start.anchor) ||
(event1->data.mapping_start.anchor &&
event2->data.mapping_start.anchor &&
strcmp((char *)event1->data.mapping_start.anchor,
(char *)event2->data.mapping_start.anchor) != 0))
return equal;
if ((event1->data.mapping_start.tag && !event2->data.mapping_start.tag) ||
(!event1->data.mapping_start.tag && event2->data.mapping_start.tag) ||
(event1->data.mapping_start.tag && event2->data.mapping_start.tag &&
strcmp((char *)event1->data.mapping_start.tag,
(char *)event2->data.mapping_start.tag) != 0))
return equal;
if ((event1->data.mapping_start.implicit !=
event2->data.mapping_start.implicit))
return equal;
return !equal;
default:
return !equal;
}
}
bool copy_event(yaml_event_t *event_to, yaml_event_t *event_from) {
switch (event_from->type) {
case YAML_STREAM_START_EVENT:
return yaml_stream_start_event_initialize(
event_to, event_from->data.stream_start.encoding);
case YAML_STREAM_END_EVENT:
return yaml_stream_end_event_initialize(event_to);
case YAML_DOCUMENT_START_EVENT:
return yaml_document_start_event_initialize(
event_to, event_from->data.document_start.version_directive,
event_from->data.document_start.tag_directives.start,
event_from->data.document_start.tag_directives.end,
event_from->data.document_start.implicit);
case YAML_DOCUMENT_END_EVENT:
return yaml_document_end_event_initialize(
event_to, event_from->data.document_end.implicit);
case YAML_ALIAS_EVENT:
return yaml_alias_event_initialize(event_to, event_from->data.alias.anchor);
case YAML_SCALAR_EVENT:
return yaml_scalar_event_initialize(
event_to, event_from->data.scalar.anchor, event_from->data.scalar.tag,
event_from->data.scalar.value, event_from->data.scalar.length,
event_from->data.scalar.plain_implicit,
event_from->data.scalar.quoted_implicit, event_from->data.scalar.style);
case YAML_SEQUENCE_START_EVENT:
return yaml_sequence_start_event_initialize(
event_to, event_from->data.sequence_start.anchor,
event_from->data.sequence_start.tag,
event_from->data.sequence_start.implicit,
event_from->data.sequence_start.style);
case YAML_SEQUENCE_END_EVENT:
return yaml_sequence_end_event_initialize(event_to);
case YAML_MAPPING_START_EVENT:
return yaml_mapping_start_event_initialize(
event_to, event_from->data.mapping_start.anchor,
event_from->data.mapping_start.tag,
event_from->data.mapping_start.implicit,
event_from->data.mapping_start.style);
case YAML_MAPPING_END_EVENT:
return yaml_mapping_end_event_initialize(event_to);
}
return false;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 2)
return 0;
yaml_parser_t parser;
yaml_emitter_t emitter;
yaml_event_t event;
yaml_event_t events[MAX_EVENTS];
size_t event_number = 0;
bool done = false;
int count = 0;
bool is_canonical = data[0] & 1;
bool is_unicode = data[1] & 1;
data += 2;
size -= 2;
if (!yaml_parser_initialize(&parser))
return 0;
yaml_parser_set_input_string(&parser, data, size);
if (!yaml_emitter_initialize(&emitter)) {
yaml_parser_delete(&parser);
return 0;
}
yaml_emitter_set_canonical(&emitter, is_canonical);
yaml_emitter_set_unicode(&emitter, is_unicode);
yaml_output_buffer_t out = {/*buf=*/NULL, /*size=*/0};
yaml_emitter_set_output(&emitter, yaml_write_handler, &out);
while (!done) {
if (!yaml_parser_parse(&parser, &event)) {
goto delete_parser;
}
done = (event.type == YAML_STREAM_END_EVENT);
if (event_number >= MAX_EVENTS) {
yaml_event_delete(&event);
goto delete_parser;
}
if (copy_event(&events[event_number++], &event)) {
yaml_event_delete(&event);
goto delete_parser;
}
if (!yaml_emitter_emit(&emitter, &event)) {
goto delete_parser;
}
}
yaml_parser_delete(&parser);
done = false;
if (!yaml_parser_initialize(&parser))
goto error;
yaml_parser_set_input_string(&parser, out.buf, out.size);
while (!done) {
if (!yaml_parser_parse(&parser, &event))
break;
done = (event.type == YAML_STREAM_END_EVENT);
if (events_equal(events + count, &event)) {
yaml_event_delete(&event);
break;
}
yaml_event_delete(&event);
count++;
}
delete_parser:
yaml_parser_delete(&parser);
error:
yaml_emitter_delete(&emitter);
for (int k = 0; k < event_number; k++) {
yaml_event_delete(events + k);
}
free(out.buf);
return 0;
}