diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2014-11-17 11:43:13 +0000 |
---|---|---|
committer | Philip Withnall <philip.withnall@collabora.co.uk> | 2014-11-17 11:46:16 +0000 |
commit | 983fa59d9b13c050f58e3f5716d89600551532fa (patch) | |
tree | 00b0ba9795579ce95cf6e51194714adbe79d6593 /tests | |
parent | 6faaa0a0f28bf18e4ff261c2b80ce7a23935a8df (diff) |
gerror: Add a GError checker
This is a path-sensitive checker which detects various invalid usages of
the GError API from GLib, such as overwriting GErrors which have not
been cleared to NULL, or double-freeing GErrors. It can statically
detect all run time errors from preconditions in GError methods.
Includes a full unit test suite with > 95% coverage.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/gerror-api.c | 665 | ||||
-rw-r--r-- | tests/gerror.head.c | 16 | ||||
-rw-r--r-- | tests/gerror.tail.c | 17 |
4 files changed, 701 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 3fc9e91..e14fb6e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,6 +16,7 @@ c_tests = \ gvariant-lookup.c \ gvariant-new.c \ nonnull.c \ + gerror-api.c \ $(NULL) templates = \ @@ -25,6 +26,8 @@ templates = \ assertion-return.tail.c \ generic.head.c \ generic.tail.c \ + gerror.head.c \ + gerror.tail.c \ gsignal.head.c \ gsignal.tail.c \ gvariant.head.c \ diff --git a/tests/gerror-api.c b/tests/gerror-api.c new file mode 100644 index 0000000..6d03b1a --- /dev/null +++ b/tests/gerror-api.c @@ -0,0 +1,665 @@ +/* Template: gerror */ + +/* + * No error + */ +{ + GError *some_error = g_error_new (G_IO_ERROR, + G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); +} + +/* + * No error + */ +{ + GError *some_error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); +} + +/* + * No error + */ +{ + GError *some_error; + + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + + if (some_error == NULL) { + // Should never be reached, so we should never get an error + // about a double-free. + g_error_free (some_error); + } + + g_error_free (some_error); +} + +/* + * warning: Freeing non-set GError + * g_error_free (NULL); + * ^~~~~~~~~~~~~~~~~~~ + */ +{ + g_error_free (NULL); +} + +/* + * warning: Freeing non-set GError + * g_error_free (some_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + g_error_free (some_error); +} + +/* + * warning: Using uninitialized GError + * g_error_free (some_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error; // uninitialised + g_error_free (some_error); +} + +/* + * warning: Freeing non-set GError + * g_error_free (some_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_clear_error (&some_error); + + g_error_free (some_error); +} + +/* + * No error + */ +{ + GError *some_error = NULL; + + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); + + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "new"); + g_error_free (some_error); +} + +/* + * warning: Overwriting already-freed GError + * g_set_error (&some_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); + + g_set_error (&some_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + g_clear_error (&some_error); +} + +/* + * No error + */ +{ + GError *some_error = NULL; + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); +} + +/* + * warning: Freeing already-freed GError + * g_error_free (some_error); // double free + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); + g_error_free (some_error); // double free +} + +/* + * No error + */ +{ + GError *some_error; // don’t explicitly initialise + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (some_error); +} + +/* + * warning: Freeing non-set GError + * g_error_free (some_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + if (some_cond) { + // Bug: @some_error is not set on the FALSE path. + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + } + g_error_free (some_error); +} + +/* + * warning: Overwriting already-set GError + * some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); +} + +/* + * warning: Overwriting already-set GError + * some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *some_error = NULL; + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + g_error_free (some_error); +} + +/* + * No error + */ +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); +} + +/* + * No error + */ +{ + g_set_error (NULL, G_IO_ERROR, G_IO_ERROR_FAILED, "no-op"); +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + + if (sub_error == NULL) { + // Should never be reached, so we should never get an error + // about double-freeing. + g_error_free (sub_error); + } + + g_error_free (sub_error); +} + +/* + * No error + */ +{ + GError *child_error = NULL; + g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "no-op"); + g_error_free (child_error); +} + +/* + * warning: Using uninitialized GError + * g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "non-clear"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *child_error; // uninitialised + g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "non-clear"); + g_error_free (child_error); +} + +/* + * warning: Overwriting already-set GError + * g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); +} + +/* + * warning: Overwriting already-set GError + * g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + g_clear_error (error); +} + +/* + * No error + */ +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_clear_error (error); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah again!"); +} + +/* + * warning: Overwriting already-set GError + * g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, "Pending error"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + guint i; + GError *child_error = NULL; + + for (i = 0; i < 5; i++) { + some_failing_func (&child_error); + // ^-- bug: can overwrite @child_error when i > 0 + } +} + +/* + * warning: Overwriting already-set GError + * g_propagate_error (error, sub_error2); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error1, *sub_error2; + + sub_error1 = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure 1"); + sub_error2 = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure 2"); + + g_propagate_error (error, sub_error1); + g_propagate_error (error, sub_error2); +} + +/* + * warning: Overwriting already-set GError + * g_propagate_error (error, sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + guint i; + + for (i = 0; i < 5; i++) { + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, + "Failure"); + g_propagate_error (error, sub_error); + // ^-- bug: will overwrite @error when i > 0 + } +} + +/* + * warning: Freeing already-freed GError + * g_error_free (main_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, + "Failure"); + g_propagate_error (error, sub_error); + g_error_free (sub_error); + // ^-- sort-of-bug: This is the first time @sub_error is freed, but it + // means a dangling pointer is returned to the caller in @error + // FIXME: The dangling pointer should be detected and warned about + // more specifically. +} + +/* + * warning: Freeing already-freed GError + * g_error_free (main_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (error, sub_error); + g_clear_error (&sub_error); + // ^-- sort-of-bug: This is the first time @sub_error is freed, but it + // means a dangling pointer is returned to the caller in @error + // FIXME: The dangling pointer should be detected and warned about + // more specifically. +} + +/* + * warning: Freeing already-freed GError + * g_clear_error (&sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (NULL, sub_error); + g_clear_error (&sub_error); + // ^-- bug: @sub_error has already effectively been freed +} + +/* + * warning: Overwriting already-set GError + * sub_error = NULL; + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (error, sub_error); + // FIXME: false positive on this line; should be no error: + sub_error = NULL; + g_clear_error (&sub_error); +} + +/* + * No error + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_prefixed_error (error, sub_error, "prefix: "); +} + +/* + * warning: Freeing already-freed GError + * g_clear_error (&sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_prefixed_error (NULL, sub_error, "prefix: "); + g_clear_error (&sub_error); + // ^-- bug: @sub_error has already effectively been freed +} + +/* + * warning: Freeing non-set GError + * g_propagate_error (error, sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + g_propagate_error (error, sub_error); +} + +/* + * warning: Freeing already-freed GError + * g_propagate_error (error, sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_error_free (sub_error); + + g_propagate_error (error, sub_error); +} + +/* + * No error + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (NULL, sub_error); +} + +/* + * warning: Using uninitialized GError + * g_propagate_error (&dest_error, sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + GError *dest_error; // uninitialised + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (&dest_error, sub_error); +} + +/* + * No error + */ +{ + GError *sub_error; + GError *dest_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (&dest_error, sub_error); + + if (dest_error == NULL) { + // Should never be reached, so this double-free should never be + // a warning. + g_error_free (sub_error); + } +} + +/* + * No error + */ +{ + GError *sub_error; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure"); + g_propagate_error (error, sub_error); + + if (error != NULL && *error == NULL) { + // Should never be reached, so this double-free should never be + // a warning. + g_error_free (sub_error); + } +} + +/* + * warning: Using uninitialized GError + * g_clear_error (&sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error; + g_clear_error (&sub_error); +} + +/* + * No error + */ +{ + g_clear_error (NULL); +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + g_clear_error (&sub_error); +} + +/* + * warning: Freeing already-freed GError + * g_clear_error (&sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + + g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_error_free (sub_error); + g_clear_error (&sub_error); +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_clear_error (&sub_error); + + if (sub_error != NULL) { + // Should never be reached, so we should never get an error. + g_error_free (sub_error); + } +} + +/* + * No error + */ +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); +} + +/* + * warning: Overwriting already-set GError + * g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + if (rand ()) { + g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "blah"); + } + + g_clear_error (&sub_error); +} + +/* + * warning: Overwriting already-set GError + * g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original"); + + if (rand ()) { + g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + } + + g_clear_error (&sub_error); +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original"); + + if (rand ()) { + g_propagate_error (error, sub_error); + } else { + g_error_free (sub_error); + } +} + +/* + * warning: Overwriting already-set GError + * g_propagate_error (&sub_error, sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original"); + + if (rand ()) { + g_propagate_error (&sub_error, sub_error); + } else { + g_error_free (sub_error); + } +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original"); + + if (rand ()) { + g_clear_error (&sub_error); + } + + g_clear_error (&sub_error); +} + +/* + * warning: Freeing already-freed GError + * g_clear_error (&sub_error); + * ^~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original"); + g_error_free (sub_error); + + if (rand ()) { + g_clear_error (&sub_error); + } +} + +/* + * No error + */ +{ + GError *sub_error = NULL; + + if (rand ()) { + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "1"); + } else { + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "2"); + } + + g_error_free (sub_error); +} + +/* + * warning: Overwriting already-set GError + * sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + * ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +{ + GError *sub_error = NULL; + + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "1"); + + if (rand ()) { + sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe"); + } + + g_error_free (sub_error); +} diff --git a/tests/gerror.head.c b/tests/gerror.head.c new file mode 100644 index 0000000..8aa7e4c --- /dev/null +++ b/tests/gerror.head.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +static void +some_failing_func (GError **error) +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, "Pending error"); +} + +static void +some_failable_func (gboolean some_cond, GError **error) +{ diff --git a/tests/gerror.tail.c b/tests/gerror.tail.c new file mode 100644 index 0000000..647ca4a --- /dev/null +++ b/tests/gerror.tail.c @@ -0,0 +1,17 @@ + /* End of some_failable_func(). */ +} + +int +main (void) +{ + GError *main_error = NULL; + + some_failable_func (rand (), &main_error); + + if (main_error != NULL) { + g_error_free (main_error); + return 1; + } + + return 0; +} |