diagnostics, analyzer: add optional per-diagnostic property bags to SARIF

I've found it useful in debugging the analyzer for the SARIF output to
contain extra analyzer-specific data in each diagnostic.

This patch:
* adds a way for a diagnostic_metadata to populate a property
bag within a SARIF "result" object based on a new vfunc
* reworks how diagnostics are emitted within the analyzer so
that a custom diagnostic_metadata subclass is used, which populates
the property bag with information from the saved_diagnostic, and with
a vfunc hook allowing for per-pending_diagnotic-subclass extra
properties.

Doing so makes it trivial to go from the SARIF output back to
pertinent parts of the analyzer's internals (e.g. the index of
the diagnostic within the ana::diagnostic_manager, the index of
the ana::exploded_node, etc).

It also replaces a lot of boilerplate in the "emit" implementations
in the various pending_diagnostics subclasses.  In particular, doing
so fixes missing CVE metadata for -Wanalyzer-fd-phase-mismatch (where
sm-fd.cc's fd_phase_mismatch::emit was failing to use its
diagnostic_metadata instance).

gcc/analyzer/ChangeLog:
	* analyzer.h (class saved_diagnostic): New forward decl.
	* bounds-checking.cc: Update for changes to
	pending_diagnostic::emit.
	* call-details.cc: Likewise.
	* diagnostic-manager.cc: Include "diagnostic-format-sarif.h".
	(saved_diagnostic::maybe_add_sarif_properties): New.
	(class pending_diagnostic_metadata): New.
	(diagnostic_manager::emit_saved_diagnostic): Create a
	pending_diagnostic_metadata and a diagnostic_emission_context.
	Pass the latter to the pending_diagnostic::emit vfunc.
	* diagnostic-manager.h
	(saved_diagnostic::maybe_add_sarif_properties): New decl.
	* engine.cc: Update for changes to pending_diagnostic::emit.
	* infinite-loop.cc: Likewise.
	* infinite-recursion.cc: Likewise.
	* kf-analyzer.cc: Likewise.
	* kf.cc: Likewise.
	* pending-diagnostic.cc
	(diagnostic_emission_context::get_pending_diagnostic): New.
	(diagnostic_emission_context::warn): New.
	(diagnostic_emission_context::inform): New.
	* pending-diagnostic.h (class diagnostic_emission_context): New.
	(pending_diagnostic::emit): Update params.
	(pending_diagnostic::maybe_add_sarif_properties): New vfunc.
	* region.cc: Don't include "diagnostic-metadata.h".
	* region-model.cc: Include "diagnostic-format-sarif.h".  Update
	for changes to pending_diagnostic::emit.
	(exposure_through_uninit_copy::maybe_add_sarif_properties): New.
	* sm-fd.cc: Update for changes to pending_diagnostic::emit.
	* sm-file.cc: Likewise.
	* sm-malloc.cc: Likewise.
	* sm-pattern-test.cc: Likewise.
	* sm-sensitive.cc: Likewise.
	* sm-signal.cc: Likewise.
	* sm-taint.cc: Likewise.
	* store.cc: Don't include "diagnostic-metadata.h".
	* varargs.cc: Update for changes to pending_diagnostic::emit.

gcc/ChangeLog:
	* diagnostic-core.h (emit_diagnostic_valist): New overload decl.
	* diagnostic-format-sarif.cc (sarif_builder::make_result_object):
	When we have metadata, call its maybe_add_sarif_properties vfunc.
	* diagnostic-metadata.h (class sarif_object): Forward decl.
	(diagnostic_metadata::~diagnostic_metadata): New.
	(diagnostic_metadata::maybe_add_sarif_properties): New vfunc.
	* diagnostic.cc (emit_diagnostic_valist): New overload.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/fd-accept.c: Update for fix to missing CWE
	metadata for -Wanalyzer-fd-phase-mismatch.
	* gcc.dg/analyzer/fd-bind.c: Likewise.
	* gcc.dg/analyzer/fd-socket-misuse.c: Likewise.
	* gcc.dg/plugin/analyzer_cpython_plugin.c: Update for changes to
	pending_diagnostic::emit.
	* gcc.dg/plugin/analyzer_gil_plugin.c: Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2023-12-01 08:47:41 -05:00
parent 83b210d55b
commit 12b67d1e13
32 changed files with 550 additions and 533 deletions

View File

@ -94,6 +94,7 @@ class bounded_ranges_manager;
struct pending_location; struct pending_location;
class pending_diagnostic; class pending_diagnostic;
class pending_note; class pending_note;
class saved_diagnostic;
struct event_loc_info; struct event_loc_info;
class checker_event; class checker_event;
class state_change_event; class state_change_event;

View File

