asn1: Make asn1_print a fuzzing tool

This commit is contained in:
Nicolas Williams
2021-03-05 13:19:38 -06:00
parent 4502e989f2
commit 6ecab8ce51
2 changed files with 96 additions and 25 deletions

View File

@@ -60,6 +60,9 @@
.Fl Fl raw-sequence
.Xc
.Oc
.Oo Fl n \*(Ba Xo Fl Fl no-print Xc Oc
.Oo Xo Fl Fl test-encode Xc Oc
.Oo Xo Fl Fl test-copy Xc Oc
.Oo Fl l v \*(Ba Xo
.Fl Fl version
.Xc
@@ -113,6 +116,22 @@ If a value parses as a given
.Ar TypeName
but any bytes are left over, try to parse those separately as
well until all bytes are consumed or an error occurs.
.It Fl n, Fl Fl no-print
For the case where
.Fl A
or
.Fl Fl try-all-types
or where a
.Ar TypeName
is given, do not output a JSON representation of the value, just
attempt to decode it.
This is useful for fuzzing.
.It Fl Fl test-encode
Check that encoding produces the same value as decoding.
Useful for fuzzing.
.It Fl Fl test-copy
Test copy functions.
Useful for fuzzing.
.It Fl v, Fl Fl version
.It Fl h, Fl Fl help
.El

View File

@@ -55,33 +55,45 @@
#include "rfc4108_asn1.h"
#include "x690sample_asn1.h"
static int sequence_flag = 0;
static int try_all_flag = 0;
static int print_flag = 1;
static int test_copy_flag;
static int test_encode_flag;
static int sequence_flag;
static int try_all_flag;
static int indent_flag = 1;
static int inner_flag = 0;
static int inner_flag;
static unsigned long indefinite_form_loop;
static unsigned long indefinite_form_loop_max = 10000;
typedef size_t (*lengther)(void *);
typedef size_t (*copyer)(const void *, void *);
typedef int (*encoder)(unsigned char *, size_t, void *, size_t *);
typedef int (*decoder)(const unsigned char *, size_t, void *, size_t *);
typedef char *(*printer)(const void *, int);
typedef void (*releaser)(void *);
const struct types {
const char *name;
size_t sz;
copyer cpy;
lengther len;
decoder decode;
encoder encode;
printer print;
releaser release;
size_t sz;
} types[] = {
#define ASN1_SYM_INTVAL(n, gn, gns, i)
#define ASN1_SYM_OID(n, gn, gns)
#define ASN1_SYM_TYPE(n, gn, gns) \
{ \
n, \
sizeof(gns), \
(copyer)copy_ ## gns, \
(lengther)length_ ## gns, \
(decoder)decode_ ## gns, \
(encoder)encode_ ## gns, \
(printer)print_ ## gns, \
(releaser)free_ ## gns, \
sizeof(gns) \
},
#include "cms_asn1_syms.x"
#include "digest_asn1_syms.x"
@@ -356,8 +368,8 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
const char *typename = "";
size_t matches = 0;
size_t sz;
size_t tried = 0;
size_t i = 0;
char *s = NULL;
void *v;
int ret = 0;
@@ -397,6 +409,7 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
v = ecalloc(1, sorted_types[i].sz);
ret = sorted_types[i].decode(buf, len, v, &sz);
if (ret == 0) {
matches++;
if (sz == len) {
fprintf(stderr, "Match: %s\n", typename);
} else if (sequence_flag) {
@@ -404,36 +417,70 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
} else {
fprintf(stderr, "Prefix match: %s\n", typename);
}
if (print_flag) {
char *s = NULL;
s = sorted_types[i].print(v, indent_flag ? ASN1_PRINT_INDENT : 0);
sorted_types[i].release(v);
if (!s) {
ret = errno;
err(1, "Could not print %s\n", typename);
}
}
free(v);
if (ret == 0) {
fprintf(stdout, "%s\n", s);
matches++;
i++;
if (try_all_flag)
continue;
free(s);
return 0;
}
if (test_encode_flag) {
unsigned char *der = emalloc(sz);
size_t wants = sorted_types[i].len(v);
if (argv[0])
if (wants != sz)
errx(1, "Encoding will not round trip");
ret = sorted_types[i].encode(der, sz, v, &sz);
if (ret != 0)
errx(1, "Encoding failed");
if (memcmp(buf, der, sz))
errx(1, "Encoding did not round trip");
}
if (test_copy_flag) {
void *vcpy = ecalloc(1, sorted_types[i].sz);
ret = sorted_types[i].cpy(v, vcpy);
if (ret != 0)
errx(1, "Copy function failed");
if (test_encode_flag) {
unsigned char *der = emalloc(sz);
size_t wants = sorted_types[i].len(vcpy);
if (wants != sz)
errx(1, "Encoding of copy will not round trip");
ret = sorted_types[i].encode(der, sz, vcpy, &sz);
if (ret != 0)
errx(1, "Encoding of copy failed");
if (memcmp(buf, der, sz))
errx(1, "Encoding of copy did not round trip");
}
sorted_types[i].release(vcpy);
free(vcpy);
}
}
sorted_types[i].release(v);
free(v);
tried++;
i++;
if (ret == 0 && !try_all_flag && !argv[0])
return 0;
if (!try_all_flag && argv[0])
continue;
if (try_all_flag) {
i++;
if (i < sizeof(types)/sizeof(types[0]))
continue;
if (matches)
break;
errx(1, "No type matched the input value");
}
if (tried > 1)
errx(1, "No type matched the input value");
/* XXX Use com_err */
switch (ret) {
@@ -493,10 +540,9 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
errx(1, "Could not decode and print data as type %s: "
"End-of-contents tag contains data", typename);
default:
err(1, "Could not decode and print data as type %s", typename);
errx(1, "Could not decode and print data as type %s", typename);
}
}
free(s);
return 0;
}
@@ -536,7 +582,7 @@ doit(char **argv)
}
static int list_types_flag = 0;
static int list_types_flag;
static int version_flag;
static int help_flag;
struct getargs args[] = {
@@ -550,6 +596,12 @@ struct getargs args[] = {
"\ttry all known types", NULL },
{ "raw-sequence", 'S', arg_flag, &sequence_flag,
"\ttry parsing leftover data", NULL },
{ "test-encode", 0, arg_flag, &test_encode_flag,
"\ttest encode round trip (for memory debugging and fuzzing)", NULL },
{ "test-copy", 0, arg_flag, &test_copy_flag,
"\ttest copy operation (for memory debugging and fuzzing)", NULL },
{ "print", 'n', arg_negative_flag, &print_flag,
"\ttest copy operation (for memory debugging and fuzzing)", NULL },
{ "version", 'v', arg_flag, &version_flag, NULL, NULL },
{ "help", 'h', arg_flag, &help_flag, NULL, NULL }
};