Coverage Report

Created: 2019-08-22 13:52

/src/aspell-fuzz/aspell_fuzzer.cpp
Line
Count
Source (jump to first uncovered line)
1
#include <stdint.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <sys/types.h>
6
#include <libgen.h>
7
#include <aspell.h>
8
#include <algorithm>
9
10
static int enable_diags;
11
static char data_dir[1024];
12
13
#define FUZZ_DEBUG(FMT, ...)                                                  \
14
2.09M
        if (enable_diags) {                                                   \
15
0
          fprintf(stderr, FMT, ##__VA_ARGS__);                                \
16
0
          fprintf(stderr, "\n");                                              \
17
0
        }
18
static const size_t MAX_CONFIG_LEN = 10*1024;
19
20
int parse_config(AspellConfig *spell_config,
21
                 uint8_t *config,
22
                 size_t config_len);
23
24
// On startup, this function is called once. Use it to access argv.
25
2
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
26
2
  char *argv0_copy = strdup((*argv)[0]);
27
2
28
2
  // Create the data dir.
29
2
  snprintf(data_dir, sizeof(data_dir), "%s/dict", dirname(argv0_copy));
30
2
31
2
  // Free off the temporary variable.
32
2
  free(argv0_copy);
33
2
34
2
  printf("Init: Running with data-dir: %s\n", data_dir);
35
2
36
2
  return 0;
37
2
}
38
39
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
40
6.23k
{
41
6.23k
  AspellCanHaveError *possible_err = NULL;
42
6.23k
  AspellSpeller *spell_checker = NULL;
43
6.23k
  AspellConfig *spell_config = NULL;
44
6.23k
  AspellDocumentChecker *doc_checker = NULL;
45
6.23k
  AspellCanHaveError *doc_err = NULL;
46
6.23k
  AspellToken token;
47
6.23k
  const char *data_str = reinterpret_cast<const char *>(data);
48
6.23k
  uint8_t config[MAX_CONFIG_LEN];
49
6.23k
  size_t config_len;
50
6.23k
  int rc;
51
6.23k
52
6.23k
  // Enable or disable diagnostics based on the FUZZ_VERBOSE environment flag.
53
6.23k
  enable_diags = (getenv("FUZZ_VERBOSE") != NULL);
54
6.23k
55
6.23k
  // Copy up to MAX_CONFIG_LEN bytes from the data.
56
6.23k
  config_len = std::min(size, MAX_CONFIG_LEN);
57
6.23k
  memcpy(config, data, config_len);
58
6.23k
59
6.23k
  // Create a new configuration class.
60
6.23k
  spell_config = new_aspell_config();
61
6.23k
62
6.23k
  // Parse configuration. Exit if the configuration was bad.
63
6.23k
  rc = parse_config(spell_config, config, config_len);
64
6.23k
  if (rc == -1)
65
181
  {
66
181
    FUZZ_DEBUG("Configuration parsing failed");
67
181
    goto EXIT_LABEL;
68
181
  }
69
6.05k
70
6.05k
  // Move the data pointer past the config.
71
6.05k
  data_str += rc;
72
6.05k
  size -= rc;
73
6.05k
74
6.05k
  FUZZ_DEBUG("Document: %.*s", (int)size, data_str);
75
6.05k
76
6.05k
  // Replace the data dir with the relative directory so that it works wherever
77
6.05k
  // it is run from, so long as dictionary files are installed relative to it.
78
6.05k
  FUZZ_DEBUG("Overriding data-dir to %s", data_dir);
79
6.05k
  aspell_config_replace(spell_config, "data-dir", data_dir);
80
6.05k
81
6.05k
  // Convert the configuration to a spell checker.
82
6.05k
  possible_err = new_aspell_speller(spell_config);
83
6.05k
  if (aspell_error_number(possible_err) != 0) {
84
619
    // Failed on configuration.
85
619
    FUZZ_DEBUG("Failed to create speller: %s",
86
619
               aspell_error_message(possible_err));
87
619
    delete_aspell_can_have_error(possible_err);
88
619
    goto EXIT_LABEL;
89
619
  }
90
5.43k
91
5.43k
  // Create a spell checker.
92
5.43k
  spell_checker = to_aspell_speller(possible_err);
93
5.43k
94
5.43k
  // Convert the spell checker to a document checker.
95
5.43k
  doc_err = new_aspell_document_checker(spell_checker);
96
5.43k
  if (aspell_error(doc_err) != 0) {
97
0
    // Failed to convert to a document checker.
98
0
    FUZZ_DEBUG("Failed to create document checker: %s",
99
0
               aspell_error_message(doc_err));
100
0
    delete_aspell_can_have_error(doc_err);
101
0
    goto EXIT_LABEL;
102
0
  }
103
5.43k
104
5.43k
  doc_checker = to_aspell_document_checker(doc_err);
105
5.43k
106
5.43k
  // Process the remainder of the document.
107
5.43k
  aspell_document_checker_process(doc_checker, data_str, size);
108
5.43k
109
5.43k
  // Iterate over all misspellings.
110
5.43k
  token = aspell_document_checker_next_misspelling(doc_checker);
111
5.43k
112
5.43k
  FUZZ_DEBUG("Token len %d", token.len);
113
5.43k
114
5.43k
  for (;
115
32.4k
       token.len != 0;
116
26.9k
       token = aspell_document_checker_next_misspelling(doc_checker))
117
26.9k
  {
118
26.9k
    // Get spelling suggestions for the misspelling.
119
26.9k
    auto word_list = aspell_speller_suggest(spell_checker,
120
26.9k
                                            data_str + token.offset,
121
26.9k
                                            token.len);
122
26.9k
123
26.9k
    // Iterate over the suggested replacement words in the word list.
124
26.9k
    AspellStringEnumeration *els = aspell_word_list_elements(word_list);
125
26.9k
126
26.9k
    for (const char *word = aspell_string_enumeration_next(els);
127
2.01M
         word != 0;
128
1.98M
         word = aspell_string_enumeration_next(els))
129
1.98M
    {
130
1.98M
      // Conditionally print out the suggested replacement words.
131
1.98M
      FUZZ_DEBUG("Suggesting replacement for word at offset %d len %d: %s",
132
1.98M
                 token.offset,
133
1.98M
                 token.len,
134
1.98M
                 word);
135
1.98M
    }
136
26.9k
    delete_aspell_string_enumeration(els);
137
26.9k
  }
138
5.43k
139
6.23k
EXIT_LABEL:
140
6.23k
141
6.23k
  if (doc_checker != NULL) {
142
5.43k
    delete_aspell_document_checker(doc_checker);
143
5.43k
  }
144
6.23k
145
6.23k
  if (spell_checker != NULL) {
146
5.43k
    delete_aspell_speller(spell_checker);
147
5.43k
  }
148
6.23k
149
6.23k
  if (spell_config != NULL) {
150
6.23k
    delete_aspell_config(spell_config);
151
6.23k
  }
152
6.23k
153
6.23k
  return 0;
154
5.43k
}
155
156
// Returns -1 on error, or the number of bytes consumed from the config string
157
// otherwise.
158
int parse_config(AspellConfig *spell_config,
159
                 uint8_t *config,
160
                 size_t config_len)