@ -30,7 +30,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "gimple-iterator.h" #include "gimple-iterator.h"
#include "diagnostic-core.h" #include "diagnostic-core.h"
#include "diagnostic-metadata.h"
#include "diagnostic-diagram.h" #include "diagnostic-diagram.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -119,10 +118,10 @@ protected:
} }
void void
maybe_show_notes (location_t loc, logger *logger) const maybe_show_notes (diagnostic_emission_context &ctxt) const
{ {
maybe_describe_array_bounds (loc); maybe_describe_array_bounds (ctxt.get_location ());
maybe_show_diagram (logger); maybe_show_diagram (ctxt.get_logger ());
} }
/* Potentially add a note about valid ways to index this array, such /* Potentially add a note about valid ways to index this array, such
@ -281,27 +280,22 @@ public:
return "concrete_buffer_overflow"; return "concrete_buffer_overflow";
} }
bool emit (rich_location *rich_loc, bool emit (diagnostic_emission_context &ctxt) final override
logger *logger) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
m.add_cwe (787); ctxt.add_cwe (787);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer overflow");
"buffer overflow");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
m.add_cwe (121); ctxt.add_cwe (121);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer overflow");
"stack-based buffer overflow");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
m.add_cwe (122); ctxt.add_cwe (122);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer overflow");
"heap-based buffer overflow");
break; break;
} }
@ -312,25 +306,25 @@ public:
unsigned HOST_WIDE_INT num_bad_bytes unsigned HOST_WIDE_INT num_bad_bytes
= m_out_of_bounds_range.m_size_in_bytes.to_uhwi (); = m_out_of_bounds_range.m_size_in_bytes.to_uhwi ();
if (m_diag_arg) if (m_diag_arg)
inform_n (rich_loc->get_loc (), inform_n (ctxt.get_location (),
num_bad_bytes, num_bad_bytes,
"write of %wu byte to beyond the end of %qE", "write of %wu byte to beyond the end of %qE",
"write of %wu bytes to beyond the end of %qE", "write of %wu bytes to beyond the end of %qE",
num_bad_bytes, num_bad_bytes,
m_diag_arg); m_diag_arg);
else else
inform_n (rich_loc->get_loc (), inform_n (ctxt.get_location (),
num_bad_bytes, num_bad_bytes,
"write of %wu byte to beyond the end of the region", "write of %wu byte to beyond the end of the region",
"write of %wu bytes to beyond the end of the region", "write of %wu bytes to beyond the end of the region",
num_bad_bytes); num_bad_bytes);
} }
else if (m_diag_arg) else if (m_diag_arg)
inform (rich_loc->get_loc (), inform (ctxt.get_location (),
"write to beyond the end of %qE", "write to beyond the end of %qE",
m_diag_arg); m_diag_arg);
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
} }
return warned; return warned;
@ -388,24 +382,20 @@ public:
return "concrete_buffer_over_read"; return "concrete_buffer_over_read";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
m.add_cwe (126); ctxt.add_cwe (126);
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer over-read");
"buffer over-read");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer over-read");
"stack-based buffer over-read");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer over-read");
"heap-based buffer over-read");
break; break;
} }
@ -416,25 +406,25 @@ public:
unsigned HOST_WIDE_INT num_bad_bytes unsigned HOST_WIDE_INT num_bad_bytes
= m_out_of_bounds_range.m_size_in_bytes.to_uhwi (); = m_out_of_bounds_range.m_size_in_bytes.to_uhwi ();
if (m_diag_arg) if (m_diag_arg)
inform_n (rich_loc->get_loc (), inform_n (ctxt.get_location (),
num_bad_bytes, num_bad_bytes,
"read of %wu byte from after the end of %qE", "read of %wu byte from after the end of %qE",
"read of %wu bytes from after the end of %qE", "read of %wu bytes from after the end of %qE",
num_bad_bytes, num_bad_bytes,
m_diag_arg); m_diag_arg);
else else
inform_n (rich_loc->get_loc (), inform_n (ctxt.get_location (),
num_bad_bytes, num_bad_bytes,
"read of %wu byte from after the end of the region", "read of %wu byte from after the end of the region",
"read of %wu bytes from after the end of the region", "read of %wu bytes from after the end of the region",
num_bad_bytes); num_bad_bytes);
} }
else if (m_diag_arg) else if (m_diag_arg)
inform (rich_loc->get_loc (), inform (ctxt.get_location (),
"read from after the end of %qE", "read from after the end of %qE",
m_diag_arg); m_diag_arg);
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
} }
return warned; return warned;
@ -493,28 +483,24 @@ public:
return "concrete_buffer_underwrite"; return "concrete_buffer_underwrite";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
m.add_cwe (124); ctxt.add_cwe (124);
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer underwrite");
"buffer underwrite");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer underwrite");
"stack-based buffer underwrite");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer underwrite");
"heap-based buffer underwrite");
break; break;
} }
if (warned) if (warned)
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
return warned; return warned;
} }
@ -568,28 +554,24 @@ public:
return "concrete_buffer_under_read"; return "concrete_buffer_under_read";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
m.add_cwe (127); ctxt.add_cwe (127);
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer under-read");
"buffer under-read");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer under-read");
"stack-based buffer under-read");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer under-read");
"heap-based buffer under-read");
break; break;
} }
if (warned) if (warned)
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
return warned; return warned;
} }
@ -679,30 +661,26 @@ public:
return "symbolic_buffer_overflow"; return "symbolic_buffer_overflow";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
m.add_cwe (787); ctxt.add_cwe (787);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer overflow");
"buffer overflow");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
m.add_cwe (121); ctxt.add_cwe (121);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer overflow");
"stack-based buffer overflow");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
m.add_cwe (122); ctxt.add_cwe (122);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer overflow");
"heap-based buffer overflow");
break; break;
} }
if (warned) if (warned)
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
return warned; return warned;
} }
@ -796,31 +774,27 @@ public:
return "symbolic_buffer_over_read"; return "symbolic_buffer_over_read";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m; ctxt.add_cwe (126);
m.add_cwe (126);
bool warned; bool warned;
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
m.add_cwe (787); ctxt.add_cwe (787);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("buffer over-read");
"buffer over-read");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
m.add_cwe (121); ctxt.add_cwe (121);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("stack-based buffer over-read");
"stack-based buffer over-read");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
m.add_cwe (122); ctxt.add_cwe (122);
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("heap-based buffer over-read");
"heap-based buffer over-read");
break; break;
} }
if (warned) if (warned)
maybe_show_notes (rich_loc->get_loc (), logger); maybe_show_notes (ctxt);
return warned; return warned;
} }

View File

@ -445,14 +445,12 @@ public:
return OPT_Wanalyzer_overlapping_buffers; return OPT_Wanalyzer_overlapping_buffers;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
bool warned; bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
warned = warning_at (rich_loc, get_controlling_option (), m_fndecl);
"overlapping buffers passed as arguments to %qD",
m_fndecl);
// TODO: draw a picture? // TODO: draw a picture?

View File

@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/checker-path.h" #include "analyzer/checker-path.h"
#include "analyzer/reachability.h" #include "analyzer/reachability.h"
#include "make-unique.h" #include "make-unique.h"
#include "diagnostic-format-sarif.h"
#if ENABLE_ANALYZER #if ENABLE_ANALYZER
@ -1018,6 +1019,31 @@ saved_diagnostic::emit_any_notes () const
pn->emit (); pn->emit ();
} }
/* For SARIF output, add additional properties to the "result" object
for this diagnostic.
This extra data is intended for use when debugging the analyzer. */
void
saved_diagnostic::maybe_add_sarif_properties (sarif_object &result_obj) const
{
sarif_property_bag &props = result_obj.get_or_create_properties ();
#define PROPERTY_PREFIX "gcc/analyzer/saved_diagnostic/"
if (m_sm)
props.set_string (PROPERTY_PREFIX "sm", m_sm->get_name ());
props.set_integer (PROPERTY_PREFIX "enode", m_enode->m_index);
props.set_integer (PROPERTY_PREFIX "snode", m_snode->m_index);
if (m_sval)
props.set (PROPERTY_PREFIX "sval", m_sval->to_json ());
if (m_state)
props.set (PROPERTY_PREFIX "state", m_state->to_json ());
if (m_best_epath)
props.set (PROPERTY_PREFIX "idx", new json::integer_number (m_idx));
#undef PROPERTY_PREFIX
/* Potentially add pending_diagnostic-specific properties. */
m_d->maybe_add_sarif_properties (result_obj);
}
/* State for building a checker_path from a particular exploded_path. /* State for building a checker_path from a particular exploded_path.
In particular, this precomputes reachability information: the set of In particular, this precomputes reachability information: the set of
source enodes for which a path be found to the diagnostic enode. */ source enodes for which a path be found to the diagnostic enode. */
@ -1498,6 +1524,29 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
best_candidates.emit_best (this, eg); best_candidates.emit_best (this, eg);
} }
/* Custom subclass of diagnostic_metadata which, for SARIF output,
populates the property bag of the diagnostic's "result" object
with information from the saved_diagnostic and the
pending_diagnostic. */
class pending_diagnostic_metadata : public diagnostic_metadata
{
public:
pending_diagnostic_metadata (const saved_diagnostic &sd)
: m_sd (sd)
{
}
void
maybe_add_sarif_properties (sarif_object &result_obj) const override
{
m_sd.maybe_add_sarif_properties (result_obj);
}
private:
const saved_diagnostic &m_sd;
};
/* Given a saved_diagnostic SD with m_best_epath through EG, /* Given a saved_diagnostic SD with m_best_epath through EG,
create an checker_path of suitable events and use it to call create an checker_path of suitable events and use it to call
SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic. */ SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic. */
@ -1563,7 +1612,9 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
auto_diagnostic_group d; auto_diagnostic_group d;
auto_cfun sentinel (sd.m_snode->m_fun); auto_cfun sentinel (sd.m_snode->m_fun);
if (sd.m_d->emit (&rich_loc, get_logger ())) pending_diagnostic_metadata m (sd);
diagnostic_emission_context diag_ctxt (sd, rich_loc, m, get_logger ());
if (sd.m_d->emit (diag_ctxt))
{ {
sd.emit_any_notes (); sd.emit_any_notes ();

View File

@ -67,6 +67,8 @@ public:
void emit_any_notes () const; void emit_any_notes () const;
void maybe_add_sarif_properties (sarif_object &result_obj) const;
//private: //private:
const state_machine *m_sm; const state_machine *m_sm;
const exploded_node *m_enode; const exploded_node *m_enode;

View File

@ -1811,13 +1811,11 @@ public:
return OPT_Wanalyzer_stale_setjmp_buffer; return OPT_Wanalyzer_stale_setjmp_buffer;
} }
bool emit (rich_location *richloc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at return ctxt.warn ("%qs called after enclosing function of %qs has returned",
(richloc, get_controlling_option (), get_user_facing_name (m_longjmp_call),
"%qs called after enclosing function of %qs has returned", get_user_facing_name (m_setjmp_call));
get_user_facing_name (m_longjmp_call),
get_user_facing_name (m_setjmp_call));
} }
const char *get_kind () const final override const char *get_kind () const final override
@ -3982,10 +3980,9 @@ public:
return OPT_Wanalyzer_jump_through_null; return OPT_Wanalyzer_jump_through_null;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("jump through null pointer");
"jump through null pointer");
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override

View File

@ -32,7 +32,6 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-core.h" #include "diagnostic-core.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h" #include "function.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "sbitmap.h" #include "sbitmap.h"
@ -178,13 +177,11 @@ public:
return OPT_Wanalyzer_infinite_loop; return OPT_Wanalyzer_infinite_loop;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* "CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')". */ /* "CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')". */
diagnostic_metadata m; ctxt.add_cwe (835);
m.add_cwe (835); return ctxt.warn ("infinite loop");
return warning_meta (rich_loc, m, get_controlling_option (),
"infinite loop");
} }
bool maybe_add_custom_events_for_superedge (const exploded_edge &, bool maybe_add_custom_events_for_superedge (const exploded_edge &,

View File

@ -31,7 +31,6 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-core.h" #include "diagnostic-core.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h" #include "function.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "sbitmap.h" #include "sbitmap.h"
@ -95,13 +94,11 @@ public:
return OPT_Wanalyzer_infinite_recursion; return OPT_Wanalyzer_infinite_recursion;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* "CWE-674: Uncontrolled Recursion". */ /* "CWE-674: Uncontrolled Recursion". */
diagnostic_metadata m; ctxt.add_cwe (674);
m.add_cwe (674); return ctxt.warn ("infinite recursion");
return warning_meta (rich_loc, m, get_controlling_option (),
"infinite recursion");
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override

View File

@ -255,9 +255,9 @@ public:
return 0; return 0;
} }
bool emit (rich_location *richloc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
inform (richloc, "path"); ctxt.inform ("path");
return true; return true;
} }

