bind9/fuzz/dns_message_parse.c
Tony Finch 45b2d8938b
Simplify and speed up DNS name compression
All we need for compression is a very small hash set of compression
offsets, because most of the information we need (the previously added
names) can be found in the message using the compression offsets.

This change combines dns_compress_find() and dns_compress_add() into
one function dns_compress_name() that both finds any existing suffix,
and adds any new prefix to the table. The old split led to performance
problems caused by duplicate names in the compression context.

Compression contexts are now either small or large, which the caller
chooses depending on the expected size of the message. There is no
dynamic resizing.

There is a behaviour change: compression now acts on all the labels in
each name, instead of just the last few.

A small benchmark suggests this is about 2x faster.
2022-10-17 08:45:44 +02:00

187 lines
4.1 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
#include <isc/file.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/message.h>
#include "fuzz.h"
bool debug = false;
static isc_mem_t *mctx = NULL;
static uint8_t *output = NULL;
static size_t output_len = 1024;
static uint8_t render_buf[64 * 1024 - 1];
int
LLVMFuzzerInitialize(int *argc __attribute__((unused)),
char ***argv __attribute__((unused))) {
isc_mem_create(&mctx);
output = isc_mem_get(mctx, output_len);
return (0);
}
static isc_result_t
parse_message(isc_buffer_t *input, dns_message_t **messagep) {
isc_result_t result;
dns_message_t *message = NULL;
dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
result = dns_message_parse(message, input, DNS_MESSAGEPARSE_BESTEFFORT);
if (result == DNS_R_RECOVERABLE) {
result = ISC_R_SUCCESS;
}
if (result == ISC_R_SUCCESS && messagep != NULL) {
*messagep = message;
} else {
dns_message_detach(&message);
}
return (result);
}
static isc_result_t
print_message(dns_message_t *message) {
isc_result_t result;
isc_buffer_t buffer;
do {
isc_buffer_init(&buffer, output, output_len);
result = dns_message_totext(message, &dns_master_style_debug, 0,
&buffer);
if (result == ISC_R_NOSPACE) {
isc_mem_put(mctx, output, output_len);
output_len *= 2;
output = isc_mem_get(mctx, output_len);
continue;
}
} while (result == ISC_R_NOSPACE);
if (debug) {
fprintf(stderr, "%.*s\n", (int)isc_buffer_usedlength(&buffer),
output);
}
return (result);
}
#define CHECKRESULT(r, f) \
{ \
r = (f); \
if (r != ISC_R_SUCCESS) { \
goto cleanup; \
} \
}
static isc_result_t
render_message(dns_message_t **messagep) {
isc_result_t result;
dns_message_t *message = *messagep;
isc_buffer_t buffer;
dns_compress_t cctx;
isc_buffer_init(&buffer, render_buf, sizeof(render_buf));
message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
for (size_t i = 0; i < DNS_SECTION_MAX; i++) {
message->counts[i] = 0;
}
dns_compress_init(&cctx, mctx, 0);
CHECKRESULT(result, dns_message_renderbegin(message, &cctx, &buffer));
CHECKRESULT(result, dns_message_rendersection(message,
DNS_SECTION_QUESTION, 0));
CHECKRESULT(result,
dns_message_rendersection(message, DNS_SECTION_ANSWER, 0));
CHECKRESULT(result, dns_message_rendersection(
message, DNS_SECTION_AUTHORITY, 0));
CHECKRESULT(result, dns_message_rendersection(
message, DNS_SECTION_ADDITIONAL, 0));
dns_message_renderend(message);
dns_compress_invalidate(&cctx);
message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
dns_message_detach(messagep);
result = parse_message(&buffer, messagep);
return (result);
cleanup:
dns_compress_invalidate(&cctx);
return (result);
}
int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
isc_buffer_t buffer;
isc_result_t result;
dns_message_t *message = NULL;
if (size > 65535) {
return (0);
}
isc_buffer_constinit(&buffer, data, size);
isc_buffer_add(&buffer, size);
isc_buffer_setactive(&buffer, size);
result = parse_message(&buffer, &message);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
result = print_message(message);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
result = render_message(&message);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
result = print_message(message);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
cleanup:
if (message != NULL) {
dns_message_detach(&message);
}
return (0);
}