From 6d4a35ca57b1af5a7a97faedb8a17c0a00890ce4 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 18 Dec 2019 23:58:49 +0000 Subject: [PATCH] Add diagnostic_metadata and CWE support This patch adds support for associating a diagnostic message with an optional diagnostic_metadata object, so that plugins can add extra data to their diagnostics (e.g. mapping a diagnostic to a taxonomy or coding standard such as from CERT or MISRA). Currently this only supports associating a CWE identifier with a diagnostic (which is what I'm using for the warnings in the analyzer patch kit), but adding a diagnostic_metadata class allows for future growth in this area without an explosion of further "warning_at" overloads for all of the different kinds of custom data that a plugin might want to add. This version of the patch renames the overly-general -fdiagnostics-show-metadata to -fdiagnostics-show-cwe and adds test coverage for it via a plugin. It also adds a note to the documentation that no GCC diagnostics currently use this; it's a feature for plugins (and, at some point, I hope, the analyzer). gcc/ChangeLog: * common.opt (fdiagnostics-show-cwe): Add. * diagnostic-core.h (class diagnostic_metadata): New forward decl. (warning_at): Add overload taking a const diagnostic_metadata &. (emit_diagnostic_valist): Add overload taking a const diagnostic_metadata *. * diagnostic-format-json.cc: Include "diagnostic-metadata.h". (json_from_metadata): New function. (json_end_diagnostic): Call it to add "metadata" child for diagnostics with metadata. (diagnostic_output_format_init): Clear context->show_cwe. * diagnostic-metadata.h: New file. * diagnostic.c: Include "diagnostic-metadata.h". (diagnostic_impl): Add const diagnostic_metadata * param. (diagnostic_n_impl): Likewise. (diagnostic_initialize): Initialize context->show_cwe. (diagnostic_set_info_translated): Initialize diagnostic->metadata. (get_cwe_url): New function. (print_any_cwe): New function. (diagnostic_report_diagnostic): Call print_any_cwe if the diagnostic has non-NULL metadata. (emit_diagnostic): Pass NULL as the metadata in the call to diagnostic_impl. (emit_diagnostic_valist): Likewise. (emit_diagnostic_valist): New overload taking a const diagnostic_metadata *. (inform): Pass NULL as the metadata in the call to diagnostic_impl. (inform_n): Likewise for diagnostic_n_impl. (warning): Likewise. (warning_at): Likewise. Add overload that takes a const diagnostic_metadata &. (warning_n): Pass NULL as the metadata in the call to diagnostic_n_impl. (pedwarn): Likewise for diagnostic_impl. (permerror): Likewise. (error): Likewise. (error_n): Likewise. (error_at): Likewise. (sorry): Likewise. (sorry_at): Likewise. (fatal_error): Likewise. (internal_error): Likewise. (internal_error_no_backtrace): Likewise. * diagnostic.h (diagnostic_info::metadata): New field. (diagnostic_context::show_cwe): New field. * doc/invoke.texi (-fno-diagnostics-show-cwe): New option. * opts.c (common_handle_option): Handle OPT_fdiagnostics_show_cwe. * toplev.c (general_init): Initialize global_dc->show_cwe. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-metadata.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_metadata.c: New test plugin. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them. From-SVN: r279556 --- gcc/ChangeLog | 51 +++++++ gcc/common.opt | 4 + gcc/diagnostic-core.h | 10 ++ gcc/diagnostic-format-json.cc | 24 +++ gcc/diagnostic-metadata.h | 42 ++++++ gcc/diagnostic.c | 142 ++++++++++++++---- gcc/diagnostic.h | 8 + gcc/doc/invoke.texi | 10 ++ gcc/opts.c | 4 + gcc/testsuite/ChangeLog | 6 + .../gcc.dg/plugin/diagnostic-test-metadata.c | 9 ++ .../plugin/diagnostic_plugin_test_metadata.c | 140 +++++++++++++++++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 1 + gcc/toplev.c | 2 + 14 files changed, 422 insertions(+), 31 deletions(-) create mode 100644 gcc/diagnostic-metadata.h create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 825b0776333..5927823a83a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,54 @@ +2019-12-18 David Malcolm + + * common.opt (fdiagnostics-show-cwe): Add. + * diagnostic-core.h (class diagnostic_metadata): New forward decl. + (warning_at): Add overload taking a const diagnostic_metadata &. + (emit_diagnostic_valist): Add overload taking a + const diagnostic_metadata *. + * diagnostic-format-json.cc: Include "diagnostic-metadata.h". + (json_from_metadata): New function. + (json_end_diagnostic): Call it to add "metadata" child for + diagnostics with metadata. + (diagnostic_output_format_init): Clear context->show_cwe. + * diagnostic-metadata.h: New file. + * diagnostic.c: Include "diagnostic-metadata.h". + (diagnostic_impl): Add const diagnostic_metadata * param. + (diagnostic_n_impl): Likewise. + (diagnostic_initialize): Initialize context->show_cwe. + (diagnostic_set_info_translated): Initialize diagnostic->metadata. + (get_cwe_url): New function. + (print_any_cwe): New function. + (diagnostic_report_diagnostic): Call print_any_cwe if the + diagnostic has non-NULL metadata. + (emit_diagnostic): Pass NULL as the metadata in the call to + diagnostic_impl. + (emit_diagnostic_valist): Likewise. + (emit_diagnostic_valist): New overload taking a + const diagnostic_metadata *. + (inform): Pass NULL as the metadata in the call to + diagnostic_impl. + (inform_n): Likewise for diagnostic_n_impl. + (warning): Likewise. + (warning_at): Likewise. Add overload that takes a + const diagnostic_metadata &. + (warning_n): Pass NULL as the metadata in the call to + diagnostic_n_impl. + (pedwarn): Likewise for diagnostic_impl. + (permerror): Likewise. + (error): Likewise. + (error_n): Likewise. + (error_at): Likewise. + (sorry): Likewise. + (sorry_at): Likewise. + (fatal_error): Likewise. + (internal_error): Likewise. + (internal_error_no_backtrace): Likewise. + * diagnostic.h (diagnostic_info::metadata): New field. + (diagnostic_context::show_cwe): New field. + * doc/invoke.texi (-fno-diagnostics-show-cwe): New option. + * opts.c (common_handle_option): Handle OPT_fdiagnostics_show_cwe. + * toplev.c (general_init): Initialize global_dc->show_cwe. + 2019-12-19 Julian Brown Maciej W. Rozycki Tobias Burnus diff --git a/gcc/common.opt b/gcc/common.opt index b4dc31c7490..058da8af877 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1334,6 +1334,10 @@ fdiagnostics-show-option Common Var(flag_diagnostics_show_option) Init(1) Amend appropriate diagnostic messages with the command line option that controls them. +fdiagnostics-show-cwe +Common Var(flag_diagnostics_show_cwe) Init(1) +Print CWE identifiers for diagnostic messages, where available. + fdiagnostics-minimum-margin-width= Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6) Set minimum width of left margin of source code when showing source. diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h index efafde4fa24..2e7f12070fb 100644 --- a/gcc/diagnostic-core.h +++ b/gcc/diagnostic-core.h @@ -45,6 +45,9 @@ class auto_diagnostic_group ~auto_diagnostic_group (); }; +/* Forward decl. */ +class diagnostic_metadata; /* See diagnostic-metadata.h. */ + extern const char *progname; extern const char *trim_filename (const char *); @@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4); extern bool warning_at (rich_location *, int, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4); +extern bool warning_at (rich_location *, const diagnostic_metadata &, int, + const char *, ...) + ATTRIBUTE_GCC_DIAG(4,5); extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2); extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *, const char *, ...) @@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int, const char *, ...) ATTRIBUTE_GCC_DIAG(4,5); extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *, va_list *) ATTRIBUTE_GCC_DIAG (4,0); +extern bool emit_diagnostic_valist (diagnostic_t, rich_location *, + const diagnostic_metadata *metadata, + int, const char *, va_list *) + ATTRIBUTE_GCC_DIAG (5,0); extern bool seen_error (void); #ifdef BUFSIZ diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc index 6782ec9dffb..18f7a56a8db 100644 --- a/gcc/diagnostic-format-json.cc +++ b/gcc/diagnostic-format-json.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "diagnostic.h" +#include "diagnostic-metadata.h" #include "json.h" #include "selftest.h" @@ -103,6 +104,20 @@ json_from_fixit_hint (const fixit_hint *hint) return fixit_obj; } +/* Generate a JSON object for METADATA. */ + +static json::object * +json_from_metadata (const diagnostic_metadata *metadata) +{ + json::object *metadata_obj = new json::object (); + + if (metadata->get_cwe ()) + metadata_obj->set ("cwe", + new json::integer_number (metadata->get_cwe ())); + + return metadata_obj; +} + /* No-op implementation of "begin_diagnostic" for JSON output. */ static void @@ -211,6 +226,12 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic, TODO: functions TODO: inlining information TODO: macro expansion information. */ + + if (diagnostic->metadata) + { + json::object *metadata_obj = json_from_metadata (diagnostic->metadata); + diag_obj->set ("metadata", metadata_obj); + } } /* No-op implementation of "begin_group_cb" for JSON output. */ @@ -268,6 +289,9 @@ diagnostic_output_format_init (diagnostic_context *context, context->end_group_cb = json_end_group; context->final_cb = json_final_cb; + /* The metadata is handled in JSON format, rather than as text. */ + context->show_cwe = false; + /* The option is handled in JSON format, rather than as text. */ context->show_option_requested = false; diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h new file mode 100644 index 00000000000..a759d44fa44 --- /dev/null +++ b/gcc/diagnostic-metadata.h @@ -0,0 +1,42 @@ +/* Additional metadata for a diagnostic. + Copyright (C) 2019 Free Software Foundation, Inc. + Contributed by David Malcolm + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_DIAGNOSTIC_METADATA_H +#define GCC_DIAGNOSTIC_METADATA_H + +/* A bundle of additional metadata that can be associated with a + diagnostic. + + Currently this only supports associating a CWE identifier with a + diagnostic. */ + +class diagnostic_metadata +{ + public: + diagnostic_metadata () : m_cwe (0) {} + + void add_cwe (int cwe) { m_cwe = cwe; } + int get_cwe () const { return m_cwe; } + + private: + int m_cwe; +}; + +#endif /* ! GCC_DIAGNOSTIC_METADATA_H */ diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index d6604e6fd41..95cfb6e76ac 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "diagnostic-color.h" #include "diagnostic-url.h" +#include "diagnostic-metadata.h" #include "edit-context.h" #include "selftest.h" #include "selftest-diagnostic.h" @@ -58,11 +59,13 @@ along with GCC; see the file COPYING3. If not see #define permissive_error_option(DC) ((DC)->opt_permissive) /* Prototypes. */ -static bool diagnostic_impl (rich_location *, int, const char *, - va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(3,0); -static bool diagnostic_n_impl (rich_location *, int, unsigned HOST_WIDE_INT, +static bool diagnostic_impl (rich_location *, const diagnostic_metadata *, + int, const char *, + va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(4,0); +static bool diagnostic_n_impl (rich_location *, const diagnostic_metadata *, + int, unsigned HOST_WIDE_INT, const char *, const char *, va_list *, - diagnostic_t) ATTRIBUTE_GCC_DIAG(5,0); + diagnostic_t) ATTRIBUTE_GCC_DIAG(6,0); static void error_recursion (diagnostic_context *) ATTRIBUTE_NORETURN; static void real_abort (void) ATTRIBUTE_NORETURN; @@ -183,6 +186,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer)); for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++) context->caret_chars[i] = '^'; + context->show_cwe = false; context->show_option_requested = false; context->abort_on_error = false; context->show_column = false; @@ -299,6 +303,7 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg, diagnostic->message.format_spec = msg; diagnostic->message.m_richloc = richloc; diagnostic->richloc = richloc; + diagnostic->metadata = NULL; diagnostic->kind = kind; diagnostic->option_index = 0; } @@ -898,6 +903,47 @@ update_effective_level_from_pragmas (diagnostic_context *context, return diag_class; } +/* Generate a URL string describing CWE. The caller is responsible for + freeing the string. */ + +static char * +get_cwe_url (int cwe) +{ + return xasprintf ("https://cwe.mitre.org/data/definitions/%i.html", cwe); +} + +/* If DIAGNOSTIC has a CWE identifier, print it. + + For example, if the diagnostic metadata associates it with CWE-119, + " [CWE-119]" will be printed, suitably colorized, and with a URL of a + description of the security issue. */ + +static void +print_any_cwe (diagnostic_context *context, + const diagnostic_info *diagnostic) +{ + if (diagnostic->metadata == NULL) + return; + + int cwe = diagnostic->metadata->get_cwe (); + if (cwe) + { + pretty_printer *pp = context->printer; + char *saved_prefix = pp_take_prefix (context->printer); + pp_string (pp, " ["); + pp_string (pp, colorize_start (pp_show_color (pp), + diagnostic_kind_color[diagnostic->kind])); + char *cwe_url = get_cwe_url (cwe); + pp_begin_url (pp, cwe_url); + free (cwe_url); + pp_printf (pp, "CWE-%i", cwe); + pp_set_prefix (context->printer, saved_prefix); + pp_end_url (pp); + pp_string (pp, colorize_stop (pp_show_color (pp))); + pp_character (pp, ']'); + } +} + /* Print any metadata about the option used to control DIAGNOSTIC to CONTEXT's printer, e.g. " [-Werror=uninitialized]". Subroutine of diagnostic_report_diagnostic. */ @@ -1058,6 +1104,8 @@ diagnostic_report_diagnostic (diagnostic_context *context, pp_format (context->printer, &diagnostic->message); (*diagnostic_starter (context)) (context, diagnostic); pp_output_formatted_text (context->printer); + if (context->show_cwe) + print_any_cwe (context, diagnostic); if (context->show_option_requested) print_option_information (context, diagnostic, orig_diag_kind); (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind); @@ -1183,8 +1231,8 @@ diagnostic_append_note (diagnostic_context *context, permerror, error, error_at, error_at, sorry, fatal_error, internal_error, and internal_error_no_backtrace, as documented and defined below. */ static bool -diagnostic_impl (rich_location *richloc, int opt, - const char *gmsgid, +diagnostic_impl (rich_location *richloc, const diagnostic_metadata *metadata, + int opt, const char *gmsgid, va_list *ap, diagnostic_t kind) { diagnostic_info diagnostic; @@ -1200,13 +1248,15 @@ diagnostic_impl (rich_location *richloc, int opt, if (kind == DK_WARNING || kind == DK_PEDWARN) diagnostic.option_index = opt; } + diagnostic.metadata = metadata; return diagnostic_report_diagnostic (global_dc, &diagnostic); } /* Implement inform_n, warning_n, and error_n, as documented and defined below. */ static bool -diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n, +diagnostic_n_impl (rich_location *richloc, const diagnostic_metadata *metadata, + int opt, unsigned HOST_WIDE_INT n, const char *singular_gmsgid, const char *plural_gmsgid, va_list *ap, diagnostic_t kind) @@ -1226,6 +1276,7 @@ diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n, diagnostic_set_info_translated (&diagnostic, text, ap, richloc, kind); if (kind == DK_WARNING) diagnostic.option_index = opt; + diagnostic.metadata = metadata; return diagnostic_report_diagnostic (global_dc, &diagnostic); } @@ -1239,7 +1290,7 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt, va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, location); - bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, kind); + bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, kind); va_end (ap); return ret; } @@ -1253,7 +1304,7 @@ emit_diagnostic (diagnostic_t kind, rich_location *richloc, int opt, auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, kind); + bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, kind); va_end (ap); return ret; } @@ -1265,7 +1316,18 @@ emit_diagnostic_valist (diagnostic_t kind, location_t location, int opt, const char *gmsgid, va_list *ap) { rich_location richloc (line_table, location); - return diagnostic_impl (&richloc, opt, gmsgid, ap, kind); + return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind); +} + +/* Wrapper around diagnostic_impl taking a va_list parameter. */ + +bool +emit_diagnostic_valist (diagnostic_t kind, rich_location *richloc, + const diagnostic_metadata *metadata, + int opt, + const char *gmsgid, va_list *ap) +{ + return diagnostic_impl (richloc, metadata, opt, gmsgid, ap, kind); } /* An informative note at LOCATION. Use this for additional details on an error @@ -1277,7 +1339,7 @@ inform (location_t location, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, location); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_NOTE); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_NOTE); va_end (ap); } @@ -1290,7 +1352,7 @@ inform (rich_location *richloc, const char *gmsgid, ...) auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - diagnostic_impl (richloc, -1, gmsgid, &ap, DK_NOTE); + diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_NOTE); va_end (ap); } @@ -1304,7 +1366,7 @@ inform_n (location_t location, unsigned HOST_WIDE_INT n, va_start (ap, plural_gmsgid); auto_diagnostic_group d; rich_location richloc (line_table, location); - diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid, + diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid, &ap, DK_NOTE); va_end (ap); } @@ -1319,7 +1381,7 @@ warning (int opt, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, input_location); - bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING); + bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING); va_end (ap); return ret; } @@ -1335,7 +1397,7 @@ warning_at (location_t location, int opt, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, location); - bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING); + bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING); va_end (ap); return ret; } @@ -1350,7 +1412,25 @@ warning_at (rich_location *richloc, int opt, const char *gmsgid, ...) auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_WARNING); + bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_WARNING); + va_end (ap); + return ret; +} + +/* Same as "warning at" above, but using METADATA. */ + +bool +warning_at (rich_location *richloc, const diagnostic_metadata &metadata, + int opt, const char *gmsgid, ...) +{ + gcc_assert (richloc); + + auto_diagnostic_group d; + va_list ap; + va_start (ap, gmsgid); + bool ret + = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap, + DK_WARNING); va_end (ap); return ret; } @@ -1366,7 +1446,7 @@ warning_n (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n, auto_diagnostic_group d; va_list ap; va_start (ap, plural_gmsgid); - bool ret = diagnostic_n_impl (richloc, opt, n, + bool ret = diagnostic_n_impl (richloc, NULL, opt, n, singular_gmsgid, plural_gmsgid, &ap, DK_WARNING); va_end (ap); @@ -1385,7 +1465,7 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n, va_list ap; va_start (ap, plural_gmsgid); rich_location richloc (line_table, location); - bool ret = diagnostic_n_impl (&richloc, opt, n, + bool ret = diagnostic_n_impl (&richloc, NULL, opt, n, singular_gmsgid, plural_gmsgid, &ap, DK_WARNING); va_end (ap); @@ -1412,7 +1492,7 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, location); - bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_PEDWARN); + bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN); va_end (ap); return ret; } @@ -1427,7 +1507,7 @@ pedwarn (rich_location *richloc, int opt, const char *gmsgid, ...) auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_PEDWARN); + bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN); va_end (ap); return ret; } @@ -1446,7 +1526,7 @@ permerror (location_t location, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, location); - bool ret = diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_PERMERROR); + bool ret = diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR); va_end (ap); return ret; } @@ -1461,7 +1541,7 @@ permerror (rich_location *richloc, const char *gmsgid, ...) auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - bool ret = diagnostic_impl (richloc, -1, gmsgid, &ap, DK_PERMERROR); + bool ret = diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR); va_end (ap); return ret; } @@ -1475,7 +1555,7 @@ error (const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, input_location); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR); va_end (ap); } @@ -1489,7 +1569,7 @@ error_n (location_t location, unsigned HOST_WIDE_INT n, va_list ap; va_start (ap, plural_gmsgid); rich_location richloc (line_table, location); - diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid, + diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid, &ap, DK_ERROR); va_end (ap); } @@ -1502,7 +1582,7 @@ error_at (location_t loc, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, loc); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR); va_end (ap); } @@ -1516,7 +1596,7 @@ error_at (rich_location *richloc, const char *gmsgid, ...) auto_diagnostic_group d; va_list ap; va_start (ap, gmsgid); - diagnostic_impl (richloc, -1, gmsgid, &ap, DK_ERROR); + diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_ERROR); va_end (ap); } @@ -1530,7 +1610,7 @@ sorry (const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, input_location); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY); va_end (ap); } @@ -1542,7 +1622,7 @@ sorry_at (location_t loc, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, loc); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY); va_end (ap); } @@ -1564,7 +1644,7 @@ fatal_error (location_t loc, const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, loc); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_FATAL); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_FATAL); va_end (ap); gcc_unreachable (); @@ -1581,7 +1661,7 @@ internal_error (const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, input_location); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE); va_end (ap); gcc_unreachable (); @@ -1597,7 +1677,7 @@ internal_error_no_backtrace (const char *gmsgid, ...) va_list ap; va_start (ap, gmsgid); rich_location richloc (line_table, input_location); - diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE_NOBT); + diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE_NOBT); va_end (ap); gcc_unreachable (); diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 91e4c509605..3a49c99e3af 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -46,6 +46,10 @@ struct diagnostic_info /* The location at which the diagnostic is to be reported. */ rich_location *richloc; + /* An optional bundle of metadata associated with the diagnostic + (or NULL). */ + const diagnostic_metadata *metadata; + /* Auxiliary data for client. */ void *x_data; /* The kind of diagnostic it is about. */ @@ -126,6 +130,10 @@ struct diagnostic_context /* Character used for caret diagnostics. */ char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES]; + /* True if we should print any CWE identifiers associated with + diagnostics. */ + bool show_cwe; + /* True if we should print the command line option which controls each diagnostic, if known. */ bool show_option_requested; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 1493e8d633c..8c3446e31bb 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -277,6 +277,7 @@ Objective-C and Objective-C++ Dialects}. -fdiagnostics-format=@r{[}text@r{|}json@r{]} @gol -fno-diagnostics-show-option -fno-diagnostics-show-caret @gol -fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol +-fno-diagnostics-show-cwe @gol -fdiagnostics-minimum-margin-width=@var{width} @gol -fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol -fdiagnostics-show-template-tree -fno-elide-type @gol @@ -4005,6 +4006,15 @@ as the types of expressions: This option suppresses the printing of these labels (in the example above, the vertical bars and the ``char *'' and ``long int'' text). +@item -fno-diagnostics-show-cwe +@opindex fno-diagnostics-show-cwe +@opindex fdiagnostics-show-cwe +Diagnostic messages can optionally have an associated +@url{https://cwe.mitre.org/index.html, CWE} identifier. +GCC itself does not do this for any of its diagnostics, but plugins may do so. +By default, if this information is present, it will be printed with +the diagnostic. This option suppresses the printing of this metadata. + @item -fno-diagnostics-show-line-numbers @opindex fno-diagnostics-show-line-numbers @opindex fdiagnostics-show-line-numbers diff --git a/gcc/opts.c b/gcc/opts.c index df6cc6495db..d4a98c8d942 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -2407,6 +2407,10 @@ common_handle_option (struct gcc_options *opts, dc->parseable_fixits_p = value; break; + case OPT_fdiagnostics_show_cwe: + dc->show_cwe = value; + break; + case OPT_fdiagnostics_show_option: dc->show_option_requested = value; break; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7fd6710168e..18b4ba31348 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-12-18 David Malcolm + + * gcc.dg/plugin/diagnostic-test-metadata.c: New test. + * gcc.dg/plugin/diagnostic_plugin_test_metadata.c: New test plugin. + * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them. + 2019-12-19 Jakub Jelinek PR fortran/92977 diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c new file mode 100644 index 00000000000..d2babd35753 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ + +extern char *gets (char *s); + +void test_cwe (void) +{ + char buf[1024]; + gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\]" } */ +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c new file mode 100644 index 00000000000..5e58115afba --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c @@ -0,0 +1,140 @@ +/* This plugin exercises diagnostic_metadata. */ + +#include "gcc-plugin.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "stringpool.h" +#include "toplev.h" +#include "basic-block.h" +#include "hash-table.h" +#include "vec.h" +#include "ggc.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "tree.h" +#include "tree-pass.h" +#include "intl.h" +#include "plugin-version.h" +#include "diagnostic.h" +#include "context.h" +#include "gcc-rich-location.h" +#include "diagnostic-metadata.h" + +int plugin_is_GPL_compatible; + +const pass_data pass_data_test_metadata = +{ + GIMPLE_PASS, /* type */ + "test_metadata", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_test_metadata : public gimple_opt_pass +{ +public: + pass_test_metadata(gcc::context *ctxt) + : gimple_opt_pass(pass_data_test_metadata, ctxt) + {} + + /* opt_pass methods: */ + bool gate (function *) { return true; } + virtual unsigned int execute (function *); + +}; // class pass_test_metadata + +/* Determine if STMT is a call with NUM_ARGS arguments to a function + named FUNCNAME. + If so, return STMT as a gcall *. Otherwise return NULL. */ + +static gcall * +check_for_named_call (gimple *stmt, + const char *funcname, unsigned int num_args) +{ + gcc_assert (funcname); + + gcall *call = dyn_cast (stmt); + if (!call) + return NULL; + + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) + return NULL; + + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname)) + return NULL; + + if (gimple_call_num_args (call) != num_args) + { + error_at (stmt->location, "expected number of args: %i (got %i)", + num_args, gimple_call_num_args (call)); + return NULL; + } + + return call; +} + +/* Exercise diagnostic_metadata. */ + +unsigned int +pass_test_metadata::execute (function *fun) +{ + gimple_stmt_iterator gsi; + basic_block bb; + + FOR_EACH_BB_FN (bb, fun) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + /* Example of CWE: complain about uses of gets. */ + if (gcall *call = check_for_named_call (stmt, "gets", 1)) + { + gcc_rich_location richloc (gimple_location (call)); + /* CWE-242: Use of Inherently Dangerous Function. */ + diagnostic_metadata m; + m.add_cwe (242); + warning_at (&richloc, m, 0, + "never use %qs", "gets"); + } + } + + return 0; +} + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + struct register_pass_info pass_info; + const char *plugin_name = plugin_info->base_name; + int argc = plugin_info->argc; + struct plugin_argument *argv = plugin_info->argv; + + if (!plugin_default_version_check (version, &gcc_version)) + return 1; + + pass_info.pass = new pass_test_metadata (g); + pass_info.reference_pass_name = "ssa"; + pass_info.ref_pass_instance_number = 1; + pass_info.pos_op = PASS_POS_INSERT_AFTER; + register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &pass_info); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index 2f75463f3d8..439fbd7224f 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -94,6 +94,7 @@ set plugin_test_list [list \ diagnostic-test-inlining-2.c \ diagnostic-test-inlining-3.c \ diagnostic-test-inlining-4.c } \ + { diagnostic_plugin_test_metadata.c diagnostic-test-metadata.c } \ { location_overflow_plugin.c \ location-overflow-test-1.c \ location-overflow-test-2.c \ diff --git a/gcc/toplev.c b/gcc/toplev.c index 059046f40f3..6f5b53aaac2 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1179,6 +1179,8 @@ general_init (const char *argv0, bool init_signals) = global_options_init.x_flag_diagnostics_show_labels; global_dc->show_line_numbers_p = global_options_init.x_flag_diagnostics_show_line_numbers; + global_dc->show_cwe + = global_options_init.x_flag_diagnostics_show_cwe; global_dc->show_option_requested = global_options_init.x_flag_diagnostics_show_option; global_dc->min_margin_width -- 2.30.2