View File

@ -719,32 +719,29 @@ public:
return OPT_Wanalyzer_putenv_of_auto_var; return OPT_Wanalyzer_putenv_of_auto_var;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m;
/* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a /* SEI CERT C Coding Standard: "POS34-C. Do not call putenv() with a
pointer to an automatic variable as the argument". */ pointer to an automatic variable as the argument". */
diagnostic_metadata::precanned_rule diagnostic_metadata::precanned_rule
rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ"); rule ("POS34-C", "https://wiki.sei.cmu.edu/confluence/x/6NYxBQ");
m.add_rule (rule); ctxt.add_rule (rule);
bool warned; bool warned;
if (m_var_decl) if (m_var_decl)
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("%qE on a pointer to automatic variable %qE",
"%qE on a pointer to automatic variable %qE", m_fndecl, m_var_decl);
m_fndecl, m_var_decl);
else else
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("%qE on a pointer to an on-stack buffer",
"%qE on a pointer to an on-stack buffer", m_fndecl);
m_fndecl);
if (warned) if (warned)
{ {
if (m_var_decl) if (m_var_decl)
inform (DECL_SOURCE_LOCATION (m_var_decl), inform (DECL_SOURCE_LOCATION (m_var_decl),
"%qE declared on stack here", m_var_decl); "%qE declared on stack here", m_var_decl);
inform (rich_loc->get_loc (), "perhaps use %qs rather than %qE", inform (ctxt.get_location (), "perhaps use %qs rather than %qE",
"setenv", m_fndecl); "setenv", m_fndecl);
} }
@ -1733,18 +1730,15 @@ public:
return OPT_Wanalyzer_undefined_behavior_strtok; return OPT_Wanalyzer_undefined_behavior_strtok;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-476: NULL Pointer Dereference. */ /* CWE-476: NULL Pointer Dereference. */
diagnostic_metadata m; ctxt.add_cwe (476);
m.add_cwe (476); if (ctxt.warn ("calling %qD for first time with NULL as argument 1"
if (warning_meta " has undefined behavior",
(rich_loc, m, get_controlling_option (), get_callee_fndecl ()))
"calling %qD for first time with NULL as argument 1"
" has undefined behavior",
get_callee_fndecl ()))
{ {
inform (rich_loc->get_loc (), inform (ctxt.get_location (),
"some implementations of %qD may crash on such input", "some implementations of %qD may crash on such input",
get_callee_fndecl ()); get_callee_fndecl ());
return true; return true;

View File

@ -109,6 +109,51 @@ evdesc::event_desc::formatted_print (const char *fmt, ...) const
return result; return result;
} }
/* class diagnostic_emission_context. */
/* Get the pending_diagnostic being emitted. */
const pending_diagnostic &
diagnostic_emission_context::get_pending_diagnostic () const
{
return *m_sd.m_d.get ();
}
/* Emit a warning, using the rich_location, metadata, and the
pending_diagnostic's option. */
bool
diagnostic_emission_context::warn (const char *gmsgid, ...)
{
const pending_diagnostic &pd = get_pending_diagnostic ();
auto_diagnostic_group d;
va_list ap;
va_start (ap, gmsgid);
const bool result = emit_diagnostic_valist (DK_WARNING,
&m_rich_loc, &m_metadata,
pd.get_controlling_option (),
gmsgid, &ap);
va_end (ap);
return result;
}
/* Emit a note, using the rich_location and metadata (and the
pending_diagnostic's option). */
void
diagnostic_emission_context::inform (const char *gmsgid, ...)
{
const pending_diagnostic &pd = get_pending_diagnostic ();
auto_diagnostic_group d;
va_list ap;
va_start (ap, gmsgid);
emit_diagnostic_valist (DK_NOTE,
&m_rich_loc, &m_metadata,
pd.get_controlling_option (),
gmsgid, &ap);
va_end (ap);
}
/* Return true if T1 and T2 are "the same" for the purposes of /* Return true if T1 and T2 are "the same" for the purposes of
diagnostic deduplication. */ diagnostic deduplication. */

View File

