diagnostic_context's dtor assumed that it owned the m_urlifier pointer
and would delete it.

As of r15-5988-g5a022062d22e0b this isn't always the case -
auto_urlify_attributes is used in various places in the C/C++ frontends
and in the middle-end to temporarily override the urlifier with an
on-stack instance, which would lead to delete-of-on-stack-buffer crashes
with -Wfatal-errors as the global_dc was cleaned up.

Fix by explicitly tracking the stack of urlifiers within
diagnostic_context, tracking for each node whether the pointer is
owned or borrowed.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-8447-g24b6d201403507.

gcc/ChangeLog:
        PR c/119366
        * diagnostic-format-sarif.cc (test_message_with_embedded_link):
        Convert diagnostic_context from one urlifier to a stack of
        urlifiers, where each node in the stack tracks whether the
        urlifier is owned or borrowed.
        * diagnostic.cc (diagnostic_context::initialize): Likewise.
        (diagnostic_context::finish): Likewise.
        (diagnostic_context::set_urlifier): Delete.
        (diagnostic_context::push_owned_urlifier): New.
        (diagnostic_context::push_borrowed_urlifier): New.
        (diagnostic_context::pop_urlifier): New.
        (diagnostic_context::get_urlifier): Reimplement in terms of stack.
        (diagnostic_context::override_urlifier): Delete.
        * diagnostic.h (diagnostic_context::set_urlifier): Delete decl.
        (diagnostic_context::override_urlifier): Delete decl.
        (diagnostic_context::push_owned_urlifier): New decl.
        (diagnostic_context::push_borrowed_urlifier): New decl.
        (diagnostic_context::pop_urlifier): New decl.
        (diagnostic_context::get_urlifier): Make return value const; hide
        implementation.
        (diagnostic_context::m_urlifier): Replace with...
        (diagnostic_context::urlifier_stack_node): ... this and...
        (diagnostic_context::m_urlifier_stack): ...this.
        * gcc-urlifier.cc
        (auto_override_urlifier::auto_override_urlifier): Reimplement.
        (auto_override_urlifier::~auto_override_urlifier): Reimplement.
        * gcc-urlifier.h (class auto_override_urlifier): Reimplement.
        (auto_urlify_attributes::auto_urlify_attributes): Update for
        pass-by-reference.
        * gcc.cc (driver::global_initializations): Update for
        reimplementation of urlifiers in terms of a stack.
        * toplev.cc (general_init): Likewise.

gcc/testsuite/ChangeLog:
        PR c/119366
        * gcc.dg/Wfatal-bad-attr-pr119366.c: New test.
---
 gcc/diagnostic-format-sarif.cc                |  2 +-
 gcc/diagnostic.cc                             | 57 ++++++++++++++-----
 gcc/diagnostic.h                              | 23 +++++---
 gcc/gcc-urlifier.cc                           |  7 +--
 gcc/gcc-urlifier.h                            |  7 +--
 gcc/gcc.cc                                    |  2 +-
 .../gcc.dg/Wfatal-bad-attr-pr119366.c         |  8 +++
 gcc/toplev.cc                                 |  2 +-
 8 files changed, 74 insertions(+), 34 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/Wfatal-bad-attr-pr119366.c

diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 554992bddba1..8dbc91ee8f32 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -4365,7 +4365,7 @@ test_message_with_embedded_link (enum sarif_version 
version)
     };
 
     test_sarif_diagnostic_context dc ("test.c", version);
-    dc.set_urlifier (::make_unique<test_urlifier> ());
+    dc.push_owned_urlifier (::make_unique<test_urlifier> ());
     rich_location richloc (line_table, UNKNOWN_LOCATION);
     dc.report (DK_ERROR, richloc, nullptr, 0,
               "foo %<-foption%> %<unrecognized%> bar");
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index c2f6714c24aa..82d7f946818b 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -254,7 +254,7 @@ diagnostic_context::initialize (int n_opts)
   m_text_callbacks.m_start_span = default_diagnostic_start_span_fn;
   m_text_callbacks.m_end_diagnostic = default_diagnostic_text_finalizer;
   m_option_mgr = nullptr;
-  m_urlifier = nullptr;
+  m_urlifier_stack = new auto_vec<urlifier_stack_node> ();
   m_last_location = UNKNOWN_LOCATION;
   m_client_aux_data = nullptr;
   m_lock = 0;
