aboutsummaryrefslogtreecommitdiffhomepage
path: root/projects/binutils/fuzz_disas_ext.c
blob: d9f9185200bf239f7c79260148acd516db167f85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* Copyright 2021 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.
*/

/*
 * This disassemble fuzzer can be used for targetting specific architectures
 * and also initilises the target architecture with a random options string.
 * This is useful, for example, to hit architectures the general disassembly
 * fuzzer may not be hitting, and also to fuzz disassemblers in different
 * settings. This fuzzer also targets both big and small endianness. Down
 * the line this fuzzer can be expanded on, e.g. to include symobls and data
 * in the memory that is to be disassembled.
 */
#include "sysdep.h"
#include "bfd.h"
#include "dis-asm.h"
#include "disassemble.h"

#include <stdint.h>

#define MAX_TEXT_SIZE 256

typedef struct
{
  char *buffer;
  size_t pos;
} SFILE;

static int
fuzz_disasm_null_styled_printf (void *stream,
             enum disassembler_style style,
             const char *format, ...)
{
  return 0;
}


static int objdump_sprintf (void *vf, const char *format, ...)
{
  SFILE *f = (SFILE *) vf;
  size_t n;
  va_list args;

  va_start (args, format);
  if (f->pos >= MAX_TEXT_SIZE){
    printf("buffer needs more space\n");
    //reset
    f->pos=0;
    return 0;
  }
  n = vsnprintf (f->buffer + f->pos, MAX_TEXT_SIZE - f->pos, format, args);
  //vfprintf(stdout, format, args);
  va_end (args);
  f->pos += n;
  return n;
}

char options[100]; // Enable the disassemblers to have random options.
char private_data[100]; // This is used for some targets. Watch out when
                        // using it as some disassemblers have pointers
                        // within their private_data.

void
disassemble_architecture(int arch, const uint8_t *Data, size_t Size, int big) {
  char AssemblyText[MAX_TEXT_SIZE];
  struct disassemble_info disasm_info;
  SFILE s;

  init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf, fuzz_disasm_null_styled_printf);
  disasm_info.fprintf_func = objdump_sprintf;
  disasm_info.print_address_func = generic_print_address;
  disasm_info.display_endian = disasm_info.endian = BFD_ENDIAN_LITTLE;
  disasm_info.buffer = (bfd_byte *) Data+10;
  disasm_info.buffer_vma = 0x1000;
  disasm_info.buffer_length = Size-10;
  disasm_info.insn_info_valid = 0;
  disasm_info.disassembler_options = options;
  disasm_info.created_styled_output = false;

  if (arch == bfd_arch_arm) {
    disasm_info.private_data = private_data;
  }

  s.buffer = AssemblyText;
  s.pos = 0;
  disasm_info.stream = &s;
  disasm_info.bytes_per_line = 0;

  disasm_info.arch = arch;
  disasm_info.mach = bfd_getl64(&Data[Size-9]);
  disasm_info.flavour = Data[Size-10];
  disasm_info.bytes_per_chunk = Data[Size-9];

  if (bfd_lookup_arch (disasm_info.arch, disasm_info.mach) != NULL) {
    disassembler_ftype disasfunc = disassembler(disasm_info.arch, big, disasm_info.mach, NULL);
    if (disasfunc != NULL) {
      disassemble_init_for_target(&disasm_info);
      while (1) {
        s.pos = 0;
        int octets = disasfunc(disasm_info.buffer_vma, &disasm_info);
        if (octets < (int) disasm_info.octets_per_byte)
          break;
        if (disasm_info.buffer_length <= (size_t) octets)
          break;
        disasm_info.buffer += octets;
        disasm_info.buffer_vma += octets / disasm_info.octets_per_byte;
        disasm_info.buffer_length -= octets;
      }
      disassemble_free_target(&disasm_info);
    }
  }
}

int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  if (Size < 210 || Size > 16394) {
    // 10 bytes for options
    // 16394 limit code to prevent timeouts
    return 0;
  }
  char arch = *Data;

  // Create a random options string
  memcpy(options, Data, 100);
  options[99] = '\0';
  Data += 100;
  Size -= 100;

  // The private data may or may not be used in the disassemble_architecture
  // depending on the the target.
  memcpy(private_data, Data, 100);
  options[99] = '\0';
  Data += 100;
  Size -= 100;

  // FUZZ_TARGET_ARCH must be defined and should be the architecture
  // you target.
  disassemble_architecture(FUZZ_TARGET_ARCH, Data, Size, 1);
  disassemble_architecture(FUZZ_TARGET_ARCH, Data, Size, 0);

  return 0;
}