@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H
#define GCC_ANALYZER_PENDING_DIAGNOSTIC_H #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
#include "diagnostic-metadata.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "analyzer/sm.h" #include "analyzer/sm.h"
@ -144,6 +145,47 @@ struct final_event : public event_desc
} /* end of namespace evdesc */ } /* end of namespace evdesc */
/* A bundle of information for use by implementations of the
pending_diagnostic::emit vfunc.
The rich_location will have already been populated with a
diagnostic_path. */
class diagnostic_emission_context
{
public:
diagnostic_emission_context (const saved_diagnostic &sd,
rich_location &rich_loc,
diagnostic_metadata &metadata,
logger *logger)
: m_sd (sd),
m_rich_loc (rich_loc),
m_metadata (metadata),
m_logger (logger)
{
}
const pending_diagnostic &get_pending_diagnostic () const;
bool warn (const char *, ...) ATTRIBUTE_GCC_DIAG (2,3);
void inform (const char *, ...) ATTRIBUTE_GCC_DIAG (2,3);
location_t get_location () const { return m_rich_loc.get_loc (); }
logger *get_logger () const { return m_logger; }
void add_cwe (int cwe) { m_metadata.add_cwe (cwe); }
void add_rule (const diagnostic_metadata::rule &r)
{
m_metadata.add_rule (r);
}
private:
const saved_diagnostic &m_sd;
rich_location &m_rich_loc;
diagnostic_metadata &m_metadata;
logger *m_logger;
};
/* An abstract base class for capturing information about a diagnostic in /* An abstract base class for capturing information about a diagnostic in
a form that is ready to emit at a later point (or be rejected). a form that is ready to emit at a later point (or be rejected).
Each kind of diagnostic will have a concrete subclass of Each kind of diagnostic will have a concrete subclass of
@ -177,10 +219,9 @@ class pending_diagnostic
path being explored. By default, don't terminate the path. */ path being explored. By default, don't terminate the path. */
virtual bool terminate_path_p () const { return false; } virtual bool terminate_path_p () const { return false; }
/* Vfunc for emitting the diagnostic. The rich_location will have been /* Vfunc for emitting the diagnostic.
populated with a diagnostic_path.
Return true if a diagnostic is actually emitted. */ Return true if a diagnostic is actually emitted. */
virtual bool emit (rich_location *, logger *) = 0; virtual bool emit (diagnostic_emission_context &) = 0;
/* Hand-coded RTTI: get an ID for the subclass. */ /* Hand-coded RTTI: get an ID for the subclass. */
virtual const char *get_kind () const = 0; virtual const char *get_kind () const = 0;
@ -361,6 +402,15 @@ class pending_diagnostic
/* Default implementation: accept this path. */ /* Default implementation: accept this path. */
return true; return true;
} }
/* Vfunc for use in SARIF output to give pending_diagnostic subclasses
the opportunity to add diagnostic-specific properties to the SARIF
"result" object for the diagnostic.
This is intended for use when debugging a diagnostic. */
virtual void maybe_add_sarif_properties (sarif_object &/*result_obj*/) const
{
/* Default no-op implementation. */
}
}; };
/* A template to make it easier to make subclasses of pending_diagnostic. /* A template to make it easier to make subclasses of pending_diagnostic.

View File

@ -40,7 +40,6 @@ along with GCC; see the file COPYING3. If not see
#include "fold-const.h" #include "fold-const.h"
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "diagnostic-color.h" #include "diagnostic-color.h"
#include "diagnostic-metadata.h"
#include "bitmap.h" #include "bitmap.h"
#include "selftest.h" #include "selftest.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
@ -79,6 +78,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/checker-path.h" #include "analyzer/checker-path.h"
#include "analyzer/feasible-graph.h" #include "analyzer/feasible-graph.h"
#include "analyzer/record-layout.h" #include "analyzer/record-layout.h"
#include "diagnostic-format-sarif.h"
#if ENABLE_ANALYZER #if ENABLE_ANALYZER
@ -512,7 +512,7 @@ public:
bool terminate_path_p () const final override { return true; } bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
switch (m_pkind) switch (m_pkind)
{ {
@ -520,37 +520,30 @@ public:
gcc_unreachable (); gcc_unreachable ();
case POISON_KIND_UNINIT: case POISON_KIND_UNINIT:
{ {
diagnostic_metadata m; ctxt.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable". */
m.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable". */ return ctxt.warn ("use of uninitialized value %qE",
return warning_meta (rich_loc, m, get_controlling_option (), m_expr);
"use of uninitialized value %qE",
m_expr);
} }
break; break;
case POISON_KIND_FREED: case POISON_KIND_FREED:
{ {
diagnostic_metadata m; ctxt.add_cwe (416); /* "CWE-416: Use After Free". */
m.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<free%> of %qE",
return warning_meta (rich_loc, m, get_controlling_option (), m_expr);
"use after %<free%> of %qE",
m_expr);
} }
break; break;
case POISON_KIND_DELETED: case POISON_KIND_DELETED:
{ {
diagnostic_metadata m; ctxt.add_cwe (416); /* "CWE-416: Use After Free". */
m.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<delete%> of %qE",
return warning_meta (rich_loc, m, get_controlling_option (), m_expr);
"use after %<delete%> of %qE",
m_expr);
} }
break; break;
case POISON_KIND_POPPED_STACK: case POISON_KIND_POPPED_STACK:
{ {
/* TODO: which CWE? */ /* TODO: which CWE? */
return warning_at return ctxt.warn
(rich_loc, get_controlling_option (), ("dereferencing pointer %qE to within stale stack frame",
"dereferencing pointer %qE to within stale stack frame",
m_expr); m_expr);
} }
break; break;
@ -655,10 +648,9 @@ public:
return OPT_Wanalyzer_shift_count_negative; return OPT_Wanalyzer_shift_count_negative;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("shift by negative count (%qE)", m_count_cst);
"shift by negative count (%qE)", m_count_cst);
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -702,11 +694,10 @@ public:
return OPT_Wanalyzer_shift_count_overflow; return OPT_Wanalyzer_shift_count_overflow;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("shift by count (%qE) >= precision of type (%qi)",
"shift by count (%qE) >= precision of type (%qi)", m_count_cst, m_operand_precision);
m_count_cst, m_operand_precision);
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -2840,23 +2831,20 @@ public:
return OPT_Wanalyzer_write_to_const; return OPT_Wanalyzer_write_to_const;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
bool warned; bool warned;
switch (m_reg->get_kind ()) switch (m_reg->get_kind ())
{ {
default: default:
warned = warning_at (rich_loc, get_controlling_option (), warned = ctxt.warn ("write to %<const%> object %qE", m_decl);
"write to %<const%> object %qE", m_decl);
break; break;
case RK_FUNCTION: case RK_FUNCTION:
warned = warning_at (rich_loc, get_controlling_option (), warned = ctxt.warn ("write to function %qE", m_decl);
"write to function %qE", m_decl);
break; break;
case RK_LABEL: case RK_LABEL:
warned = warning_at (rich_loc, get_controlling_option (), warned = ctxt.warn ("write to label %qE", m_decl);
"write to label %qE", m_decl);
break; break;
} }
if (warned) if (warned)
@ -2908,10 +2896,9 @@ public:
return OPT_Wanalyzer_write_to_string_literal; return OPT_Wanalyzer_write_to_string_literal;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("write to string literal");
"write to string literal");
/* Ideally we would show the location of the STRING_CST as well, /* Ideally we would show the location of the STRING_CST as well,
but it is not available at this point. */ but it is not available at this point. */
} }
@ -3112,14 +3099,12 @@ public:
return OPT_Wanalyzer_allocation_size; return OPT_Wanalyzer_allocation_size;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m; ctxt.add_cwe (131);
m.add_cwe (131);
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("allocated buffer size is not a multiple"
"allocated buffer size is not a multiple" " of the pointee's size");
" of the pointee's size");
} }
label_text describe_final_event (const evdesc::final_event &ev) final label_text describe_final_event (const evdesc::final_event &ev) final
@ -5970,15 +5955,14 @@ public:
return same_tree_p (m_arg, ((const float_as_size_arg &) other).m_arg); return same_tree_p (m_arg, ((const float_as_size_arg &) other).m_arg);
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m; bool warned = ctxt.warn ("use of floating-point arithmetic here might"
bool warned = warning_meta (rich_loc, m, get_controlling_option (), " yield unexpected results");
"use of floating-point arithmetic here might"
" yield unexpected results");
if (warned) if (warned)
inform (rich_loc->get_loc (), "only use operands of an integer type" inform (ctxt.get_location (),
" inside the size argument"); "only use operands of an integer type"
" inside the size argument");
return warned; return warned;
} }
@ -6214,37 +6198,33 @@ public:
return OPT_Wanalyzer_exposure_through_uninit_copy; return OPT_Wanalyzer_exposure_through_uninit_copy;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-200: Exposure of Sensitive Information to an Unauthorized Actor. */ /* CWE-200: Exposure of Sensitive Information to an Unauthorized Actor. */
m.add_cwe (200); ctxt.add_cwe (200);
enum memory_space mem_space = get_src_memory_space (); enum memory_space mem_space = get_src_memory_space ();
bool warned; bool warned;
switch (mem_space) switch (mem_space)
{ {
default: default:
warned = warning_meta warned = ctxt.warn ("potential exposure of sensitive information"
(rich_loc, m, get_controlling_option (), " by copying uninitialized data"
"potential exposure of sensitive information" " across trust boundary");
" by copying uninitialized data across trust boundary");
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
warned = warning_meta warned = ctxt.warn ("potential exposure of sensitive information"
(rich_loc, m, get_controlling_option (), " by copying uninitialized data from stack"
"potential exposure of sensitive information" " across trust boundary");
" by copying uninitialized data from stack across trust boundary");
break; break;
case MEMSPACE_HEAP: case MEMSPACE_HEAP:
warned = warning_meta warned = ctxt.warn ("potential exposure of sensitive information"
(rich_loc, m, get_controlling_option (), " by copying uninitialized data from heap"
"potential exposure of sensitive information" " across trust boundary");
" by copying uninitialized data from heap across trust boundary");
break; break;
} }
if (warned) if (warned)
{ {
location_t loc = rich_loc->get_loc (); const location_t loc = ctxt.get_location ();
inform_number_of_uninit_bits (loc); inform_number_of_uninit_bits (loc);
complain_about_uninit_ranges (loc); complain_about_uninit_ranges (loc);
@ -6276,6 +6256,17 @@ public:
interest->add_region_creation (m_src_region); interest->add_region_creation (m_src_region);
} }
void
maybe_add_sarif_properties (sarif_object &result_obj) const final override
{
sarif_property_bag &props = result_obj.get_or_create_properties ();
#define PROPERTY_PREFIX "gcc/-Wanalyzer-exposure-through-uninit-copy/"
props.set (PROPERTY_PREFIX "src_region", m_src_region->to_json ());
props.set (PROPERTY_PREFIX "dest_region", m_dest_region->to_json ());
props.set (PROPERTY_PREFIX "copied_sval", m_copied_sval->to_json ());
#undef PROPERTY_PREFIX
}
private: private:
enum memory_space get_src_memory_space () const enum memory_space get_src_memory_space () const
{ {

View File

@ -40,7 +40,6 @@ along with GCC; see the file COPYING3. If not see
#include "fold-const.h" #include "fold-const.h"
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "diagnostic-color.h" #include "diagnostic-color.h"
#include "diagnostic-metadata.h"
#include "bitmap.h" #include "bitmap.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"

View File

@ -29,7 +29,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "options.h" #include "options.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -465,19 +464,16 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
/*CWE-775: Missing Release of File Descriptor or Handle after Effective /*CWE-775: Missing Release of File Descriptor or Handle after Effective
Lifetime Lifetime
*/ */
diagnostic_metadata m; ctxt.add_cwe (775);
m.add_cwe (775);
if (m_arg) if (m_arg)
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of file descriptor %qE", m_arg);
"leak of file descriptor %qE", m_arg);
else else
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of file descriptor");
"leak of file descriptor");
} }
label_text label_text
@ -550,20 +546,18 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
bool warned; bool warned;
switch (m_fd_dir) switch (m_fd_dir)
{ {
case DIRS_READ: case DIRS_READ:
warned = warning_at (rich_loc, get_controlling_option (), warned = ctxt.warn ("%qE on read-only file descriptor %qE",
"%qE on read-only file descriptor %qE", m_callee_fndecl, m_arg);
m_callee_fndecl, m_arg);
break; break;
case DIRS_WRITE: case DIRS_WRITE:
warned = warning_at (rich_loc, get_controlling_option (), warned = ctxt.warn ("%qE on write-only file descriptor %qE",
"%qE on write-only file descriptor %qE", m_callee_fndecl, m_arg);
m_callee_fndecl, m_arg);
break; break;
default: default:
gcc_unreachable (); gcc_unreachable ();
@ -612,13 +606,11 @@ public:
return OPT_Wanalyzer_fd_double_close; return OPT_Wanalyzer_fd_double_close;
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
// CWE-1341: Multiple Releases of Same Resource or Handle // CWE-1341: Multiple Releases of Same Resource or Handle
m.add_cwe (1341); ctxt.add_cwe (1341);
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("double %<close%> of file descriptor %qE", m_arg);
"double %<close%> of file descriptor %qE", m_arg);
} }
label_text label_text
@ -677,12 +669,10 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
bool warned; bool warned = ctxt.warn ("%qE on closed file descriptor %qE",
warned = warning_at (rich_loc, get_controlling_option (), m_callee_fndecl, m_arg);
"%qE on closed file descriptor %qE", m_callee_fndecl,
m_arg);
if (warned) if (warned)
inform_filedescriptor_attribute (DIRS_READ_WRITE); inform_filedescriptor_attribute (DIRS_READ_WRITE);
return warned; return warned;
@ -748,12 +738,10 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
bool warned; bool warned = ctxt.warn ("%qE on possibly invalid file descriptor %qE",
warned = warning_at (rich_loc, get_controlling_option (), m_callee_fndecl, m_arg);
"%qE on possibly invalid file descriptor %qE",
m_callee_fndecl, m_arg);
if (warned) if (warned)
inform_filedescriptor_attribute (DIRS_READ_WRITE); inform_filedescriptor_attribute (DIRS_READ_WRITE);
return warned; return warned;
@ -859,14 +847,12 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */ /* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */
diagnostic_metadata m; ctxt.add_cwe (666);
m.add_cwe (666); return ctxt.warn ("%qE on file descriptor %qE in wrong phase",
return warning_at (rich_loc, get_controlling_option (), m_callee_fndecl, m_arg);
"%qE on file descriptor %qE in wrong phase",
m_callee_fndecl, m_arg);
} }
label_text label_text
@ -1019,25 +1005,22 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
switch (m_expected_type) switch (m_expected_type)
{ {
default: default:
gcc_unreachable (); gcc_unreachable ();
case EXPECTED_TYPE_SOCKET: case EXPECTED_TYPE_SOCKET:
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("%qE on non-socket file descriptor %qE",
"%qE on non-socket file descriptor %qE", m_callee_fndecl, m_arg);
m_callee_fndecl, m_arg);
case EXPECTED_TYPE_STREAM_SOCKET: case EXPECTED_TYPE_STREAM_SOCKET:
if (m_sm.is_datagram_socket_fd_p (m_actual_state)) if (m_sm.is_datagram_socket_fd_p (m_actual_state))
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("%qE on datagram socket file descriptor %qE",
"%qE on datagram socket file descriptor %qE", m_callee_fndecl, m_arg);
m_callee_fndecl, m_arg);
else else
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("%qE on non-stream-socket file descriptor %qE",
"%qE on non-stream-socket file descriptor %qE", m_callee_fndecl, m_arg);
m_callee_fndecl, m_arg);
} }
} }

