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