A :type:`diagnostic_file` is an opaque type describing a particular input file.
-.. function:: const diagnostic_file * diagnostic_manager_new_file (diagnostic_manager *diag_mgr, \
- const char *name, \
- const char *sarif_source_language)
+.. function:: diagnostic_file * diagnostic_manager_new_file (diagnostic_manager *diag_mgr, \
+ const char *name, \
+ const char *sarif_source_language)
Create a new :type:`diagnostic_file` for file ``name``. Repeated calls
with strings that match ``name`` will return the same object.
Given these declarations::
static diagnostic_manager *diag_mgr;
- static const diagnostic_file *main_file;
+ static diagnostic_file *main_file;
we can create a :type:`diagnostic_file` describing an input file ``foo.c``
via :func:`diagnostic_manager_new_file`::
bool create (const file_cache::input_context &in_context,
const char *file_path, FILE *fp, unsigned highest_use_count);
void evict ();
+ void set_content (const char *buf, size_t sz);
private:
/* These are information used to store a line boundary. */
};
+static const char *
+find_end_of_line (const char *s, size_t len);
+
/* Current position in real source file. */
location_t input_location = UNKNOWN_LOCATION;
return r->missing_trailing_newline_p ();
}
+void
+file_cache::add_buffered_content (const char *file_path,
+ const char *buffer,
+ size_t sz)
+{
+ gcc_assert (file_path);
+
+ file_cache_slot *r = lookup_file (file_path);
+ if (!r)
+ {
+ unsigned highest_use_count = 0;
+ r = evicted_cache_tab_entry (&highest_use_count);
+ if (!r->create (m_input_context, file_path, nullptr, highest_use_count))
+ return;
+ }
+
+ r->set_content (buffer, sz);
+}
+
void
file_cache_slot::evict ()
{
return true;
}
+void
+file_cache_slot::set_content (const char *buf, size_t sz)
+{
+ m_data = (char *)xmalloc (sz);
+ memcpy (m_data, buf, sz);
+ m_nb_read = m_size = sz;
+ m_alloc_offset = 0;
+
+ if (m_fp)
+ {
+ fclose (m_fp);
+ m_fp = nullptr;
+ }
+
+ /* Compute m_total_lines based on content of buffer. */
+ m_total_lines = 0;
+ const char *line_start = m_data;
+ size_t remaining_size = sz;
+ while (const char *line_end = find_end_of_line (line_start, remaining_size))
+ {
+ ++m_total_lines;
+ remaining_size -= line_end + 1 - line_start;
+ line_start = line_end + 1;
+ }
+}
+
/* file_cache's ctor. */
file_cache::file_cache ()
void
file_cache_slot::dump (FILE *out, int indent) const
{
- if (!m_fp)
+ if (!m_file_path)
{
fprintf (out, "%*s(unused)\n", indent, "");
return;
}
fprintf (out, "%*sfile_path: %s\n", indent, "", m_file_path);
+ fprintf (out, "%*sfp: %p\n", indent, "", (void *)m_fp);
fprintf (out, "%*sneeds_read_p: %i\n", indent, "", (int)needs_read_p ());
fprintf (out, "%*sneeds_grow_p: %i\n", indent, "", (int)needs_grow_p ());
fprintf (out, "%*suse_count: %i\n", indent, "", m_use_count);
terminator was not found. We need to determine line endings in the same
manner that libcpp does: any of \n, \r\n, or \r is a line ending. */
-static char *
-find_end_of_line (char *s, size_t len)
+static const char *
+find_end_of_line (const char *s, size_t len)
{
for (const auto end = s + len; s != end; ++s)
{
/* There is no more data to process. */
return false;
- char *line_start = m_data + m_line_start_idx;
+ const char *line_start = m_data + m_line_start_idx;
- char *next_line_start = NULL;
+ const char *next_line_start = NULL;
size_t len = 0;
- char *line_end = find_end_of_line (line_start, remaining_size);
+ const char *line_end = find_end_of_line (line_start, remaining_size);
if (line_end == NULL)
{
/* We haven't found an end-of-line delimiter in the cache.
len = line_end - line_start;
if (m_line_start_idx < m_nb_read)
- *line = line_start;
+ *line = const_cast<char *> (line_start);
++m_line_num;
ASSERT_TRUE (source_line.get_buffer () == NULL);
}
+/* Verify reading from buffers (e.g. for sarif-replay). */
+
+static void
+test_reading_source_buffer ()
+{
+ const char *text = ("01234567890123456789\n"
+ "This is the test text\n"
+ "This is the 3rd line");
+ const char *filename = "foo.txt";
+ file_cache fc;
+ fc.add_buffered_content (filename, text, strlen (text));
+
+ /* Read back a specific line from the tempfile. */
+ char_span source_line = fc.get_source_line (filename, 3);
+ ASSERT_TRUE (source_line);
+ ASSERT_TRUE (source_line.get_buffer () != NULL);
+ ASSERT_EQ (20, source_line.length ());
+ ASSERT_TRUE (!strncmp ("This is the 3rd line",
+ source_line.get_buffer (), source_line.length ()));
+
+ source_line = fc.get_source_line (filename, 2);
+ ASSERT_TRUE (source_line);
+ ASSERT_TRUE (source_line.get_buffer () != NULL);
+ ASSERT_EQ (21, source_line.length ());
+ ASSERT_TRUE (!strncmp ("This is the test text",
+ source_line.get_buffer (), source_line.length ()));
+
+ source_line = fc.get_source_line (filename, 4);
+ ASSERT_FALSE (source_line);
+ ASSERT_TRUE (source_line.get_buffer () == NULL);
+}
+
/* Tests of lexing. */
/* Verify that token TOK from PARSER has cpp_token_as_text
for_each_line_table_case (test_lexer_char_constants);
test_reading_source_line ();
+ test_reading_source_buffer ();
test_line_offset_overflow ();
char_span get_source_line (const char *file_path, int line);
bool missing_trailing_newline_p (const char *file_path);
+ void add_buffered_content (const char *file_path,
+ const char *buffer,
+ size_t sz);
+
private:
file_cache_slot *evicted_cache_tab_entry (unsigned *highest_use_count);
file_cache_slot *add_file (const char *file_path);
diagnostic_text_sink *m_inner;
};
-/* Wrapper around a const diagnostic_file *. */
+/* Wrapper around a diagnostic_file *. */
class file
{
public:
file () : m_inner (nullptr) {}
- file (const diagnostic_file *file) : m_inner (file) {}
+ file (diagnostic_file *file) : m_inner (file) {}
file (const file &other) : m_inner (other.m_inner) {}
file &operator= (const file &other) { m_inner = other.m_inner; return *this; }
- const diagnostic_file * m_inner;
+ void set_buffered_content (const char *data, size_t sz);
+
+ diagnostic_file * m_inner;
};
/* Wrapper around a const diagnostic_physical_location *. */
// Implementation
+// class file
+
+inline void
+file::set_buffered_content (const char *data, size_t sz)
+{
+ diagnostic_file_set_buffered_content (m_inner, data, sz);
+}
+
// class execution_path
inline diagnostic_event_id
char *m_str;
};
+class content_buffer
+{
+public:
+ content_buffer (const char *data, size_t sz)
+ : m_data (xmalloc (sz)),
+ m_sz (sz)
+ {
+ memcpy (m_data, data, sz);
+ }
+ ~content_buffer ()
+ {
+ free (m_data);
+ }
+
+ void *m_data;
+ size_t m_sz;
+};
+
/* This has to be a "struct" as it is exposed in the C API. */
struct diagnostic_file
{
- diagnostic_file (const char *name, const char *sarif_source_language)
- : m_name (name), m_sarif_source_language (sarif_source_language)
+ diagnostic_file (diagnostic_manager &mgr,
+ const char *name,
+ const char *sarif_source_language)
+ : m_mgr (mgr),
+ m_name (name),
+ m_sarif_source_language (sarif_source_language)
{
}
return m_sarif_source_language.get_str ();
}
+ const content_buffer *
+ get_content () const
+ {
+ return m_content.get ();
+ }
+ void set_buffered_content (const char *buf, size_t sz);
+
private:
+ diagnostic_manager &m_mgr;
owned_nullable_string m_name;
owned_nullable_string m_sarif_source_language;
+ std::unique_ptr<content_buffer> m_content;
};
/* This has to be a "struct" as it is exposed in the C API. */
void emit (diagnostic &diag, const char *msgid, va_list *args)
LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0);
- const diagnostic_file *
+ diagnostic_file *
new_file (const char *name,
const char *sarif_source_language)
{
if (diagnostic_file **slot = m_str_to_file_map.get (name))
return *slot;
- diagnostic_file *file = new diagnostic_file (name, sarif_source_language);
+ diagnostic_file *file
+ = new diagnostic_file (*this, name, sarif_source_language);
m_str_to_file_map.put (file->get_name (), file);
return file;
}
}
}
+void
+diagnostic_file::set_buffered_content (const char *buf, size_t sz)
+{
+ m_content = ::make_unique<content_buffer> (buf, sz);
+
+ // Populate file_cache:
+ file_cache &fc = m_mgr.get_dc ().get_file_cache ();
+ fc.add_buffered_content (m_name.get_str (), buf, sz);
+}
+
/* class impl_diagnostic_client_data_hooks. */
const client_version_info *
/* Public entrypoint. */
-const diagnostic_file *
+diagnostic_file *
diagnostic_manager_new_file (diagnostic_manager *diag_mgr,
const char *name,
const char *sarif_source_language)
return diag_mgr->new_file (name, sarif_source_language);
}
+/* Public entrypoint. */
+
+void
+diagnostic_file_set_buffered_content (diagnostic_file *file,
+ const char *buf,
+ size_t sz)
+{
+ FAIL_IF_NULL (file);
+ FAIL_IF_NULL (buf);
+
+ file->set_buffered_content (buf, sz);
+}
+
void
diagnostic_manager_debug_dump_file (diagnostic_manager *,
const diagnostic_file *file,
FAIL_IF_NULL (out);
if (file)
{
+ fprintf (out, "file(name=\"%s\"",
+ file->get_name ());
if (file->get_sarif_source_language ())
- {
- fprintf (out, "file(name=\"%s\", sarif_source_language=\"%s\")",
- file->get_name (),
- file->get_sarif_source_language ());
- }
- else
- {
- fprintf (out, "file(name=\"%s\")",
- file->get_name ());
- }
+ fprintf (out, ", sarif_source_language=\"%s\"",
+ file->get_sarif_source_language ());
+ if (const content_buffer *buf = file->get_content ())
+ fprintf (out, ", content=(size=%zi)", buf->m_sz);
+ fprintf (out, ")");
}
else
fprintf (out, "(null)");
See SARIF v2.1.0 Appendix J for suggested values for various
programmming languages. */
-extern const diagnostic_file *
+extern diagnostic_file *
diagnostic_manager_new_file (diagnostic_manager *diag_mgr,
const char *name,
const char *sarif_source_language)
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3);
+/* Populate the source-quoting cache for FILE, specifying the
+ given buffer as the content of the file (rather than
+ attempting to read the content from the filesystem). */
+
+extern void
+diagnostic_file_set_buffered_content (diagnostic_file *file,
+ const char *buf,
+ size_t sz)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
/* Write a representation of FILE to OUT, for debugging. */
extern void
diagnostic_manager_add_sarif_sink;
diagnostic_manager_write_patch;
diagnostic_manager_new_file;
+ diagnostic_file_set_buffered_content;
diagnostic_manager_debug_dump_file;
diagnostic_manager_new_location_from_file_and_line;
diagnostic_manager_new_location_from_file_line_column;
enum status
handle_tool_obj (const json::object &tool_obj);
+ // "artifact" object (§3.24). */
+ void
+ handle_artifact_obj (const json::object &artifact_obj);
+
// "result" object (§3.27)
enum status
handle_result_obj (const json::object &result_obj,
replayer_location_map m_json_location_map;
const json::object *m_driver_obj;
- const json::value *m_artifacts_arr;
+ const json::array *m_artifacts_arr;
};
static const char *
if (!m_driver_obj)
return status::err_invalid_sarif;
-#if 0
- m_artifacts_arr = get_optional_property<json::array>
- (run_obj, property_spec_ref ("run", "artifacts","3.14.15"));
-#endif
+ const property_spec_ref prop_artifacts ("run", "artifacts", "3.14.15");
+ m_artifacts_arr
+ = get_optional_property<json::array> (run_obj, prop_artifacts);
+ if (m_artifacts_arr)
+ for (auto element : *m_artifacts_arr)
+ {
+ if (const json::object *artifact_obj
+ = require_object_for_element (*element, prop_artifacts))
+ handle_artifact_obj (*artifact_obj);
+ else
+ return status::err_invalid_sarif;
+ }
/* If present, run.results must be null or be an array. */
const property_spec_ref prop_results ("run", "results", "3.14.23");
return status::ok;
}
+/* Process an artifact object (SARIF v2.1.0 section 3.24).
+ Create a libgdiagnostics::file for each artifact that has a uri,
+ effectively prepopulating a cache with source language and contents. */
+
+void
+sarif_replayer::handle_artifact_obj (const json::object &artifact_obj)
+{
+ const property_spec_ref location ("artifact", "location", "3.24.2");
+ auto artifact_loc_obj
+ = get_optional_property<json::object> (artifact_obj, location);
+ if (!artifact_loc_obj)
+ return;
+
+ // we should now have an artifactLocation object (§3.4)
+
+ // 3.4.3 uri property
+ const property_spec_ref prop_uri ("artifactLocation", "uri", "3.4.3");
+ auto artifact_loc_uri
+ = get_optional_property<json::string> (*artifact_loc_obj, prop_uri);
+ if (!artifact_loc_uri)
+ return;
+
+ const char *sarif_source_language = nullptr;
+ const property_spec_ref prop_source_lang
+ ("artifact", "sourceLanguage", "3.24.10");
+ if (auto source_lang_jstr
+ = get_optional_property<json::string> (artifact_obj,
+ prop_source_lang))
+ sarif_source_language = source_lang_jstr->get_string ();
+
+ /* Create the libgdiagnostics::file. */
+ auto file = m_output_mgr.new_file (artifact_loc_uri->get_string (),
+ sarif_source_language);
+
+ // Set contents, if available
+ const property_spec_ref prop_contents
+ ("artifact", "contents", "3.24.8");
+ if (auto content_obj
+ = get_optional_property<json::object> (artifact_obj,
+ prop_contents))
+ {
+ // We should have an artifactContent object (§3.3)
+ const property_spec_ref prop_text
+ ("artifactContent", "text", "3.3.2");
+ if (auto text_jstr
+ = get_optional_property<json::string> (*content_obj,
+ prop_text))
+ file.set_buffered_content (text_jstr->get_string (),
+ text_jstr->get_length ());
+ }
+}
+
/* Process a tool object (SARIF v2.1.0 section 3.18). */
enum status
/* { dg-begin-multiline-output "" }
/this/does/not/exist/test.bas:2:8: error: 'GOTO' is considered harmful
+ 2 | GOTO label
+ | ^~~~~~~~~~
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
/this/does/not/exist/test.bas:1:1: note: this is the target of the 'GOTO'
+ 1 | label: PRINT "hello world!"
+ | ^~~~~~
{ dg-end-multiline-output "" } */
-// TODO: quote the source
// TODO: trailing [error]
--- /dev/null
+/* This is signal-1.c, but the uri for the artifact does not exist,
+ to see if we can get sarif-replay to use the provided artifact
+ "contents".
+
+ As before, the dg directives were stripped out from the generated .sarif
+ to avoid confusing DejaGnu for this test. */
+
+{"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "GNU C17",
+ "fullName": "GNU C17 (GCC) version 15.0.0 20240709 (experimental) (x86_64-pc-linux-gnu)",
+ "version": "15.0.0 20240709 (experimental)",
+ "informationUri": "https://gcc.gnu.org/gcc-15/",
+ "rules": [{"id": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "helpUri": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-unsafe-call-within-signal-handler"}]}},
+ "taxonomies": [{"name": "CWE",
+ "version": "4.7",
+ "organization": "MITRE",
+ "shortDescription": {"text": "The MITRE Common Weakness Enumeration"},
+ "taxa": [{"id": "479",
+ "helpUri": "https://cwe.mitre.org/data/definitions/479.html"}]}],
+ "invocations": [{"executionSuccessful": true,
+ "toolExecutionNotifications": []}],
+ "originalUriBaseIds": {"PWD": {"uri": "file:///THIS/DOES/NOT/EXIST/"}},
+ "artifacts": [{"location": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "sourceLanguage": "c",
+ "contents": {"text": "/* Example of a bad call within a signal handler.\n 'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is\n not allowed from a signal handler. */\n\n\n#include <stdio.h>\n#include <signal.h>\n\nextern void body_of_program(void);\n\nvoid custom_logger(const char *msg)\n{\n fprintf(stderr, \"LOG: %s\", msg);\n}\n\nstatic void handler(int signum)\n{\n custom_logger(\"got signal\");\n}\n\nint main(int argc, const char *argv)\n{\n custom_logger(\"started\");\n\n signal(SIGINT, handler);\n\n body_of_program();\n\n custom_logger(\"stopped\");\n\n return 0;\n}\n"},
+ "roles": ["analysisTarget",
+ "tracedFile"]}],
+ "results": [{"ruleId": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "taxa": [{"id": "479",
+ "toolComponent": {"name": "cwe"}}],
+ "properties": {"gcc/analyzer/saved_diagnostic/sm": "signal",
+ "gcc/analyzer/saved_diagnostic/enode": 57,
+ "gcc/analyzer/saved_diagnostic/snode": 11,
+ "gcc/analyzer/saved_diagnostic/state": "in_signal_handler",
+ "gcc/analyzer/saved_diagnostic/idx": 0},
+ "level": "warning",
+ "message": {"text": "call to ‘fprintf’ from within signal handler"},
+ "locations": [{"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 13,
+ "startColumn": 3,
+ "endColumn": 34},
+ "contextRegion": {"startLine": 13,
+ "snippet": {"text": " fprintf(stderr, \"LOG: %s\", msg);\n"}}},
+ "logicalLocations": [{"name": "custom_logger",
+ "fullyQualifiedName": "custom_logger",
+ "decoratedName": "custom_logger",
+ "kind": "function"}]}],
+ "codeFlows": [{"threadFlows": [{"id": "main",
+ "locations": [{"properties": {"gcc/analyzer/checker_event/emission_id": "(1)",
+ "gcc/analyzer/checker_event/kind": "EK_FUNCTION_ENTRY"},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 21,
+ "startColumn": 5,
+ "endColumn": 9},
+ "contextRegion": {"startLine": 21,
+ "snippet": {"text": "int main(int argc, const char *argv)\n"}}},
+ "logicalLocations": [{"name": "main",
+ "fullyQualifiedName": "main",
+ "decoratedName": "main",
+ "kind": "function"}],
+ "message": {"text": "entry to ‘main’"}},
+ "kinds": ["enter",
+ "function"],
+ "nestingLevel": 1,
+ "executionOrder": 1},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(2)",
+ "gcc/analyzer/checker_event/kind": "EK_STATE_CHANGE"},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 25,
+ "startColumn": 3,
+ "endColumn": 26},
+ "contextRegion": {"startLine": 25,
+ "snippet": {"text": " signal(SIGINT, handler);\n"}}},
+ "logicalLocations": [{"name": "main",
+ "fullyQualifiedName": "main",
+ "decoratedName": "main",
+ "kind": "function"}],
+ "message": {"text": "registering ‘handler’ as signal handler"}},
+ "nestingLevel": 1,
+ "executionOrder": 2},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(3)",
+ "gcc/analyzer/checker_event/kind": "EK_CUSTOM"},
+ "location": {"message": {"text": "later on, when the signal is delivered to the process"}},
+ "nestingLevel": 0,
+ "executionOrder": 3},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(4)",
+ "gcc/analyzer/checker_event/kind": "EK_FUNCTION_ENTRY"},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 16,
+ "startColumn": 13,
+ "endColumn": 20},
+ "contextRegion": {"startLine": 16,
+ "snippet": {"text": "static void handler(int signum)\n"}}},
+ "logicalLocations": [{"name": "handler",
+ "fullyQualifiedName": "handler",
+ "decoratedName": "handler",
+ "kind": "function"}],
+ "message": {"text": "entry to ‘handler’"}},
+ "kinds": ["enter",
+ "function"],
+ "nestingLevel": 1,
+ "executionOrder": 4},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(5)",
+ "gcc/analyzer/checker_event/kind": "EK_CALL_EDGE",
+ "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CALL",
+ "src_idx": 7,
+ "dst_idx": 10,
+ "desc": "call"}},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 18,
+ "startColumn": 3,
+ "endColumn": 30},
+ "contextRegion": {"startLine": 18,
+ "snippet": {"text": " custom_logger(\"got signal\");\n"}}},
+ "logicalLocations": [{"name": "handler",
+ "fullyQualifiedName": "handler",
+ "decoratedName": "handler",
+ "kind": "function"}],
+ "message": {"text": "calling ‘custom_logger’ from ‘handler’"}},
+ "kinds": ["call",
+ "function"],
+ "nestingLevel": 1,
+ "executionOrder": 5},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(6)",
+ "gcc/analyzer/checker_event/kind": "EK_FUNCTION_ENTRY"},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 11,
+ "startColumn": 6,
+ "endColumn": 19},
+ "contextRegion": {"startLine": 11,
+ "snippet": {"text": "void custom_logger(const char *msg)\n"}}},
+ "logicalLocations": [{"name": "custom_logger",
+ "fullyQualifiedName": "custom_logger",
+ "decoratedName": "custom_logger",
+ "kind": "function"}],
+ "message": {"text": "entry to ‘custom_logger’"}},
+ "kinds": ["enter",
+ "function"],
+ "nestingLevel": 2,
+ "executionOrder": 6},
+ {"properties": {"gcc/analyzer/checker_event/emission_id": "(7)",
+ "gcc/analyzer/checker_event/kind": "EK_WARNING"},
+ "location": {"physicalLocation": {"artifactLocation": {"uri": "signal-1.c",
+ "uriBaseId": "PWD"},
+ "region": {"startLine": 13,
+ "startColumn": 3,
+ "endColumn": 34},
+ "contextRegion": {"startLine": 13,
+ "snippet": {"text": " fprintf(stderr, \"LOG: %s\", msg);\n"}}},
+ "logicalLocations": [{"name": "custom_logger",
+ "fullyQualifiedName": "custom_logger",
+ "decoratedName": "custom_logger",
+ "kind": "function"}],
+ "message": {"text": "call to ‘fprintf’ from within signal handler"}},
+ "kinds": ["danger"],
+ "nestingLevel": 2,
+ "executionOrder": 7}]}]}]}]}]}
+
+// TODO: show the CWE
+/* { dg-begin-multiline-output "" }
+signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wanalyzer-unsafe-call-within-signal-handler]
+ 13 | fprintf(stderr, "LOG: %s", msg);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'main': event 1
+ |
+ | 21 | int main(int argc, const char *argv)
+ | | ^~~~~
+ | | |
+ | | (1) entry to ‘main’
+ |
+ 'main': event 2
+ |
+ | 25 | signal(SIGINT, handler);
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) registering ‘handler’ as signal handler
+ |
+ event 3
+ |
+ |GNU C17:
+ | (3): later on, when the signal is delivered to the process
+ |
+ +--> 'handler': event 4
+ |
+ | 16 | static void handler(int signum)
+ | | ^~~~~~~~
+ | | |
+ | | (4) entry to ‘handler’
+ |
+ 'handler': event 5
+ |
+ | 18 | custom_logger("got signal");
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) calling ‘custom_logger’ from ‘handler’
+ |
+ +--> 'custom_logger': event 6
+ |
+ | 11 | void custom_logger(const char *msg)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (6) entry to ‘custom_logger’
+ |
+ 'custom_logger': event 7
+ |
+ | 13 | fprintf(stderr, "LOG: %s", msg);
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) call to ‘fprintf’ from within signal handler
+ |
+ { dg-end-multiline-output "" } */
"nestingLevel": 2,
"executionOrder": 7}]}]}]}]}]}
-// TODO: replay the source code
// TODO: show the CWE
/* { dg-begin-multiline-output "" }
../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wanalyzer-unsafe-call-within-signal-handler]
+ 13 | fprintf(stderr, "LOG: %s", msg);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'main': event 1
|
+ | 21 | int main(int argc, const char *argv)
+ | | ^~~~~
+ | | |
+ | | (1) entry to ‘main’
|
'main': event 2
|
+ | 25 | signal(SIGINT, handler);
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) registering ‘handler’ as signal handler
|
event 3
|
|
+--> 'handler': event 4
|
+ | 16 | static void handler(int signum)
+ | | ^~~~~~~~
+ | | |
+ | | (4) entry to ‘handler’
|
'handler': event 5
|
+ | 18 | custom_logger("got signal");
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) calling ‘custom_logger’ from ‘handler’
|
+--> 'custom_logger': event 6
|
+ | 11 | void custom_logger(const char *msg)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (6) entry to ‘custom_logger’
|
'custom_logger': event 7
|
+ | 13 | fprintf(stderr, "LOG: %s", msg);
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) call to ‘fprintf’ from within signal handler
|
{ dg-end-multiline-output "" } */