View File

@ -29,7 +29,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "options.h" #include "options.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -176,14 +175,12 @@ public:
return OPT_Wanalyzer_double_fclose; return OPT_Wanalyzer_double_fclose;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-1341: Multiple Releases of Same Resource or Handle. */ /* CWE-1341: Multiple Releases of Same Resource or Handle. */
m.add_cwe (1341); ctxt.add_cwe (1341);
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("double %<fclose%> of FILE %qE",
"double %<fclose%> of FILE %qE", m_arg);
m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)
@ -224,19 +221,15 @@ public:
return OPT_Wanalyzer_file_leak; return OPT_Wanalyzer_file_leak;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-775: "Missing Release of File Descriptor or Handle after /* CWE-775: "Missing Release of File Descriptor or Handle after
Effective Lifetime". */ Effective Lifetime". */
m.add_cwe (775); ctxt.add_cwe (775);
if (m_arg) if (m_arg)
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of FILE %qE", m_arg);
"leak of FILE %qE",
m_arg);
else else
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of FILE");
"leak of FILE");
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)

View File

@ -30,7 +30,6 @@ along with GCC; see the file COPYING3. If not see
#include "options.h" #include "options.h"
#include "bitmap.h" #include "bitmap.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -840,23 +839,20 @@ public:
return OPT_Wanalyzer_mismatching_deallocation; return OPT_Wanalyzer_mismatching_deallocation;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m; ctxt.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
if (const deallocator *expected_dealloc if (const deallocator *expected_dealloc
= m_expected_deallocators->maybe_get_single ()) = m_expected_deallocators->maybe_get_single ())
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("%qE should have been deallocated with %qs"
"%qE should have been deallocated with %qs" " but was deallocated with %qs",
" but was deallocated with %qs", m_arg, expected_dealloc->m_name,
m_arg, expected_dealloc->m_name, m_actual_dealloc->m_name);
m_actual_dealloc->m_name);
else else
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("%qs called on %qE returned from a mismatched"
"%qs called on %qE returned from a mismatched" " allocation function",
" allocation function", m_actual_dealloc->m_name, m_arg);
m_actual_dealloc->m_name, m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)
@ -919,13 +915,11 @@ public:
return OPT_Wanalyzer_double_free; return OPT_Wanalyzer_double_free;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m; ctxt.add_cwe (415); /* CWE-415: Double Free. */
m.add_cwe (415); /* CWE-415: Double Free. */ return ctxt.warn ("double-%qs of %qE", m_funcname, m_arg);
return warning_meta (rich_loc, m, get_controlling_option (),
"double-%qs of %qE", m_funcname, m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)
@ -1015,13 +1009,11 @@ public:
return OPT_Wanalyzer_possible_null_dereference; return OPT_Wanalyzer_possible_null_dereference;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
diagnostic_metadata m; ctxt.add_cwe (690);
m.add_cwe (690); return ctxt.warn ("dereference of possibly-NULL %qE", m_arg);
return warning_meta (rich_loc, m, get_controlling_option (),
"dereference of possibly-NULL %qE", m_arg);
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -1104,16 +1096,14 @@ public:
return OPT_Wanalyzer_possible_null_argument; return OPT_Wanalyzer_possible_null_argument;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m; ctxt.add_cwe (690);
m.add_cwe (690);
bool warned bool warned
= warning_meta (rich_loc, m, get_controlling_option (), = ctxt.warn ("use of possibly-NULL %qE where non-null expected",
"use of possibly-NULL %qE where non-null expected", m_arg);
m_arg);
if (warned) if (warned)
inform_nonnull_attribute (m_fndecl, m_arg_idx); inform_nonnull_attribute (m_fndecl, m_arg_idx);
return warned; return warned;
@ -1157,13 +1147,11 @@ public:
bool terminate_path_p () const final override { return true; } bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-476: NULL Pointer Dereference. */ /* CWE-476: NULL Pointer Dereference. */
diagnostic_metadata m; ctxt.add_cwe (476);
m.add_cwe (476); return ctxt.warn ("dereference of NULL %qE", m_arg);
return warning_meta (rich_loc, m, get_controlling_option (),
"dereference of NULL %qE", m_arg);
} }
label_text describe_return_of_state (const evdesc::return_of_state &info) label_text describe_return_of_state (const evdesc::return_of_state &info)
@ -1227,21 +1215,18 @@ public:
bool terminate_path_p () const final override { return true; } bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-476: NULL Pointer Dereference. */ /* CWE-476: NULL Pointer Dereference. */
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m; ctxt.add_cwe (476);
m.add_cwe (476);
bool warned; bool warned;
if (zerop (m_arg)) if (zerop (m_arg))
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of NULL where non-null expected");
"use of NULL where non-null expected");
else else
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of NULL %qE where non-null expected",
"use of NULL %qE where non-null expected", m_arg);
m_arg);
if (warned) if (warned)
inform_nonnull_attribute (m_fndecl, m_arg_idx); inform_nonnull_attribute (m_fndecl, m_arg_idx);
return warned; return warned;
@ -1284,14 +1269,12 @@ public:
return OPT_Wanalyzer_use_after_free; return OPT_Wanalyzer_use_after_free;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* CWE-416: Use After Free. */ /* CWE-416: Use After Free. */
diagnostic_metadata m; ctxt.add_cwe (416);
m.add_cwe (416); return ctxt.warn ("use after %<%s%> of %qE",
return warning_meta (rich_loc, m, get_controlling_option (), m_deallocator->m_name, m_arg);
"use after %<%s%> of %qE",
m_deallocator->m_name, m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)
@ -1378,17 +1361,14 @@ public:
return OPT_Wanalyzer_malloc_leak; return OPT_Wanalyzer_malloc_leak;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* "CWE-401: Missing Release of Memory after Effective Lifetime". */ /* "CWE-401: Missing Release of Memory after Effective Lifetime". */
diagnostic_metadata m; ctxt.add_cwe (401);
m.add_cwe (401);
if (m_arg) if (m_arg)
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of %qE", m_arg);
"leak of %qE", m_arg);
else else
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("leak of %qs", "<unknown>");
"leak of %qs", "<unknown>");
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)
@ -1452,11 +1432,10 @@ public:
return OPT_Wanalyzer_free_of_non_heap; return OPT_Wanalyzer_free_of_non_heap;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m; ctxt.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
switch (get_memory_space ()) switch (get_memory_space ())
{ {
default: default:
@ -1466,16 +1445,14 @@ public:
case MEMSPACE_CODE: case MEMSPACE_CODE:
case MEMSPACE_GLOBALS: case MEMSPACE_GLOBALS:
case MEMSPACE_READONLY_DATA: case MEMSPACE_READONLY_DATA:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("%<%s%> of %qE which points to memory"
"%<%s%> of %qE which points to memory" " not on the heap",
" not on the heap", m_funcname, m_arg);
m_funcname, m_arg);
break; break;
case MEMSPACE_STACK: case MEMSPACE_STACK:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("%<%s%> of %qE which points to memory"
"%<%s%> of %qE which points to memory" " on the stack",
" on the stack", m_funcname, m_arg);
m_funcname, m_arg);
break; break;
} }
} }
@ -1531,7 +1508,7 @@ public:
return OPT_Wanalyzer_deref_before_check; return OPT_Wanalyzer_deref_before_check;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
/* Don't emit the warning if we can't show where the deref /* Don't emit the warning if we can't show where the deref
and the check occur. */ and the check occur. */
@ -1605,10 +1582,9 @@ public:
m_deref_enode->get_supernode ()->m_bb)) m_deref_enode->get_supernode ()->m_bb))
return false; return false;
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("check of %qE for NULL after already"
"check of %qE for NULL after already" " dereferencing it",
" dereferencing it", m_arg);
m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)

