312 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			8.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_DOCUMENTS 16
 | 
						|
 | 
						|
bool nodes_equal(yaml_document_t *document1, int index1,
 | 
						|
                 yaml_document_t *document2, int index2, int level) {
 | 
						|
  const bool equal = true;
 | 
						|
 | 
						|
  if (level++ > 1000)
 | 
						|
    return !equal;
 | 
						|
  yaml_node_t *node1 = yaml_document_get_node(document1, index1);
 | 
						|
 | 
						|
  if (!node1)
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  yaml_node_t *node2 = yaml_document_get_node(document2, index2);
 | 
						|
 | 
						|
  if (!node2)
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  if (node1->type != node2->type)
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  if (strcmp((char *)node1->tag, (char *)node2->tag) != 0)
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  switch (node1->type) {
 | 
						|
  case YAML_SCALAR_NODE:
 | 
						|
    if (node1->data.scalar.length != node2->data.scalar.length)
 | 
						|
      return !equal;
 | 
						|
    if (strncmp((char *)node1->data.scalar.value,
 | 
						|
                (char *)node2->data.scalar.value,
 | 
						|
                node1->data.scalar.length) != 0)
 | 
						|
      return !equal;
 | 
						|
    break;
 | 
						|
  case YAML_SEQUENCE_NODE:
 | 
						|
    if ((node1->data.sequence.items.top - node1->data.sequence.items.start) !=
 | 
						|
        (node2->data.sequence.items.top - node2->data.sequence.items.start))
 | 
						|
      return !equal;
 | 
						|
    for (int k = 0; k < (node1->data.sequence.items.top -
 | 
						|
                         node1->data.sequence.items.start);
 | 
						|
         k++) {
 | 
						|
      if (!nodes_equal(document1, node1->data.sequence.items.start[k],
 | 
						|
                       document2, node2->data.sequence.items.start[k], level))
 | 
						|
        return !equal;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case YAML_MAPPING_NODE:
 | 
						|
    if ((node1->data.mapping.pairs.top - node1->data.mapping.pairs.start) !=
 | 
						|
        (node2->data.mapping.pairs.top - node2->data.mapping.pairs.start))
 | 
						|
      return !equal;
 | 
						|
    for (int k = 0;
 | 
						|
         k < (node1->data.mapping.pairs.top - node1->data.mapping.pairs.start);
 | 
						|
         k++) {
 | 
						|
      if (!nodes_equal(document1, node1->data.mapping.pairs.start[k].key,
 | 
						|
                       document2, node2->data.mapping.pairs.start[k].key,
 | 
						|
                       level))
 | 
						|
        return !equal;
 | 
						|
      if (!nodes_equal(document1, node1->data.mapping.pairs.start[k].value,
 | 
						|
                       document2, node2->data.mapping.pairs.start[k].value,
 | 
						|
                       level))
 | 
						|
        return !equal;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    return !equal;
 | 
						|
  }
 | 
						|
  return equal;
 | 
						|
}
 | 
						|
 | 
						|
bool documents_equal(yaml_document_t *document1, yaml_document_t *document2) {
 | 
						|
 | 
						|
  const bool equal = true;
 | 
						|
 | 
						|
  if ((document1->version_directive && !document2->version_directive) ||
 | 
						|
      (!document1->version_directive && document2->version_directive) ||
 | 
						|
      (document1->version_directive && document2->version_directive &&
 | 
						|
       (document1->version_directive->major !=
 | 
						|
            document2->version_directive->major ||
 | 
						|
        document1->version_directive->minor !=
 | 
						|
            document2->version_directive->minor)))
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  if ((document1->tag_directives.end - document1->tag_directives.start) !=
 | 
						|
      (document2->tag_directives.end - document2->tag_directives.start))
 | 
						|
    return !equal;
 | 
						|
  for (int k = 0;
 | 
						|
       k < (document1->tag_directives.end - document1->tag_directives.start);
 | 
						|
       k++) {
 | 
						|
    if ((strcmp((char *)document1->tag_directives.start[k].handle,
 | 
						|
                (char *)document2->tag_directives.start[k].handle) != 0) ||
 | 
						|
        (strcmp((char *)document1->tag_directives.start[k].prefix,
 | 
						|
                (char *)document2->tag_directives.start[k].prefix) != 0))
 | 
						|
      return !equal;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((document1->nodes.top - document1->nodes.start) !=
 | 
						|
      (document2->nodes.top - document2->nodes.start))
 | 
						|
    return !equal;
 | 
						|
 | 
						|
  if (document1->nodes.top != document1->nodes.start) {
 | 
						|
    if (!nodes_equal(document1, 1, document2, 1, 0))
 | 
						|
      return !equal;
 | 
						|
  }
 | 
						|
 | 
						|
  return equal;
 | 
						|
}
 | 
						|
 | 
						|
bool copy_document(yaml_document_t *document_to,
 | 
						|
                   yaml_document_t *document_from) {
 | 
						|
  bool error = true;
 | 
						|
 | 
						|
  yaml_node_t *node;
 | 
						|
  yaml_node_item_t *item;
 | 
						|
  yaml_node_pair_t *pair;
 | 
						|
 | 
						|
  if (!yaml_document_initialize(document_to, document_from->version_directive,
 | 
						|
                                document_from->tag_directives.start,
 | 
						|
                                document_from->tag_directives.end,
 | 
						|
                                document_from->start_implicit,
 | 
						|
                                document_from->end_implicit))
 | 
						|
    return !error;
 | 
						|
 | 
						|
  for (node = document_from->nodes.start; node < document_from->nodes.top;
 | 
						|
       node++) {
 | 
						|
    switch (node->type) {
 | 
						|
    case YAML_SCALAR_NODE:
 | 
						|
      if (!yaml_document_add_scalar(
 | 
						|
              document_to, node->tag, node->data.scalar.value,
 | 
						|
              node->data.scalar.length, node->data.scalar.style))
 | 
						|
        goto out;
 | 
						|
      break;
 | 
						|
    case YAML_SEQUENCE_NODE:
 | 
						|
      if (!yaml_document_add_sequence(document_to, node->tag,
 | 
						|
                                      node->data.sequence.style))
 | 
						|
        goto out;
 | 
						|
      break;
 | 
						|
    case YAML_MAPPING_NODE:
 | 
						|
      if (!yaml_document_add_mapping(document_to, node->tag,
 | 
						|
                                     node->data.mapping.style))
 | 
						|
        goto out;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (node = document_from->nodes.start; node < document_from->nodes.top;
 | 
						|
       node++) {
 | 
						|
    switch (node->type) {
 | 
						|
    case YAML_SEQUENCE_NODE:
 | 
						|
      for (item = node->data.sequence.items.start;
 | 
						|
           item < node->data.sequence.items.top; item++) {
 | 
						|
        if (!yaml_document_append_sequence_item(
 | 
						|
                document_to, node - document_from->nodes.start + 1, *item))
 | 
						|
          goto out;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case YAML_MAPPING_NODE:
 | 
						|
      for (pair = node->data.mapping.pairs.start;
 | 
						|
           pair < node->data.mapping.pairs.top; pair++) {
 | 
						|
        if (!yaml_document_append_mapping_pair(
 | 
						|
                document_to, node - document_from->nodes.start + 1, pair->key,
 | 
						|
                pair->value))
 | 
						|
          goto out;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return error;
 | 
						|
 | 
						|
out:
 | 
						|
  yaml_document_delete(document_to);
 | 
						|
  return !error;
 | 
						|
}
 | 
						|
 | 
						|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 | 
						|
  if (size < 2)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  yaml_parser_t parser;
 | 
						|
  yaml_emitter_t emitter;
 | 
						|
 | 
						|
  yaml_document_t document;
 | 
						|
  yaml_document_t documents[MAX_DOCUMENTS];
 | 
						|
  size_t document_number = 0;
 | 
						|
  int count = 0;
 | 
						|
  bool done = false;
 | 
						|
  bool equal = false;
 | 
						|
  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))
 | 
						|
    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);
 | 
						|
  yaml_emitter_open(&emitter);
 | 
						|
 | 
						|
  while (!done) {
 | 
						|
    if (!yaml_parser_load(&parser, &document)) {
 | 
						|
      equal = 1;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    done = (!yaml_document_get_root_node(&document));
 | 
						|
    if (!done) {
 | 
						|
      if (document_number >= MAX_DOCUMENTS) {
 | 
						|
        yaml_document_delete(&document);
 | 
						|
        equal = true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!copy_document(&documents[document_number++], &document)) {
 | 
						|
        yaml_document_delete(&document);
 | 
						|
        equal = true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      if (!(yaml_emitter_dump(&emitter, &document) ||
 | 
						|
            (yaml_emitter_flush(&emitter) && 0))) {
 | 
						|
        equal = true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      count++;
 | 
						|
    } else {
 | 
						|
      yaml_document_delete(&document);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  yaml_parser_delete(&parser);
 | 
						|
  yaml_emitter_close(&emitter);
 | 
						|
  yaml_emitter_delete(&emitter);
 | 
						|
 | 
						|
  if (!equal) {
 | 
						|
    count = 0;
 | 
						|
    done = false;
 | 
						|
    if (!yaml_parser_initialize(&parser))
 | 
						|
      goto error;
 | 
						|
 | 
						|
    if (!out.buf) {
 | 
						|
      yaml_parser_delete(&parser);
 | 
						|
      goto error;
 | 
						|
    }
 | 
						|
 | 
						|
    yaml_parser_set_input_string(&parser, out.buf, out.size);
 | 
						|
 | 
						|
    while (!done) {
 | 
						|
      if (!yaml_parser_load(&parser, &document)) {
 | 
						|
        yaml_parser_delete(&parser);
 | 
						|
        goto error;
 | 
						|
      }
 | 
						|
 | 
						|
      done = (!yaml_document_get_root_node(&document));
 | 
						|
      if (!done) {
 | 
						|
        if (!documents_equal(documents + count, &document)) {
 | 
						|
          yaml_parser_delete(&parser);
 | 
						|
          goto error;
 | 
						|
        }
 | 
						|
        count++;
 | 
						|
      }
 | 
						|
      yaml_document_delete(&document);
 | 
						|
    }
 | 
						|
    yaml_parser_delete(&parser);
 | 
						|
  }
 | 
						|
 | 
						|
  for (int k = 0; k < document_number; k++) {
 | 
						|
    yaml_document_delete(documents + k);
 | 
						|
  }
 | 
						|
 | 
						|
error:
 | 
						|
 | 
						|
  free(out.buf);
 | 
						|
  return 0;
 | 
						|
}
 |