On Tue, 2014-09-23 at 23:27 +0000, Joseph S. Myers wrote: [...] > dump::write, recording::context::add_error_va, > recording::string::from_printf all use fixed-size buffers with vsnprintf > but no apparent reason to assume this can never result in truncation, and > missing API comments (lots of functions are missing such comments ...) to > state either the caller's responsibility to limit the length of the > result, or that the API provides for truncation. Unless there's a > definite reason truncation is needed, dynamic allocation should be used. > A patch was submitted a while back to add xasprintf and xvasprintf to > libiberty - <https://gcc.gnu.org/ml/gcc-patches/2009-11/msg01448.html> and > <https://gcc.gnu.org/ml/gcc-patches/2009-11/msg01449.html> (I don't know > if that's the most recent version), which could be resurrected. [...]
The ideal I'm aiming for here is that a well-behaved library should never abort, so I've rewritten these functions to use vasprintf, and added error-handling checks to cover the case where malloc returns NULL within vasprintf. I believe this fixes the specific issues you pointed out (apart from the numerous missing API comments, which I'll do it a followup). Note that there's still a fixed-size buffer within gcc::jit::recording::context, the field: char m_first_error_str[1024]; Currently this is populated using strncpy followed by an explicit write of a truncation byte to make sure, but it *is* another truncation. Presumably I should address this in a followup, by making that be dynamically-allocated? (perhaps by making errors be first-class entities in the API, by introducing a gcc_jit_error subclass of gcc_jit_object, with a location field as well as the text message). Committed to branch dmalcolm/jit: gcc/jit/ChangeLog.jit: * internal-api.c (gcc::jit::dump::write): Eliminate fixed-size buffer "buf" by replacing call to vsnprintf with one to vasprintf and a free, emitting an error on the dump's context if a malloc failure occurs. (gcc::jit::recording::context::add_error_va): Likewise, using a precanned message if the malloc inside vasprinf fails. Split local "buf" into "malloced_msg" and "errmsg" to ensure that we free the message iff we're using one malloc-ed by vasprintf. (gcc::jit::recording::string::from_printf): Eliminate fixed-size buffer "buf" by replacing call to vsnprintf with one to vasprintf and a free, emitting an error on the relevant context if a malloc failure occurs. --- gcc/jit/ChangeLog.jit | 15 +++++++++++++++ gcc/jit/internal-api.c | 50 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit index 4ddd3cb..3cadaab 100644 --- a/gcc/jit/ChangeLog.jit +++ b/gcc/jit/ChangeLog.jit @@ -1,5 +1,20 @@ 2014-09-24 David Malcolm <dmalc...@redhat.com> + * internal-api.c (gcc::jit::dump::write): Eliminate fixed-size + buffer "buf" by replacing call to vsnprintf with one to vasprintf + and a free, emitting an error on the dump's context if a malloc + failure occurs. + (gcc::jit::recording::context::add_error_va): Likewise, using + a precanned message if the malloc inside vasprinf fails. Split + local "buf" into "malloced_msg" and "errmsg" to ensure that we + free the message iff we're using one malloc-ed by vasprintf. + (gcc::jit::recording::string::from_printf): Eliminate fixed-size + buffer "buf" by replacing call to vsnprintf with one to vasprintf + and a free, emitting an error on the relevant context if a malloc + failure occurs. + +2014-09-24 David Malcolm <dmalc...@redhat.com> + * dummy-frontend.c: Update copyright year. Follow standard for initial includes by removing redundant include of "ansidecl.h". * internal-api.c: Follow standard for initial includes by removing diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c index 76ada70..15e9f81 100644 --- a/gcc/jit/internal-api.c +++ b/gcc/jit/internal-api.c @@ -95,12 +95,20 @@ dump::~dump () void dump::write (const char *fmt, ...) { - char buf[4096]; va_list ap; + char *buf = NULL; + va_start (ap, fmt); - vsnprintf (buf, sizeof (buf), fmt, ap); + vasprintf (&buf, fmt, ap); va_end (ap); + if (!buf) + { + m_ctxt.add_error (NULL, "malloc failure writing to dumpfile %s", + m_filename); + return; + } + fwrite (buf, strlen (buf), 1, m_file); /* Update line/column: */ @@ -114,6 +122,8 @@ dump::write (const char *fmt, ...) else m_column++; } + + free (buf); } recording::location * @@ -680,8 +690,14 @@ recording::context::add_error (location *loc, const char *fmt, ...) void recording::context::add_error_va (location *loc, const char *fmt, va_list ap) { - char buf[1024]; - vsnprintf (buf, sizeof (buf) - 1, fmt, ap); + char *malloced_msg; + const char *errmsg; + + vasprintf (&malloced_msg, fmt, ap); + if (malloced_msg) + errmsg = malloced_msg; + else + errmsg = "out of memory generating error message"; const char *ctxt_progname = get_str_option (GCC_JIT_STR_OPTION_PROGNAME); @@ -692,19 +708,21 @@ recording::context::add_error_va (location *loc, const char *fmt, va_list ap) fprintf (stderr, "%s: %s: error: %s\n", ctxt_progname, loc->get_debug_string (), - buf); + errmsg); else fprintf (stderr, "%s: error: %s\n", ctxt_progname, - buf); + errmsg); if (!m_error_count) { - strncpy (m_first_error_str, buf, sizeof(m_first_error_str)); + strncpy (m_first_error_str, errmsg, sizeof(m_first_error_str)); m_first_error_str[sizeof(m_first_error_str) - 1] = '\0'; } m_error_count++; + + free (malloced_msg); } const char * @@ -797,15 +815,23 @@ recording::string::~string () recording::string * recording::string::from_printf (context *ctxt, const char *fmt, ...) { - char buf[4096]; va_list ap; - va_start (ap, fmt); - - vsnprintf (buf, sizeof (buf), fmt, ap); + char *buf = NULL; + recording::string *result; + va_start (ap, fmt); + vasprintf (&buf, fmt, ap); va_end (ap); - return ctxt->new_string (buf); + if (!buf) + { + ctxt->add_error (NULL, "malloc failure"); + return NULL; + } + + result = ctxt->new_string (buf); + free (buf); + return result; } recording::string * -- 1.7.11.7