View File

@ -31,7 +31,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -92,11 +91,10 @@ public:
return 0; return 0;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("pattern match on %<%E %s %E%>",
"pattern match on %<%E %s %E%>", m_lhs, op_symbol_code (m_op), m_rhs);
m_lhs, op_symbol_code (m_op), m_rhs);
} }
private: private:

View File

@ -30,7 +30,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "options.h" #include "options.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -95,15 +94,12 @@ public:
return OPT_Wanalyzer_exposure_through_output_file; return OPT_Wanalyzer_exposure_through_output_file;
} }
bool emit (rich_location *rich_loc, bool emit (diagnostic_emission_context &ctxt) final override
logger *) final override
{ {
diagnostic_metadata m;
/* CWE-532: Information Exposure Through Log Files */ /* CWE-532: Information Exposure Through Log Files */
m.add_cwe (532); ctxt.add_cwe (532);
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("sensitive value %qE written to output file",
"sensitive value %qE written to output file", m_arg);
m_arg);
} }
label_text describe_state_change (const evdesc::state_change &change) label_text describe_state_change (const evdesc::state_change &change)

View File

@ -32,7 +32,6 @@ along with GCC; see the file COPYING3. If not see
#include "options.h" #include "options.h"
#include "bitmap.h" #include "bitmap.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "diagnostic-event-id.h" #include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
@ -114,15 +113,13 @@ public:
return OPT_Wanalyzer_unsafe_call_within_signal_handler; return OPT_Wanalyzer_unsafe_call_within_signal_handler;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; auto_diagnostic_group d;
diagnostic_metadata m;
/* CWE-479: Signal Handler Use of a Non-reentrant Function. */ /* CWE-479: Signal Handler Use of a Non-reentrant Function. */
m.add_cwe (479); ctxt.add_cwe (479);
if (warning_meta (rich_loc, m, get_controlling_option (), if (ctxt.warn ("call to %qD from within signal handler",
"call to %qD from within signal handler", m_unsafe_fndecl))
m_unsafe_fndecl))
{ {
/* If we know a possible alternative function, add a note /* If we know a possible alternative function, add a note
suggesting the replacement. */ suggesting the replacement. */

View File

@ -31,7 +31,6 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "options.h" #include "options.h"
#include "diagnostic-path.h" #include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h" #include "analyzer/analyzer-logging.h"
#include "gimple-iterator.h" #include "gimple-iterator.h"
@ -211,33 +210,29 @@ public:
return OPT_Wanalyzer_tainted_array_index; return OPT_Wanalyzer_tainted_array_index;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-129: "Improper Validation of Array Index". */ /* CWE-129: "Improper Validation of Array Index". */
m.add_cwe (129); ctxt.add_cwe (129);
if (m_arg) if (m_arg)
switch (m_has_bounds) switch (m_has_bounds)
{ {
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE"
"use of attacker-controlled value %qE" " in array lookup without bounds checking",
" in array lookup without bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE"
"use of attacker-controlled value %qE" " in array lookup without checking for negative",
" in array lookup without checking for negative", m_arg);
m_arg);
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE"
"use of attacker-controlled value %qE" " in array lookup without upper-bounds checking",
" in array lookup without upper-bounds checking", m_arg);
m_arg);
break; break;
} }
else else
@ -246,21 +241,18 @@ public:
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value"
"use of attacker-controlled value" " in array lookup without bounds checking");
" in array lookup without bounds checking");
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value"
"use of attacker-controlled value" " in array lookup without checking for"
" in array lookup without checking for" " negative");
" negative");
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value"
"use of attacker-controlled value" " in array lookup without upper-bounds"
" in array lookup without upper-bounds" " checking");
" checking");
break; break;
} }
} }
@ -327,33 +319,29 @@ public:
return OPT_Wanalyzer_tainted_offset; return OPT_Wanalyzer_tainted_offset;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-823: "Use of Out-of-range Pointer Offset". */ /* CWE-823: "Use of Out-of-range Pointer Offset". */
m.add_cwe (823); ctxt.add_cwe (823);
if (m_arg) if (m_arg)
switch (m_has_bounds) switch (m_has_bounds)
{ {
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as offset"
"use of attacker-controlled value %qE as offset" " without bounds checking",
" without bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as offset"
"use of attacker-controlled value %qE as offset" " without lower-bounds checking",
" without lower-bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as offset"
"use of attacker-controlled value %qE as offset" " without upper-bounds checking",
" without upper-bounds checking", m_arg);
m_arg);
break; break;
} }
else else
@ -362,19 +350,16 @@ public:
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as offset"
"use of attacker-controlled value as offset" " without bounds checking");
" without bounds checking");
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as offset"
"use of attacker-controlled value as offset" " without lower-bounds checking");
" without lower-bounds checking");
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as offset"
"use of attacker-controlled value as offset" " without upper-bounds checking");
" without upper-bounds checking");
break; break;
} }
} }
@ -437,33 +422,29 @@ public:
return OPT_Wanalyzer_tainted_size; return OPT_Wanalyzer_tainted_size;
} }
bool emit (rich_location *rich_loc, logger *) override bool emit (diagnostic_emission_context &ctxt) override
{ {
/* "CWE-129: Improper Validation of Array Index". */ /* "CWE-129: Improper Validation of Array Index". */
diagnostic_metadata m; ctxt.add_cwe (129);
m.add_cwe (129);
if (m_arg) if (m_arg)
switch (m_has_bounds) switch (m_has_bounds)
{ {
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as size"
"use of attacker-controlled value %qE as size" " without bounds checking",
" without bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as size"
"use of attacker-controlled value %qE as size" " without lower-bounds checking",
" without lower-bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as size"
"use of attacker-controlled value %qE as size" " without upper-bounds checking",
" without upper-bounds checking", m_arg);
m_arg);
break; break;
} }
else else
@ -472,19 +453,16 @@ public:
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as size"
"use of attacker-controlled value as size" " without bounds checking");
" without bounds checking");
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as size"
"use of attacker-controlled value as size" " without lower-bounds checking");
" without lower-bounds checking");
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as size"
"use of attacker-controlled value as size" " without upper-bounds checking");
" without upper-bounds checking");
break; break;
} }
} }
@ -547,9 +525,9 @@ public:
return "tainted_access_attrib_size"; return "tainted_access_attrib_size";
} }
bool emit (rich_location *rich_loc, logger *logger) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
bool warned = tainted_size::emit (rich_loc, logger); bool warned = tainted_size::emit (ctxt);
if (warned) if (warned)
{ {
inform (DECL_SOURCE_LOCATION (m_callee_fndecl), inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
@ -583,20 +561,17 @@ public:
return OPT_Wanalyzer_tainted_divisor; return OPT_Wanalyzer_tainted_divisor;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* CWE-369: "Divide By Zero". */ /* CWE-369: "Divide By Zero". */
m.add_cwe (369); ctxt.add_cwe (369);
if (m_arg) if (m_arg)
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value %qE as divisor"
"use of attacker-controlled value %qE as divisor" " without checking for zero",
" without checking for zero", m_arg);
m_arg);
else else
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacker-controlled value as divisor"
"use of attacker-controlled value as divisor" " without checking for zero");
" without checking for zero");
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -645,11 +620,10 @@ public:
return OPT_Wanalyzer_tainted_allocation_size; return OPT_Wanalyzer_tainted_allocation_size;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* "CWE-789: Memory Allocation with Excessive Size Value". */ /* "CWE-789: Memory Allocation with Excessive Size Value". */
m.add_cwe (789); ctxt.add_cwe (789);
bool warned; bool warned;
if (m_arg) if (m_arg)
@ -658,24 +632,21 @@ public:
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value %qE as"
"use of attacker-controlled value %qE as" " allocation size without bounds checking",
" allocation size without bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value %qE as"
"use of attacker-controlled value %qE as" " allocation size without"
" allocation size without" " lower-bounds checking",
" lower-bounds checking", m_arg);
m_arg);
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value %qE as"
"use of attacker-controlled value %qE as" " allocation size without"
" allocation size without" " upper-bounds checking",
" upper-bounds checking", m_arg);
m_arg);
break; break;
} }
else else
@ -684,27 +655,24 @@ public:
default: default:
gcc_unreachable (); gcc_unreachable ();
case BOUNDS_NONE: case BOUNDS_NONE:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value as"
"use of attacker-controlled value as" " allocation size without bounds"
" allocation size without bounds" " checking");
" checking");
break; break;
case BOUNDS_UPPER: case BOUNDS_UPPER:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value as"
"use of attacker-controlled value as" " allocation size without"
" allocation size without" " lower-bounds checking");
" lower-bounds checking");
break; break;
case BOUNDS_LOWER: case BOUNDS_LOWER:
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("use of attacker-controlled value as"
"use of attacker-controlled value as" " allocation size without"
" allocation size without" " upper-bounds checking");
" upper-bounds checking");
break; break;
} }
if (warned) if (warned)
{ {
location_t loc = rich_loc->get_loc (); const location_t loc = ctxt.get_location ();
switch (m_mem_space) switch (m_mem_space)
{ {
default: default:
@ -800,15 +768,13 @@ public:
return OPT_Wanalyzer_tainted_assertion; return OPT_Wanalyzer_tainted_assertion;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
/* "CWE-617: Reachable Assertion". */ /* "CWE-617: Reachable Assertion". */
m.add_cwe (617); ctxt.add_cwe (617);
return warning_meta (rich_loc, m, get_controlling_option (), return ctxt.warn ("use of attacked-controlled value in"
"use of attacked-controlled value in" " condition for assertion");
" condition for assertion");
} }
location_t fixup_location (location_t loc, location_t fixup_location (location_t loc,

View File

@ -38,7 +38,6 @@ along with GCC; see the file COPYING3. If not see
#include "fold-const.h" #include "fold-const.h"
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "diagnostic-color.h" #include "diagnostic-color.h"
#include "diagnostic-metadata.h"
#include "bitmap.h" #include "bitmap.h"
#include "selftest.h" #include "selftest.h"
#include "analyzer/analyzer.h" #include "analyzer/analyzer.h"

View File

@ -41,7 +41,6 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/supergraph.h" #include "analyzer/supergraph.h"
#include "analyzer/diagnostic-manager.h" #include "analyzer/diagnostic-manager.h"
#include "analyzer/exploded-graph.h" #include "analyzer/exploded-graph.h"
#include "diagnostic-metadata.h"
#include "analyzer/call-details.h" #include "analyzer/call-details.h"
#if ENABLE_ANALYZER #if ENABLE_ANALYZER
@ -403,11 +402,9 @@ public:
&& 0 == strcmp (m_usage_fnname, other.m_usage_fnname)); && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; return ctxt.warn ("%qs after %qs", m_usage_fnname, "va_end");
return warning_at (rich_loc, get_controlling_option (),
"%qs after %qs", m_usage_fnname, "va_end");
} }
const char *get_kind () const final override const char *get_kind () const final override
@ -478,11 +475,9 @@ public:
return va_list_sm_diagnostic::subclass_equal_p (other); return va_list_sm_diagnostic::subclass_equal_p (other);
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; return ctxt.warn ("missing call to %qs", "va_end");
return warning_at (rich_loc, get_controlling_option (),
"missing call to %qs", "va_end");
} }
const char *get_kind () const final override { return "va_list_leak"; } const char *get_kind () const final override { return "va_list_leak"; }
@ -892,18 +887,15 @@ public:
return OPT_Wanalyzer_va_arg_type_mismatch; return OPT_Wanalyzer_va_arg_type_mismatch;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d;
diagnostic_metadata m;
/* "CWE-686: Function Call With Incorrect Argument Type". */ /* "CWE-686: Function Call With Incorrect Argument Type". */
m.add_cwe (686); ctxt.add_cwe (686);
bool warned bool warned
= warning_meta (rich_loc, m, get_controlling_option (), = ctxt.warn ("%<va_arg%> expected %qT but received %qT"
"%<va_arg%> expected %qT but received %qT" " for variadic argument %i of %qE",
" for variadic argument %i of %qE", m_expected_type, m_actual_type,
m_expected_type, m_actual_type, get_variadic_index_for_diagnostic (), m_va_list_tree);
get_variadic_index_for_diagnostic (), m_va_list_tree);
return warned; return warned;
} }
@ -942,15 +934,12 @@ public:
return OPT_Wanalyzer_va_list_exhausted; return OPT_Wanalyzer_va_list_exhausted;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d;
diagnostic_metadata m;
/* CWE-685: Function Call With Incorrect Number of Arguments. */ /* CWE-685: Function Call With Incorrect Number of Arguments. */
m.add_cwe (685); ctxt.add_cwe (685);
bool warned = warning_meta (rich_loc, m, get_controlling_option (), bool warned = ctxt.warn ("%qE has no more arguments (%i consumed)",
"%qE has no more arguments (%i consumed)", m_va_list_tree, get_num_consumed ());
m_va_list_tree, get_num_consumed ());
return warned; return warned;
} }