@@ -424,8 +424,13 @@ diagnostic_context::finish ()
   delete m_option_mgr;
   m_option_mgr = nullptr;
 
-  delete m_urlifier;
-  m_urlifier = nullptr;
+  if (m_urlifier_stack)
+    {
+      while (!m_urlifier_stack->is_empty ())
+       pop_urlifier ();
+      delete m_urlifier_stack;
+      m_urlifier_stack = nullptr;
+    }
 
   freeargv (m_original_argv);
   m_original_argv = nullptr;
@@ -549,13 +554,43 @@ set_option_manager 
(std::unique_ptr<diagnostic_option_manager> mgr,
 }
 
 void
-diagnostic_context::set_urlifier (std::unique_ptr<urlifier> urlifier)
+diagnostic_context::push_owned_urlifier (std::unique_ptr<urlifier> ptr)
 {
-  delete m_urlifier;
-  /* Ideally the field would be a std::unique_ptr here.  */
-  m_urlifier = urlifier.release ();
+  gcc_assert (m_urlifier_stack);
+  const urlifier_stack_node node = { ptr.release (), true };
+  m_urlifier_stack->safe_push (node);
 }
 
+void
+diagnostic_context::push_borrowed_urlifier (const urlifier &loan)
+{
+  gcc_assert (m_urlifier_stack);
+  const urlifier_stack_node node = { const_cast <urlifier *> (&loan), false };
+  m_urlifier_stack->safe_push (node);
+}
+
+void
+diagnostic_context::pop_urlifier ()
+{
+  gcc_assert (m_urlifier_stack);
+  gcc_assert (m_urlifier_stack->length () > 0);
+
+  const urlifier_stack_node node = m_urlifier_stack->pop ();
+  if (node.m_owned)
+    delete node.m_urlifier;
+}
+
+const urlifier *
+diagnostic_context::get_urlifier () const
+{
+  if (!m_urlifier_stack)
+    return nullptr;
+  if (m_urlifier_stack->is_empty ())
+    return nullptr;
+  return m_urlifier_stack->last ().m_urlifier;
+}
+
+
 /* Set PP as the reference printer for this context.
    Refresh all output sinks.  */
 
@@ -605,14 +640,6 @@ diagnostic_context::set_prefixing_rule 
(diagnostic_prefixing_rule_t rule)
       pp_prefixing_rule (sink->get_printer ()) = rule;
 }
 
