Skip to content

Commit

Permalink
Merge pull request #110 from mrc0mmand/variantify-variant-generation
Browse files Browse the repository at this point in the history
rand: make pseudo-random variants a bit more variable
  • Loading branch information
evverx authored Jun 24, 2022
2 parents f5fd3c4 + 8f5e162 commit a7cdc8a
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 231 deletions.
1 change: 1 addition & 0 deletions src/dfuzzer-test-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ static void handle_method_call(

g_variant_get(parameters, "(iu&g@a{ss}@a(uiyo))", &i, &u, &str, NULL, NULL);
g_printf("%s: signature size: %zu\n", method_name, strlen(str));
g_assert_true(g_variant_is_signature(str));

response = g_strdup_printf("%s", str);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", response));
Expand Down
26 changes: 13 additions & 13 deletions src/dfuzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

/* Shared global variables */
/** Maximum buffer size for generated strings by rand module (in Bytes) */
guint64 df_buf_size = MAX_BUF_LEN;
guint64 df_buf_size = MAX_BUFFER_LENGTH;
int df_verbose_flag;
int df_debug_flag;

Expand All @@ -54,7 +54,7 @@ static int df_list_names;
/** Tested process PID */
static int df_pid = -1;
/** NULL terminated struct of methods names which will be skipped from testing */
static struct suppression_item *suppressions[MAXLEN];
static struct suppression_item *suppressions[MAX_SUPPRESSIONS];
/** If -s option is passed 1, otherwise 0 */
static int df_supflg;
/** Suppression file #1 */
Expand Down Expand Up @@ -771,25 +771,25 @@ void df_parse_parameters(int argc, char **argv)
while ((c = getopt_long(argc, argv, "n:o:i:m:b:t:e:L:x:y:f:I:p:sdvlhV", options, NULL)) >= 0) {
switch (c) {
case 'n':
if (strlen(optarg) >= MAXLEN) {
if (strlen(optarg) >= MAX_OBJECT_PATH_LENGTH) {
df_fail("%s: maximum %d characters for option --"
" 'n'\n", argv[0], MAXLEN - 1);
" 'n'\n", argv[0], MAX_OBJECT_PATH_LENGTH - 1);
exit(1);
}
target_proc.name = optarg;
break;
case 'o':
if (strlen(optarg) >= MAXLEN) {
if (strlen(optarg) >= MAX_OBJECT_PATH_LENGTH) {
df_fail("%s: maximum %d characters for option --"
" 'o'\n", argv[0], MAXLEN - 1);
" 'o'\n", argv[0], MAX_OBJECT_PATH_LENGTH - 1);
exit(1);
}
target_proc.obj_path = optarg;
break;
case 'i':
if (strlen(optarg) >= MAXLEN) {
if (strlen(optarg) >= MAX_OBJECT_PATH_LENGTH) {
df_fail("%s: maximum %d characters for option --"
" 'i'\n", argv[0], MAXLEN - 1);
" 'i'\n", argv[0], MAX_OBJECT_PATH_LENGTH - 1);
exit(1);
}
target_proc.interface = optarg;
Expand All @@ -804,8 +804,8 @@ void df_parse_parameters(int argc, char **argv)
exit(1);
}

if (df_buf_size < MINLEN) {
df_fail("Error: at least %d bytes required for the -%c option\n", MINLEN, c);
if (df_buf_size < MIN_BUFFER_LENGTH) {
df_fail("Error: at least %d bytes required for the -%c option\n", MIN_BUFFER_LENGTH, c);
exit(1);
}

Expand Down Expand Up @@ -845,9 +845,9 @@ void df_parse_parameters(int argc, char **argv)
break;
case 'L':
//we need at least 1 more char than usual for directory separator
if (strlen(optarg) >= MAXLEN -1) {
if (strlen(optarg) >= MAX_OBJECT_PATH_LENGTH -1) {
df_fail("%s: maximum %d characters for option --"
" 'L'\n", argv[0], MAXLEN - 1);
" 'L'\n", argv[0], MAX_OBJECT_PATH_LENGTH - 1);
exit(1);
}
log_dir_name = optarg;
Expand Down Expand Up @@ -1011,7 +1011,7 @@ int df_load_suppressions(void)
df_verbose("Found suppressions for bus: '%s'\n", target_proc.name);

i = 0;
while (i < (MAXLEN - 1) && (n = getline(&line, &len, f)) > 0) {
while (i < (MAX_SUPPRESSIONS - 1) && (n = getline(&line, &len, f)) > 0) {
g_autoptr(char) suppression = NULL, description = NULL;
char *p;

Expand Down
8 changes: 1 addition & 7 deletions src/dfuzzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@
#include <errno.h>
#include <unistd.h>

/** Minimal buffer size for generated strings */
#define MINLEN 512

/** Maximum length of strings containing D-Bus name, interface and object path */
#define MAXLEN 256

#define DF_BUS_ROOT_NODE "/"

enum {
Expand All @@ -44,7 +38,7 @@ enum {

/** Structure containing D-Bus name, object path and interface of process. */
struct fuzzing_target {
/* names on D-Bus have the most MAXLEN characters */
/* names on D-Bus have the most MAX_OBJECT_PATH_LENGTH characters */
/** Bus name */
char *name;
/** Object path */
Expand Down
178 changes: 2 additions & 176 deletions src/fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ static void df_fuzz_write_log(const struct df_dbus_method *method, GVariant *val
static int df_exec_cmd_check(const char *cmd);
static int df_fuzz_call_method(const struct df_dbus_method *method, GVariant *value);

guint64 df_get_number_of_iterations(const char *signature) {
guint64 df_get_number_of_iterations(const char *signature)
{
guint64 iterations = 0;
guint32 multiplier = 1, current_nest_level = 0;

Expand Down Expand Up @@ -117,181 +118,6 @@ guint64 df_get_number_of_iterations(const char *signature) {
return CLAMP(iterations, 10, G_MAXUINT64);
}

/* Generate a GVariant with random data for a basic (non-compound) type
*
* Note: variant itself is treated as a basic type, since it's a bit special and
* cannot be iterated on
*/
GVariant *df_generate_random_basic(const GVariantType *type, guint64 iteration) {
g_autoptr(char) ssig = NULL;

if (!type) {
g_assert_not_reached();
return NULL;
}

ssig = g_variant_type_dup_string(type);

if (g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN))
return g_variant_new(ssig, df_rand_gboolean(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE))
return g_variant_new(ssig, df_rand_guint8(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16))
return g_variant_new(ssig, df_rand_gint16(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16))
return g_variant_new(ssig, df_rand_guint16(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32))
return g_variant_new(ssig, df_rand_gint32(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32))
return g_variant_new(ssig, df_rand_guint32(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64))
return g_variant_new(ssig, df_rand_gint64(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64))
return g_variant_new(ssig, df_rand_guint64(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_HANDLE))
return g_variant_new(ssig, df_rand_unixFD(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_DOUBLE))
return g_variant_new(ssig, df_rand_gdouble(iteration));
else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING)) {
g_autoptr(char) str = NULL;

if (df_rand_string(&str, iteration) < 0) {
df_fail("Failed to generate a random string\n");
return NULL;
}

return g_variant_new(ssig, str);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_OBJECT_PATH)) {
g_autoptr(char) obj_path = NULL;

if (df_rand_dbus_objpath_string(&obj_path, iteration) < 0) {
df_fail("Failed to generate a random object path\n");
return NULL;
}

return g_variant_new(ssig, obj_path);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_SIGNATURE)) {
g_autoptr(char) sig_str = NULL;

if (df_rand_dbus_signature_string(&sig_str, iteration) < 0) {
df_fail("Failed to generate a random signature string\n");
return NULL;
}

return g_variant_new(ssig, sig_str);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_VARIANT)) {
GVariant *variant = NULL;

if (df_rand_GVariant(&variant, iteration) < 0) {
df_fail("Failed to generate a random GVariant\n");
return NULL;
}

return g_variant_new(ssig, variant);
} else {
df_fail("Invalid basic type: %s\n", ssig);
g_assert_not_reached();
}

return NULL;
}

GVariant *df_generate_random_from_signature(const char *signature, guint64 iteration)
{
g_autoptr(GVariantType) type = NULL;
g_autoptr(GVariantBuilder) builder = NULL;

if (!signature ||
!g_variant_is_signature(signature) ||
!g_variant_type_string_is_valid(signature)) {
df_fail("Invalid signature: %s\n", signature);
return NULL;
}

type = g_variant_type_new(signature);
/* Leaf nodes */
if (g_variant_type_is_basic(type) || g_variant_type_is_variant(type))
return df_generate_random_basic(type, iteration);

builder = g_variant_builder_new(type);

for (const GVariantType *iter = g_variant_type_first(type);
iter;
iter = g_variant_type_next(iter)) {

g_autoptr(char) ssig = NULL;

ssig = g_variant_type_dup_string(iter);

if (g_variant_type_is_basic(iter) || g_variant_type_is_variant(iter)) {
/* Basic type, generate a random value
* Note: treat 'variant' as a basic type, since it can't
* be iterated on by g_variant_type_{first,next}()
*/
GVariant *basic;

basic = df_generate_random_basic(iter, iteration);
if (!basic)
return NULL;

g_variant_builder_add_value(builder, basic);
} else if (g_variant_type_is_tuple(iter)) {
/* Tuple */
GVariant *tuple = NULL;

tuple = df_generate_random_from_signature(ssig, iteration);
if (!tuple)
return NULL;

g_variant_builder_add_value(builder, tuple);
} else if (g_variant_type_is_array(iter)) {
/* Array */
g_autoptr(char) array_signature = NULL;
const GVariantType *array_type = NULL;
int nest_level = 0;

/* Open the "main" array container */
g_variant_builder_open(builder, iter);

/* Resolve all levels of arrays (e.g. aaaai) */
for (array_type = g_variant_type_element(iter);
g_variant_type_is_array(array_type);
array_type = g_variant_type_element(array_type)) {

/* Open an container for each nested array */
g_variant_builder_open(builder, array_type);
nest_level++;
}

array_signature = g_variant_type_dup_string(array_type);

/* Create a pseudo-randomly sized array */
for (size_t i = 0; i < df_rand_array_size(iteration); i++) {
GVariant *array_item = NULL;

array_item = df_generate_random_from_signature(array_signature, iteration);
if (!array_item)
return NULL;

g_variant_builder_add_value(builder, array_item);
}

/* Close container of each array level */
for (int i = 0; i < nest_level; i++)
g_variant_builder_close(builder);

/* Close the "main" array container */
g_variant_builder_close(builder);
} else {
/* TODO: maybe */
df_fail("Not implemented: %s\n", ssig);
return NULL;
}
}

return g_variant_builder_end(builder);
}

/**
* @function Saves pointer on D-Bus interface proxy for this module to be
* able to call methods through this proxy during fuzz testing.
Expand Down
17 changes: 14 additions & 3 deletions src/fuzz.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@
#ifndef FUZZ_H
#define FUZZ_H

/** Minimal buffer size for generated strings */
#define MIN_BUFFER_LENGTH 512
/** Maximum buffer size for generated strings, default is cca 50 kB */
#define MAX_BUFFER_LENGTH 50000
/** Maximum length of strings containing D-Bus object path */
#define MAX_OBJECT_PATH_LENGTH 256
/** Maximum length of D-Bus signature string */
#define MAXSIG 255
#define MAX_SIGNATURE_LENGTH 255
#define MAX_SUPPRESSIONS 256

/* Basic (non-container) types which can appear in a signature
*
* https://dbus.freedesktop.org/doc/dbus-specification.html#id-1.3.8
*/
#define SIGNATURE_BASIC_TYPES "ybnqiuxtdsogh"

/** Maximum amount of unimportant exceptions for one method; if reached
* testing continues with a next method */
Expand Down Expand Up @@ -61,8 +74,6 @@ G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(df_dbus_method_t, df_dbus_method_clear)
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(df_dbus_property_t, df_dbus_property_clear)

guint64 df_get_number_of_iterations(const char *signature);
GVariant *df_generate_random_basic(const GVariantType *type, guint64 iteration);
GVariant *df_generate_random_from_signature(const char *signature, guint64 iteration);
/**
* @function Saves pointer on D-Bus interface proxy for this module to be
* able to call methods through this proxy during fuzz testing. Also saves
Expand Down
Loading

0 comments on commit a7cdc8a

Please sign in to comment.