View File

@ -123,6 +123,12 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
const char *, ...) ATTRIBUTE_GCC_DIAG(4,5); const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *, extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
va_list *) ATTRIBUTE_GCC_DIAG (4,0); va_list *) ATTRIBUTE_GCC_DIAG (4,0);
extern bool emit_diagnostic_valist (diagnostic_t,
rich_location *,
const diagnostic_metadata *,
int,
const char *,
va_list *) ATTRIBUTE_GCC_DIAG (5,0);
extern bool seen_error (void); extern bool seen_error (void);
#ifdef BUFSIZ #ifdef BUFSIZ

View File

@ -569,16 +569,20 @@ sarif_builder::make_result_object (diagnostic_context *context,
free (rule_id); free (rule_id);
} }
/* "taxa" property (SARIF v2.1.0 section 3.27.8). */
if (diagnostic->metadata) if (diagnostic->metadata)
if (int cwe_id = diagnostic->metadata->get_cwe ()) {
{ /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
json::array *taxa_arr = new json::array (); if (int cwe_id = diagnostic->metadata->get_cwe ())
json::object *cwe_id_obj {
= make_reporting_descriptor_reference_object_for_cwe_id (cwe_id); json::array *taxa_arr = new json::array ();
taxa_arr->append (cwe_id_obj); json::object *cwe_id_obj
result_obj->set ("taxa", taxa_arr); = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
} taxa_arr->append (cwe_id_obj);
result_obj->set ("taxa", taxa_arr);
}
diagnostic->metadata->maybe_add_sarif_properties (*result_obj);
}
/* "level" property (SARIF v2.1.0 section 3.27.10). */ /* "level" property (SARIF v2.1.0 section 3.27.10). */
if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind)) if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))