-/* Set the urlifier without deleting the existing one.  */
-
-void
-diagnostic_context::override_urlifier (urlifier *urlifier)
-{
-  m_urlifier = urlifier;
-}
-
 void
 diagnostic_context::create_edit_context ()
 {
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 202760b2f85d..62bffd2c6851 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -609,8 +609,11 @@ public:
   void set_output_format (std::unique_ptr<diagnostic_output_format> 
output_format);
   void set_text_art_charset (enum diagnostic_text_art_charset charset);
   void set_client_data_hooks (std::unique_ptr<diagnostic_client_data_hooks> 
hooks);
-  void set_urlifier (std::unique_ptr<urlifier>);
-  void override_urlifier (urlifier *);
+
+  void push_owned_urlifier (std::unique_ptr<urlifier>);
+  void push_borrowed_urlifier (const urlifier &);
+  void pop_urlifier ();
+
   void create_edit_context ();
   void set_warning_as_error_requested (bool val)
   {
@@ -667,7 +670,8 @@ public:
   {
     return m_client_data_hooks;
   }
-  urlifier *get_urlifier () const { return m_urlifier; }
+
+  const urlifier *get_urlifier () const;
 
   text_art::theme *get_diagram_theme () const { return m_diagrams.m_theme; }
 
@@ -888,11 +892,16 @@ private:
   diagnostic_option_manager *m_option_mgr;
   unsigned m_lang_mask;
 
-  /* An optional hook for adding URLs to quoted text strings in
+  /* A stack of optional hooks for adding URLs to quoted text strings in
      diagnostics.  Only used for the main diagnostic message.
-     Owned by the context; this would be a std::unique_ptr if
-     diagnostic_context had a proper ctor.  */
-  urlifier *m_urlifier;
+     Typically a single one owner by the context, but can be temporarily
+     overridden by a borrowed urlifier (e.g. on-stack).  */
+  struct urlifier_stack_node
+  {
+    urlifier *m_urlifier;
+    bool m_owned;
+  };
+  auto_vec<urlifier_stack_node> *m_urlifier_stack;
 
 public:
   /* Auxiliary data for client.  */
diff --git a/gcc/gcc-urlifier.cc b/gcc/gcc-urlifier.cc
index 49611b71d9b9..a4094582f416 100644
--- a/gcc/gcc-urlifier.cc
+++ b/gcc/gcc-urlifier.cc
@@ -220,15 +220,14 @@ make_gcc_urlifier (unsigned int lang_mask)
 
 /* class auto_override_urlifier.  */
 
-auto_override_urlifier::auto_override_urlifier (urlifier *new_urlifier)
-: m_old_urlifier (global_dc->get_urlifier ())
+auto_override_urlifier::auto_override_urlifier (const urlifier &new_urlifier)
 {
-  global_dc->override_urlifier (new_urlifier);
+  global_dc->push_borrowed_urlifier (new_urlifier);
 }
 
 auto_override_urlifier::~auto_override_urlifier ()
 {
-  global_dc->override_urlifier (m_old_urlifier);
+  global_dc->pop_urlifier ();
 }
 
 #if CHECKING_P
diff --git a/gcc/gcc-urlifier.h b/gcc/gcc-urlifier.h
index 5ffbbf7a4ceb..eefed4903282 100644
--- a/gcc/gcc-urlifier.h
+++ b/gcc/gcc-urlifier.h
@@ -33,11 +33,8 @@ extern char *make_doc_url (const char *doc_url_suffix);
 class auto_override_urlifier
 {
 public:
-  auto_override_urlifier (urlifier *new_urlifier);
+  auto_override_urlifier (const urlifier &new_urlifier);
   ~auto_override_urlifier ();
-
-protected:
-  urlifier * const m_old_urlifier;
 };
 
 /* Subclass of urlifier that attempts to add URLs to quoted strings
@@ -71,7 +68,7 @@ class auto_urlify_attributes
 {
 public:
   auto_urlify_attributes ()
-  : m_override (&m_urlifier)
+  : m_override (m_urlifier)
   {
   }
 
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 04b3736a5da1..c7b2aa6df166 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -8356,7 +8356,7 @@ driver::global_initializations ()
   diagnostic_initialize (global_dc, 0);
   diagnostic_color_init (global_dc);
   diagnostic_urls_init (global_dc);
-  global_dc->set_urlifier (make_gcc_urlifier (0));
+  global_dc->push_owned_urlifier (make_gcc_urlifier (0));
 
 #ifdef GCC_DRIVER_HOST_INITIALIZATION
   /* Perform host dependent initialization when needed.  */
diff --git a/gcc/testsuite/gcc.dg/Wfatal-bad-attr-pr119366.c 
b/gcc/testsuite/gcc.dg/Wfatal-bad-attr-pr119366.c
new file mode 100644
index 000000000000..2ca4eedadeda
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wfatal-bad-attr-pr119366.c
@@ -0,0 +1,8 @@
+/* Verify that we don't crash if we bail out with a fatal error
+   while an on-stack attribute_urlifier is active (PR c/119366).  */
+/* { dg-options "-Wfatal-errors -Werror=attributes" } */
+void foo() __attribute__((this_does_not_exist)); /* { dg-error 
"'this_does_not_exist' attribute directive ignored" } */
+
+/* { dg-message "some warnings being treated as errors" "treated as errors" 
{target "*-*-*"} 0 } */
+/* { dg-message "terminated due to -Wfatal-errors" "terminated" { target *-*-* 
} 0 } */
+
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index c5d46ec98015..6d8b8852fb84 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1099,7 +1099,7 @@ general_init (const char *argv0, bool init_signals, 
unique_argv original_argv)
                                                        lang_mask,
                                                        &global_options),
      lang_mask);
-  global_dc->set_urlifier (make_gcc_urlifier (lang_mask));
+  global_dc->push_owned_urlifier (make_gcc_urlifier (lang_mask));
 
   if (init_signals)
     {
-- 
2.26.3

Reply via email to