161
6.23k
{
162
6.23k
  uint8_t line[MAX_CONFIG_LEN];
163
6.23k
164
6.23k
  uint8_t *config_ptr = config;
165
6.23k
  size_t config_ptr_used = 0;
166
6.23k
167
6.23k
  uint8_t *delimiter;
168
6.23k
169
6.23k
  // Iterate over the lines.
170
6.23k
  for (delimiter = (uint8_t *)memchr(config_ptr,
171
6.23k
                                     '\n',
172
6.23k
                                     config_len - config_ptr_used);
173
55.7k
       delimiter != NULL;
174
49.5k
       delimiter = (uint8_t *)memchr(config_ptr,
175
49.5k
                                     '\n',
176
49.5k
                                     config_len - config_ptr_used))
177
50.8k
  {
178
50.8k
    int line_len = delimiter - config_ptr;
179
50.8k
180
50.8k
    if (line_len == 0) {
181
1.13k
      // The line is zero-length; it's the end of configuration. Skip over the
182
1.13k
      // delimiter and break out.
183
1.13k
      FUZZ_DEBUG("Breaking out of config");
184
1.13k
      config_ptr++;
185
1.13k
      config_ptr_used++;
186
1.13k
      break;
187
1.13k
    }
188
49.7k
189
49.7k
    // Copy the line into the line array. Replace the newline by a null.
190
49.7k
    memcpy(line, config_ptr, line_len);
191
49.7k
    line[line_len] = 0;
192
49.7k
193
49.7k
    // Try and split the line by =.
194
49.7k
    uint8_t *kv_delim = (uint8_t *)memchr(line, '=', line_len);
195
49.7k
196
49.7k
    if (kv_delim == NULL) {
197
181
      // Can't split as a k/v pair. Exit early.
198
181
      return -1;
199
181
    }
200
49.5k
201
49.5k
    // Convert the line into a key, value pair.
202
49.5k
    kv_delim[0] = 0;
203
49.5k
204
49.5k
    char *keyword = reinterpret_cast<char *>(line);
205
49.5k
    char *value = reinterpret_cast<char *>(kv_delim + 1);
206
49.5k
207
49.5k
    FUZZ_DEBUG("Key: %s; Value: %s", keyword, value);
208
49.5k
    int ok = aspell_config_replace(spell_config, keyword, value);
209
49.5k
    if (!ok) {
210
35.4k
      // Log any errors and continue.
211
35.4k
      FUZZ_DEBUG("Config error from aspell_config_replace: %s",
212
35.4k
                 aspell_config_error_message(spell_config));
213
35.4k
    }
214
49.5k
215
49.5k
    // Advance the config pointers.  Make sure to add 1 for the delimiter.
216
49.5k
    config_ptr += (line_len + 1);
217
49.5k
    config_ptr_used += (line_len + 1);
218
49.5k
  }
219
6.23k
220
6.23k
  // Return how much data  was used.
221
6.23k
  FUZZ_DEBUG("Used %zu bytes of configuration data", config_ptr_used);
222
6.05k
223
6.05k
  return config_ptr_used;
224
6.23k
}