Bulletproof -fdiagnostics-format=json against bad locations (PR c++/90462)
PR c++/90462 reports an ICE with -fdiagnostics-format=json when attempting to serialize a malformed location to JSON. The compound location_t in question has meaningful "caret" and "start" locations, but has UNKNOWN_LOCATION for its "finish" location, leading to a NULL pointer dereference when attempting to build a JSON string for the filename. This patch bulletproofs the JSON output so that attempts to write a JSON object for a location with a NULL file will lead to an object with no "file" key, and attempts to write a compound location with UNKNOWN_LOCATION for its start or finish will lead to the corresponding JSON child object being omitted. This patch also adds a json::object::get member function, for self-testing the above. gcc/ChangeLog: PR c++/90462 * diagnostic-format-json.cc: Include "selftest.h". (json_from_expanded_location): Only add "file" key for non-NULL file strings. (json_from_location_range): Don't add "start" and "finish" children if they are UNKNOWN_LOCATION. (selftest::test_unknown_location): New selftest. (selftest::test_bad_endpoints): New selftest. (selftest::diagnostic_format_json_cc_tests): New function. * json.cc (json::object::get): New function. (selftest::test_object_get): New selftest. (selftest::json_cc_tests): Call it. * json.h (json::object::get): New decl. * selftest-run-tests.c (selftest::run_tests): Call selftest::diagnostic_format_json_cc_tests. * selftest.h (selftest::diagnostic_format_json_cc_tests): New decl. gcc/testsuite/ChangeLog: PR c++/90462 * g++.dg/pr90462.C: New test. From-SVN: r271535
This commit is contained in:
parent
cf9219c742
commit
30d3ba5142
@ -1,3 +1,23 @@
|
||||
2019-05-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/90462
|
||||
* diagnostic-format-json.cc: Include "selftest.h".
|
||||
(json_from_expanded_location): Only add "file" key for non-NULL
|
||||
file strings.
|
||||
(json_from_location_range): Don't add "start" and "finish"
|
||||
children if they are UNKNOWN_LOCATION.
|
||||
(selftest::test_unknown_location): New selftest.
|
||||
(selftest::test_bad_endpoints): New selftest.
|
||||
(selftest::diagnostic_format_json_cc_tests): New function.
|
||||
* json.cc (json::object::get): New function.
|
||||
(selftest::test_object_get): New selftest.
|
||||
(selftest::json_cc_tests): Call it.
|
||||
* json.h (json::object::get): New decl.
|
||||
* selftest-run-tests.c (selftest::run_tests): Call
|
||||
selftest::diagnostic_format_json_cc_tests.
|
||||
* selftest.h (selftest::diagnostic_format_json_cc_tests): New
|
||||
decl.
|
||||
|
||||
2019-05-22 Kwok Cheung Yeung <kcy@codesourcery.com>
|
||||
Andrew Stubbs <amd@codesourcery.com>
|
||||
|
||||
|
@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "coretypes.h"
|
||||
#include "diagnostic.h"
|
||||
#include "json.h"
|
||||
#include "selftest.h"
|
||||
|
||||
/* The top-level JSON array of pending diagnostics. */
|
||||
|
||||
@ -45,7 +46,8 @@ json_from_expanded_location (location_t loc)
|
||||
{
|
||||
expanded_location exploc = expand_location (loc);
|
||||
json::object *result = new json::object ();
|
||||
result->set ("file", new json::string (exploc.file));
|
||||
if (exploc.file)
|
||||
result->set ("file", new json::string (exploc.file));
|
||||
result->set ("line", new json::number (exploc.line));
|
||||
result->set ("column", new json::number (exploc.column));
|
||||
return result;
|
||||
@ -66,9 +68,11 @@ json_from_location_range (const location_range *loc_range, unsigned range_idx)
|
||||
|
||||
json::object *result = new json::object ();
|
||||
result->set ("caret", json_from_expanded_location (caret_loc));
|
||||
if (start_loc != caret_loc)
|
||||
if (start_loc != caret_loc
|
||||
&& start_loc != UNKNOWN_LOCATION)
|
||||
result->set ("start", json_from_expanded_location (start_loc));
|
||||
if (finish_loc != caret_loc)
|
||||
if (finish_loc != caret_loc
|
||||
&& finish_loc != UNKNOWN_LOCATION)
|
||||
result->set ("finish", json_from_expanded_location (finish_loc));
|
||||
|
||||
if (loc_range->m_label)
|
||||
@ -262,3 +266,53 @@ diagnostic_output_format_init (diagnostic_context *context,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
|
||||
but verify that we handle this gracefully. */
|
||||
|
||||
static void
|
||||
test_unknown_location ()
|
||||
{
|
||||
delete json_from_expanded_location (UNKNOWN_LOCATION);
|
||||
}
|
||||
|
||||
/* Verify that we gracefully handle attempts to serialize bad
|
||||
compound locations. */
|
||||
|
||||
static void
|
||||
test_bad_endpoints ()
|
||||
{
|
||||
location_t bad_endpoints
|
||||
= make_location (BUILTINS_LOCATION,
|
||||
UNKNOWN_LOCATION, UNKNOWN_LOCATION);
|
||||
|
||||
location_range loc_range;
|
||||
loc_range.m_loc = bad_endpoints;
|
||||
loc_range.m_range_display_kind = SHOW_RANGE_WITH_CARET;
|
||||
loc_range.m_label = NULL;
|
||||
|
||||
json::object *obj = json_from_location_range (&loc_range, 0);
|
||||
/* We should have a "caret" value, but no "start" or "finish" values. */
|
||||
ASSERT_TRUE (obj != NULL);
|
||||
ASSERT_TRUE (obj->get ("caret") != NULL);
|
||||
ASSERT_TRUE (obj->get ("start") == NULL);
|
||||
ASSERT_TRUE (obj->get ("finish") == NULL);
|
||||
delete obj;
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
||||
void
|
||||
diagnostic_format_json_cc_tests ()
|
||||
{
|
||||
test_unknown_location ();
|
||||
test_bad_endpoints ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
#endif /* #if CHECKING_P */
|
||||
|
29
gcc/json.cc
29
gcc/json.cc
@ -99,6 +99,22 @@ object::set (const char *key, value *v)
|
||||
m_map.put (xstrdup (key), v);
|
||||
}
|
||||
|
||||
/* Get the json::value * for KEY.
|
||||
|
||||
The object retains ownership of the value. */
|
||||
|
||||
value *
|
||||
object::get (const char *key) const
|
||||
{
|
||||
gcc_assert (key);
|
||||
|
||||
value **ptr = const_cast <map_t &> (m_map).get (key);
|
||||
if (ptr)
|
||||
return *ptr;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* class json::array, a subclass of json::value, representing
|
||||
an ordered collection of values. */
|
||||
|
||||
@ -240,6 +256,18 @@ assert_print_eq (const json::value &jv, const char *expected_json)
|
||||
ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
|
||||
}
|
||||
|
||||
/* Verify that object::get works as expected. */
|
||||
|
||||
static void
|
||||
test_object_get ()
|
||||
{
|
||||
object obj;
|
||||
value *val = new json::string ("value");
|
||||
obj.set ("foo", val);
|
||||
ASSERT_EQ (obj.get ("foo"), val);
|
||||
ASSERT_EQ (obj.get ("not-present"), NULL);
|
||||
}
|
||||
|
||||
/* Verify that JSON objects are written correctly. We can't test more than
|
||||
one key/value pair, as we don't impose a guaranteed ordering. */
|
||||
|
||||
@ -306,6 +334,7 @@ test_writing_literals ()
|
||||
void
|
||||
json_cc_tests ()
|
||||
{
|
||||
test_object_get ();
|
||||
test_writing_objects ();
|
||||
test_writing_arrays ();
|
||||
test_writing_numbers ();
|
||||
|
@ -90,6 +90,7 @@ class object : public value
|
||||
void print (pretty_printer *pp) const FINAL OVERRIDE;
|
||||
|
||||
void set (const char *key, value *v);
|
||||
value *get (const char *key) const;
|
||||
|
||||
private:
|
||||
typedef hash_map <char *, value *,
|
||||
|
@ -90,6 +90,7 @@ selftest::run_tests ()
|
||||
rely on. */
|
||||
diagnostic_show_locus_c_tests ();
|
||||
diagnostic_c_tests ();
|
||||
diagnostic_format_json_cc_tests ();
|
||||
edit_context_c_tests ();
|
||||
fold_const_c_tests ();
|
||||
spellcheck_c_tests ();
|
||||
|
@ -218,6 +218,7 @@ extern void bitmap_c_tests ();
|
||||
extern void cgraph_c_tests ();
|
||||
extern void convert_c_tests ();
|
||||
extern void diagnostic_c_tests ();
|
||||
extern void diagnostic_format_json_cc_tests ();
|
||||
extern void diagnostic_show_locus_c_tests ();
|
||||
extern void dumpfile_c_tests ();
|
||||
extern void edit_context_c_tests ();
|
||||
|
@ -1,3 +1,8 @@
|
||||
2019-05-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/90462
|
||||
* g++.dg/pr90462.C: New test.
|
||||
|
||||
2019-05-22 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* g++.dg/cpp1y/udlit-char-template-neg.C: Expect the error on a
|
||||
|
49
gcc/testsuite/g++.dg/pr90462.C
Normal file
49
gcc/testsuite/g++.dg/pr90462.C
Normal file
@ -0,0 +1,49 @@
|
||||
/* { dg-options "-Wdeprecated-copy -fdiagnostics-format=json" } */
|
||||
|
||||
template <class> class b;
|
||||
struct B {
|
||||
typedef b<char> *c;
|
||||
};
|
||||
class d {
|
||||
public:
|
||||
B::c operator->();
|
||||
};
|
||||
template <class> struct e;
|
||||
class f {
|
||||
typedef int g;
|
||||
};
|
||||
template <class, class> class h;
|
||||
template <class i> class b {
|
||||
public:
|
||||
i j;
|
||||
i k;
|
||||
int l;
|
||||
void assign() {
|
||||
int m;
|
||||
h<i, int> n(&m);
|
||||
n.o(&j, &k, l);
|
||||
}
|
||||
};
|
||||
template <class i, class> class s : f { s &p(const i *, const i *, g); };
|
||||
template <class i, class t> s<i, t> &s<i, t>::p(const i *, const i *, g) {
|
||||
d q;
|
||||
q->assign();
|
||||
}
|
||||
struct G {
|
||||
G();
|
||||
G(int);
|
||||
G(G &);
|
||||
};
|
||||
template <class i, class> class h {
|
||||
public:
|
||||
h(int *);
|
||||
void o(const i *, const i *, unsigned);
|
||||
i r();
|
||||
};
|
||||
template <class i, class t> void h<i, t>::o(const i *, const i *, unsigned) {
|
||||
G a;
|
||||
a = r();
|
||||
}
|
||||
template s<char, e<char>> &s<char, e<char>>::p(const char *, const char *, g);
|
||||
|
||||
/* { dg-regexp ".*" } */
|
Loading…
Reference in New Issue
Block a user