View File

@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_DIAGNOSTIC_METADATA_H #ifndef GCC_DIAGNOSTIC_METADATA_H
#define GCC_DIAGNOSTIC_METADATA_H #define GCC_DIAGNOSTIC_METADATA_H
class sarif_object;
/* A bundle of additional metadata that can be associated with a /* A bundle of additional metadata that can be associated with a
diagnostic. diagnostic.
@ -63,6 +65,14 @@ class diagnostic_metadata
}; };
diagnostic_metadata () : m_cwe (0) {} diagnostic_metadata () : m_cwe (0) {}
virtual ~diagnostic_metadata () {}
/* Hook for SARIF output to allow for adding diagnostic-specific
properties to the result object's property bag. */
virtual void
maybe_add_sarif_properties (sarif_object &/*result_obj*/) const
{
}
void add_cwe (int cwe) { m_cwe = cwe; } void add_cwe (int cwe) { m_cwe = cwe; }
int get_cwe () const { return m_cwe; } int get_cwe () const { return m_cwe; }

View File

@ -1834,6 +1834,18 @@ emit_diagnostic_valist (diagnostic_t kind, location_t location, int opt,
return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind); return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind);
} }
/* As above, but with rich_location and metadata. */
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 /* An informative note at LOCATION. Use this for additional details on an error
message. */ message. */
void void

View File

@ -65,7 +65,7 @@ int test_accept_on_accept (int fd_a)
if (fd_b == -1) if (fd_b == -1)
return -1; return -1;
int fd_c = accept (fd_b, NULL, 0); /* { dg-warning "'accept' on file descriptor 'fd_b' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */ int fd_c = accept (fd_b, NULL, 0); /* { dg-warning "'accept' on file descriptor 'fd_b' in wrong phase \\\[CWE-666\\\] \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
/* { dg-message "'accept' expects a listening stream socket file descriptor but 'fd_b' is connected" "final event" { target *-*-* } .-1 } */ /* { dg-message "'accept' expects a listening stream socket file descriptor but 'fd_b' is connected" "final event" { target *-*-* } .-1 } */
return fd_b; return fd_b;

View File

@ -35,7 +35,7 @@ void test_double_bind (int fd, const char *sockname)
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1); strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
bind (fd, (struct sockaddr *)&addr, sizeof (addr)); bind (fd, (struct sockaddr *)&addr, sizeof (addr));
bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'fd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */ bind (fd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'fd' in wrong phase \\\[CWE-666\\\] \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
/* { dg-message "'bind' expects a new socket file descriptor but 'fd' has already been bound" "final event" { target *-*-* } .-1 } */ /* { dg-message "'bind' expects a new socket file descriptor but 'fd' has already been bound" "final event" { target *-*-* } .-1 } */
} }
@ -71,7 +71,7 @@ void test_bind_after_accept (int fd, const char *sockname)
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1); strncpy (addr.sun_path, sockname, sizeof(addr.sun_path) - 1);
bind (afd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'afd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */ bind (afd, (struct sockaddr *)&addr, sizeof (addr)); /* { dg-warning "'bind' on file descriptor 'afd' in wrong phase \\\[CWE-666\\\] \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
/* { dg-message "'bind' expects a new socket file descriptor but 'afd' is already connected" "final event" { target *-*-* } .-1 } */ /* { dg-message "'bind' expects a new socket file descriptor but 'afd' is already connected" "final event" { target *-*-* } .-1 } */
close (afd); close (afd);

View File

@ -18,7 +18,7 @@ void test_read_on_new_socket (void *buf)
int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */ int fd = socket (AF_UNIX, SOCK_STREAM, 0); /* { dg-message "stream socket created here" } */
if (fd == -1) if (fd == -1)
return; return;
read (fd, buf, 1); /* { dg-warning "'read' on file descriptor 'fd' in wrong phase \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */ read (fd, buf, 1); /* { dg-warning "'read' on file descriptor 'fd' in wrong phase \\\[CWE-666\\\] \\\[-Wanalyzer-fd-phase-mismatch\\\]" "warning" } */
/* { dg-message "'read' expects a stream socket to be connected via 'accept' but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */ /* { dg-message "'read' expects a stream socket to be connected via 'accept' but 'fd' has not yet been bound" "final event" { target *-*-* } .-1 } */
close (fd); close (fd);
} }

View File

@ -310,18 +310,16 @@ public:
} }
bool bool
emit (rich_location *rich_loc, logger *) final override emit (diagnostic_emission_context &ctxt) final override
{ {
diagnostic_metadata m;
bool warned; bool warned;
// just assuming constants for now // just assuming constants for now
auto actual_refcnt auto actual_refcnt
= m_actual_refcnt->dyn_cast_constant_svalue ()->get_constant (); = m_actual_refcnt->dyn_cast_constant_svalue ()->get_constant ();
auto ob_refcnt = m_ob_refcnt->dyn_cast_constant_svalue ()->get_constant (); auto ob_refcnt = m_ob_refcnt->dyn_cast_constant_svalue ()->get_constant ();
warned = warning_meta (rich_loc, m, get_controlling_option (), warned = ctxt.warn ("expected %qE to have "
"expected %qE to have " "reference count: %qE but ob_refcnt field is: %qE",
"reference count: %qE but ob_refcnt field is: %qE", m_reg_tree, actual_refcnt, ob_refcnt);
m_reg_tree, actual_refcnt, ob_refcnt);
// location_t loc = rich_loc->get_loc (); // location_t loc = rich_loc->get_loc ();
// foo (loc); // foo (loc);

View File

@ -155,10 +155,9 @@ class double_save_thread : public gil_diagnostic
return m_call == sub_other.m_call; return m_call == sub_other.m_call;
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("nested usage of %qs", "Py_BEGIN_ALLOW_THREADS");
"nested usage of %qs", "Py_BEGIN_ALLOW_THREADS");
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -194,19 +193,16 @@ class fncall_without_gil : public gil_diagnostic
&& m_arg_idx == sub_other.m_arg_idx); && m_arg_idx == sub_other.m_arg_idx);
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d;
if (m_callee_fndecl) if (m_callee_fndecl)
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("use of PyObject as argument %i of %qE"
"use of PyObject as argument %i of %qE" " without the GIL",
" without the GIL", m_arg_idx + 1, m_callee_fndecl);
m_arg_idx + 1, m_callee_fndecl);
else else
return warning_at (rich_loc, get_controlling_option (), return ctxt.warn ("use of PyObject as argument %i of call"
"use of PyObject as argument %i of call" " without the GIL",
" without the GIL", m_arg_idx + 1, m_callee_fndecl);
m_arg_idx + 1, m_callee_fndecl);
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override
@ -245,11 +241,9 @@ class pyobject_usage_without_gil : public gil_diagnostic
((const pyobject_usage_without_gil&)base_other).m_expr); ((const pyobject_usage_without_gil&)base_other).m_expr);
} }
bool emit (rich_location *rich_loc, logger *) final override bool emit (diagnostic_emission_context &ctxt) final override
{ {
auto_diagnostic_group d; return ctxt.warn ("use of PyObject %qE without the GIL", m_expr);
return warning_at (rich_loc, get_controlling_option (),
"use of PyObject %qE without the GIL", m_expr);
} }
label_text describe_final_event (const evdesc::final_event &ev) final override label_text describe_final_event (const evdesc::final_event &ev) final override