blob: cb7bda7f41cd0bff5f664c6fffb7dfe6ef5cb9cd [file] [log] [blame]
Lev Walkincb523912017-09-30 19:33:23 -07001#include <T.h>
2
3#include <string.h>
4#include <errno.h>
5#include <unistd.h>
6#include <sys/stat.h>
7#include <sysexits.h>
8
Lev Walkind3cce462017-10-01 13:43:36 -07009#ifdef ASN1_TEXT
10#define STRINGIFY(x) #x
11#define ASN1_STR STRINGIFY(ASN1_TEXT)
12#else
13#define ASN1_STR "T"
14#endif
15
16static const struct encoding_map {
17 const char *name;
18 const char *dir_name;
19 enum asn_transfer_syntax syntax;
20} encodings[] = {
21 {"DER", "der", ATS_DER},
22 {"OER", "oer", ATS_CANONICAL_OER},
23 {"UPER", "uper", ATS_UNALIGNED_CANONICAL_PER},
24 {"XER", "xer", ATS_CANONICAL_XER},
25};
26
27static enum asn_transfer_syntax
28lookup_syntax(const char *name) {
29 if(name) {
30 for(size_t i = 0; i < sizeof(encodings) / sizeof(encodings[0]); i++) {
31 struct encoding_map enc = encodings[i];
32 if(strcasecmp(name, enc.name) == 0) {
33 return enc.syntax;
34 }
35 }
36 }
37 return ATS_INVALID;
38}
39
40
Lev Walkincb523912017-09-30 19:33:23 -070041#ifdef ENABLE_LIBFUZZER
42
Lev Walkind3cce462017-10-01 13:43:36 -070043static int initialized;
44static enum asn_transfer_syntax syntax;
45static void __attribute__((constructor)) initialize() {
46 initialized = 1;
47 const char *data_dir = getenv("ASN1_DATA_DIR");
48 if(data_dir && strrchr(data_dir, '/')) {
49 data_dir = strrchr(data_dir, '/') + 1;
50 }
51 syntax = lookup_syntax(data_dir);
52 if(syntax == ATS_INVALID) {
53 fprintf(stderr,
54 "Expected ASN1_DATA_DIR={der,oer,uper,xer} environment "
55 "variable.\n");
56 exit(EX_UNAVAILABLE);
57 }
58}
59
Lev Walkincb523912017-09-30 19:33:23 -070060int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
61
62int
63LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
Lev Walkind3cce462017-10-01 13:43:36 -070064 if(!initialized) exit(0);
65
66 /*
67 * Try to decode whatever garbage that comes in Data/Size.
68 * The idea is that we should not crash, and we should not leak memory,
69 * no matter what garbage we're dealing with.
70 */
71 T_t *structure = 0;
72 (void)asn_decode(0, syntax, &asn_DEF_T, (void **)&structure, Data, Size);
73 ASN_STRUCT_FREE(asn_DEF_T, structure);
74
Lev Walkincb523912017-09-30 19:33:23 -070075 return 0;
76}
77
78#else /* The usual check */
79
80static void
81usage(const char *progname) {
82 fprintf(stderr,
83 "Usage: %s {-c|-g <dir>} [-n <number>] [-e <encoding> ...]\n"
84 "OPTIONS:\n"
85 " -c Check encode-decode round-trip on random data\n"
86 " -g <dir> Generate random data for selected encodings\n"
Lev Walkin791d3b72017-10-02 16:24:28 -070087 " -s <size> Approximate max random value size for -c and -g\n"
Lev Walkincb523912017-09-30 19:33:23 -070088 " -n <number> Number of iterations for -c and -g\n"
89 " -e <encoding> Encodings to test or generate random data for\n"
90 "Encodings (ASN.1 Transfer Syntaxes):\n"
91 " DER Distinguished Encoding Rules (compatible with "
92 "BER)\n"
93 " OER Canonical Octet Encoding Rules\n"
94 " UPER Canonical Unaligned Packed Encoding Rules\n"
95 " XER XML Encoding Rules\n",
96 progname);
97}
98
Lev Walkincb523912017-09-30 19:33:23 -070099static int
100file_write_cb(const void *data, size_t size, void *key) {
101 return fwrite(data, 1, size, (FILE *)key) == size ? 0 : -1;
102}
103
104static void
Lev Walkin791d3b72017-10-02 16:24:28 -0700105generate_random_data(enum asn_transfer_syntax syntax, const char *top_dirname, size_t max_random_value_size, int iterations, int debug) {
Lev Walkincb523912017-09-30 19:33:23 -0700106 char dirname[PATH_MAX];
107 size_t dirname_len = 0;
108 dirname[dirname_len] = '\0';
109
110 for(size_t i = 0; i < sizeof(encodings)/sizeof(encodings[0]); i++) {
111 struct encoding_map enc = encodings[i];
112 if(enc.syntax == syntax) {
113 int r = snprintf(dirname, sizeof(dirname), "%s/%s", top_dirname,
Lev Walkind3cce462017-10-01 13:43:36 -0700114 enc.dir_name);
Lev Walkincb523912017-09-30 19:33:23 -0700115 if(r >= sizeof(dirname) - sizeof("filename.bin")) {
116 fprintf(stderr, "Too long filenames\n");
117 exit(EX_SOFTWARE);
118 }
119 dirname_len = r;
120 fprintf(stderr, "Generating %d random %s values of %s into %s\n",
121 iterations, enc.name, asn_DEF_T.name, dirname);
122 break;
123 }
124 }
125 assert(dirname[0]);
126
127 (void)mkdir(top_dirname, 0777);
128
129 if(mkdir(dirname, 0777) == -1) {
130 if(errno == EEXIST) {
131 fprintf(stderr, "%s: is already present, remove.\n", dirname);
132 fprintf(stderr, "%s: not overwriting potentially valuable data.\n",
133 dirname);
134 }
135 perror(dirname);
136 exit(2);
137 }
138
Lev Walkince6b0a62017-10-01 16:59:20 -0700139 size_t generated_ok = 0;
Lev Walkincb523912017-09-30 19:33:23 -0700140 for(int i = 0; i < iterations; i++) {
141 T_t *structure = 0;
142 FILE *f;
143 snprintf(&dirname[dirname_len], sizeof(dirname) - dirname_len,
144 "/%03d.bin", i);
145
Lev Walkin791d3b72017-10-02 16:24:28 -0700146 if(asn_random_fill(&asn_DEF_T, (void **)&structure,
147 max_random_value_size)
148 == -1) {
Lev Walkincb523912017-09-30 19:33:23 -0700149 assert(structure == 0);
150 fprintf(stderr, "Can't generate %d'th value, skipping\n", i);
151 continue;
152 }
153 assert(structure != 0);
154
155 const char *filename = dirname;
156 f = fopen(filename, "wb");
157 if(!f) {
158 perror(filename);
159 assert(f);
160 exit(EX_SOFTWARE);
161 }
162 asn_enc_rval_t rval =
163 asn_encode(0, syntax, &asn_DEF_T, structure, file_write_cb, f);
164 fclose(f);
165 if(rval.encoded == -1) {
166 fprintf(stderr, "Cannot encode a random value of T into %s:\n",
167 filename);
168 if(rval.failed_type) {
169 fprintf(stderr, "(Failed type: %s)\n", rval.failed_type->name);
170 }
171 asn_fprint(stderr, &asn_DEF_T, structure);
172 exit(EX_SOFTWARE);
173 }
174
Lev Walkin791d3b72017-10-02 16:24:28 -0700175 if(debug) {
176 if(i < 5 || debug > 1) {
177 fprintf(stderr, "[%s] ", &filename[dirname_len+1]);
178 asn_fprint(stderr, &asn_DEF_T, structure);
179 } else if(i == 5) {
180 fprintf(stderr, "... and so on\n");
181 }
Lev Walkincb523912017-09-30 19:33:23 -0700182 }
183
184 ASN_STRUCT_FREE(asn_DEF_T, structure);
Lev Walkince6b0a62017-10-01 16:59:20 -0700185 generated_ok++;
186 }
187
188 if(!generated_ok) {
189 fprintf(stderr, "Requested to generate %d values, but failed.\n",
190 iterations);
191 exit(EX_SOFTWARE);
Lev Walkincb523912017-09-30 19:33:23 -0700192 }
193
194}
195
196static void
Lev Walkin791d3b72017-10-02 16:24:28 -0700197check_random_roundtrip(enum asn_transfer_syntax syntax, size_t max_random_value_size, int iterations, int debug) {
Lev Walkincb523912017-09-30 19:33:23 -0700198 struct encoding_map enc;
199
200 for(size_t i = 0; i < sizeof(encodings)/sizeof(encodings[0]); i++) {
201 enc = encodings[i];
202 if(enc.syntax == syntax) {
203 fprintf(stderr, "Testing %d iterations of round-trip for %s\n",
204 iterations, enc.name);
205 break;
206 }
207 }
208
209 for(int i = 0; i < iterations; i++) {
Lev Walkina5b02882017-10-01 22:48:44 -0700210 char tmp_buffer[512];
211 char *buffer = tmp_buffer;
212 size_t buffer_size = sizeof(tmp_buffer);
Lev Walkincb523912017-09-30 19:33:23 -0700213 T_t *structure = 0;
214 T_t *decoded_structure = 0;
215
Lev Walkin791d3b72017-10-02 16:24:28 -0700216 if(asn_random_fill(&asn_DEF_T, (void **)&structure,
217 max_random_value_size)
218 == -1) {
Lev Walkincb523912017-09-30 19:33:23 -0700219 assert(structure == 0);
220 fprintf(stderr, "Can't generate %d'th value, skipping\n", i);
221 continue;
222 }
223 assert(structure != 0);
224
225 asn_enc_rval_t er;
226 for(;;) {
227 er = asn_encode_to_buffer(
228 0, syntax, &asn_DEF_T, structure, buffer, buffer_size);
Lev Walkind3cce462017-10-01 13:43:36 -0700229 if(er.encoded == -1) {
230 fprintf(stderr, "Encoded T into %zd bytes\n", er.encoded);
231 fprintf(stderr, "Structure %s:\n",
232 sizeof(ASN1_STR) > 60 ? "T" : ASN1_STR);
233 asn_fprint(stderr, &asn_DEF_T, structure);
234 assert(er.encoded >= 0);
235 exit(EX_SOFTWARE);
236 }
Lev Walkincb523912017-09-30 19:33:23 -0700237 if(er.encoded > buffer_size && buffer == tmp_buffer) {
Lev Walkin791d3b72017-10-02 16:24:28 -0700238 if(debug) {
239 fprintf(
240 stderr,
241 "Reallocate output buffer %zu -> %zu (iteration %d)\n",
242 buffer_size, er.encoded, i);
243 }
Lev Walkincb523912017-09-30 19:33:23 -0700244 buffer = malloc(er.encoded + 1);
245 assert(buffer);
246 buffer[er.encoded] = '\0';
247 buffer_size = er.encoded;
248 continue;
249 }
250 break;
251 }
Lev Walkina5b02882017-10-01 22:48:44 -0700252 if(er.encoded > buffer_size) {
253 fprintf(stderr, "Data %zd does not fit into buffer %zu\n",
254 er.encoded, buffer_size);
255 assert(er.encoded <= buffer_size);
256 }
Lev Walkincb523912017-09-30 19:33:23 -0700257
258 asn_dec_rval_t rval =
259 asn_decode(0, syntax, &asn_DEF_T, (void **)&decoded_structure,
260 buffer, er.encoded);
261 if(rval.code == RC_OK) {
Lev Walkind3cce462017-10-01 13:43:36 -0700262 /* Everything's cool... or is it? Expecting a proper consumed */
263 if(rval.consumed != er.encoded) {
Lev Walkina5b02882017-10-01 22:48:44 -0700264 fprintf(stderr, "Encoded into %zd, yet consumed %zu\n",
Lev Walkind3cce462017-10-01 13:43:36 -0700265 er.encoded, rval.consumed);
Lev Walkina5b02882017-10-01 22:48:44 -0700266 fprintf(stderr, "Original random structure:\n");
Lev Walkind3cce462017-10-01 13:43:36 -0700267 asn_fprint(stderr, &asn_DEF_T, structure);
268 assert(rval.consumed == er.encoded);
269 exit(EX_SOFTWARE);
270 }
Lev Walkincb523912017-09-30 19:33:23 -0700271 } else {
272 fprintf(stderr,
273 "Decoding %zu bytes of T yielded %s after byte %zu\n",
274 er.encoded, rval.code == RC_FAIL ? "RC_FAIL" : "RC_WMORE",
275 rval.consumed);
Lev Walkina5b02882017-10-01 22:48:44 -0700276 fprintf(stderr, "Original random structure:\n");
Lev Walkincb523912017-09-30 19:33:23 -0700277 asn_fprint(stderr, &asn_DEF_T, structure);
278 exit(EX_SOFTWARE);
279 }
280
Lev Walkind3cce462017-10-01 13:43:36 -0700281 /*
282 * Confirm that we decoded the same data.
283 */
284 int cmp = asn_DEF_T.op->compare_struct(&asn_DEF_T, structure,
285 decoded_structure);
286 if(cmp != 0) {
287 fprintf(stderr, "Random %s value:\n", ASN1_STR);
288 asn_fprint(stderr, &asn_DEF_T, structure);
289 fprintf(stderr, "Decoded %s value:\n", ASN1_STR);
290 asn_fprint(stderr, &asn_DEF_T, decoded_structure);
291 assert(cmp == 0);
292 }
293 ASN_STRUCT_FREE(asn_DEF_T, structure);
294 ASN_STRUCT_FREE(asn_DEF_T, decoded_structure);
Lev Walkincb523912017-09-30 19:33:23 -0700295
296 if(buffer != tmp_buffer) {
297 free(buffer);
Lev Walkincb523912017-09-30 19:33:23 -0700298 }
299
300 if(i < 5) {
301 fprintf(stderr, "[%03d] round-trip in %zd bytes OK\n", i,
302 er.encoded);
303 } else if(i == 5) {
304 fprintf(stderr, "... and so on\n");
305 }
306 }
307
308 fprintf(stderr, "OK %d iterations of round-trip for %s\n", iterations,
309 enc.name);
310}
311
312int main(int argc, char **argv) {
313 uint32_t enabled_encodings = 0;
314 enum {
315 MODE_UNKNOWN,
316 MODE_GENERATE_RANDOM_DATA,
317 MODE_CHECK_RANDOM_ROUNDTRIP
318 } mode = MODE_UNKNOWN;
319 const char *generate_into_dir = NULL;
320 int iterations = 100;
Lev Walkin791d3b72017-10-02 16:24:28 -0700321 size_t max_random_value_size = 128;
322 int debug = 0;
Lev Walkincb523912017-09-30 19:33:23 -0700323 int c;
324
Lev Walkin791d3b72017-10-02 16:24:28 -0700325 while((c = getopt(argc, argv, "cde:g:hn:s:")) != -1) {
Lev Walkincb523912017-09-30 19:33:23 -0700326 switch(c) {
327 case 'c':
328 mode = MODE_CHECK_RANDOM_ROUNDTRIP;
329 break;
Lev Walkin791d3b72017-10-02 16:24:28 -0700330 case 'd':
331 debug = 1;
332 break;
Lev Walkincb523912017-09-30 19:33:23 -0700333 case 'e':
Lev Walkind3cce462017-10-01 13:43:36 -0700334 enabled_encodings |= 1 << lookup_syntax(optarg);
Lev Walkincb523912017-09-30 19:33:23 -0700335 if(enabled_encodings & (1 << ATS_INVALID)) {
336 fprintf(stderr, "-e %s: Unknown (unsupported?) encoding\n",
337 optarg);
338 exit(EX_UNAVAILABLE);
339 }
340 break;
341 case 'g':
342 mode = MODE_GENERATE_RANDOM_DATA;
343 generate_into_dir = optarg;
344 break;
345 case 'h':
346 usage(argv[0]);
347 exit(0);
348 case 'n':
349 iterations = atoi(optarg);
350 if(iterations <= 0) {
351 fprintf(stderr, "-n %s: positive value expected\n", optarg);
352 exit(EX_DATAERR);
353 }
354 break;
Lev Walkin791d3b72017-10-02 16:24:28 -0700355 case 's':
356 if(atoi(optarg) <= 0) {
357 fprintf(stderr, "-s %s: positive value expected\n", optarg);
358 exit(EX_DATAERR);
359 }
360 max_random_value_size = atoi(optarg);
361 break;
Lev Walkincb523912017-09-30 19:33:23 -0700362 default:
363 usage(argv[0]);
364 exit(2);
365 }
366 }
367
368 if(mode == MODE_UNKNOWN) {
369 usage(argv[0]);
370 exit(2);
371 } else if(!enabled_encodings) {
372 for(size_t i = 0; i < sizeof(encodings)/sizeof(encodings[0]); i++) {
373 enabled_encodings |= 1 << encodings[i].syntax;
374 }
375 }
376
377 /* Enumerate requested encodings (-e ...) */
378 for(unsigned i = 0; i < 8*sizeof(enabled_encodings)-1; i++) {
379 if(enabled_encodings & (1 << i)) {
380 enum asn_transfer_syntax syntax = i;
381 switch(mode) {
382 case MODE_UNKNOWN:
383 assert(mode != MODE_UNKNOWN);
384 break;
385 case MODE_GENERATE_RANDOM_DATA:
Lev Walkin791d3b72017-10-02 16:24:28 -0700386 generate_random_data(syntax, generate_into_dir,
387 max_random_value_size, iterations, debug);
Lev Walkincb523912017-09-30 19:33:23 -0700388 break;
389 case MODE_CHECK_RANDOM_ROUNDTRIP:
Lev Walkin791d3b72017-10-02 16:24:28 -0700390 check_random_roundtrip(syntax, max_random_value_size,
391 iterations, debug);
Lev Walkincb523912017-09-30 19:33:23 -0700392 break;
393 }
394 }
395 }
396
397 return 0;
398}
399
400#endif