This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PING][PATCH][RFC] vterminate use of malloc-less demangler


Thanks for the notes. Inlined responses below...

Benjamin Kosnik wrote:


Please review http://gcc.gnu.org/ml/gcc-patches/2007-01/msg02435.html


Questions:

1) do you have a testcase? Seems as if this is the case. Can you please include it?


Testing this work is, unfortunately, rather tricky. I do have a simple test case, appended below, that allows me to confirm manually that __verbose_terminate_handler is operating without heap use.

This test, however, is hard to fit into any test framework. First, it aborts whether or not it's successful; that's the nature of changing termination code. Second, it relies on MALLOC_CHECK_, making it Linux only and not portable. And third, it relies on carnal knowledge of libc's heap organization in order to "lightly" corrupt the heap without overdoing it.

Some of these restrictions might be avoidable. For example, interposing malloc, realloc, free, and calloc catches calls to heap functions in the same way as MALLOC_CHECK_, so could make the test slightly more portable. This still isn't fully portable across all platforms, though, and may require platform-specific link options even where practical. Likewise, the abort() can be caught with a SIGABRT handler, but again is not portable. I experimented with a test based on these ideas, but it's probably too complex to be reliable.

There seems to be no identifiable current vterminate.cc test I can extend for this patch. If you have other ideas for how to add a suitable test to the libstdc++ test suite, please let me know.

2) Are you planning on making this new function available to other users besides the verbose terminate handler? If so, the declaration should go below cxa_demangle in cxxabi.h and the function should be exported. If not, the declaration should be removed.


The __gcclibcxx_demangle_callback() function is intended as a private contract between glibc and libstdc++. There's no plan to use it elsewhere. Callback-based demangling for general use is provided by cplus_demangle_v3_callback(), supplied by libiberty's include/demangle.h as an extension to the existing cplus_demangle_v3(). Moving the declaration of __gcclibcxx_demangle_callback() to cxxabi.h is also slightly misleading, as the function isn't actually part of the defined ABI.

The current declaration can't be removed if not placed in cxxabi.h -- this would prevent the vterminate.cc from compiling.

3) Do you see any other functions in libsupc++ that start with __gcclibcxx_? If not, what do the rest of the runtime functions start with, and what do you think about using that prefix?


There don't appear to be any other functions that are private contracts between glibc and libstdc++, so this name seems reasonable. In particular, it's not part of the C++ ABI, so naming it __cxa_... was deemed unacceptable. (In practice, it's actually a variant of __cxa_demangle(), but has to have a different name as the specification of __cxa_demangle() doesn't permit malloc-less operation).

The function is already in glibc, named thus, as part of revision 121305 -- this patch merely activates it. The name was suggested as a way to indicate glibc functions that are specifically intended for use by C++, but not part of any defined ABI.

// Compile with
// g++ -Wall -W -pedantic terminate_test.cc -o terminate_test
//   path/to/new/libstdc++.a

#include <new>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


// Environment variable that controls malloc() error reporting.
static const char *const MALLOC_CHECK = "MALLOC_CHECK_";
// Required value of MALLOC_CHECK_.
static const char *const REQUIRED_MALLOC_CHECK = "3";


// Set MALLOC_CHECK_ to 3 and re-exec the entire program again.  Libc tests
// this value only once, on program startup, so re-setting it here is too late
// for this invocation.  To successfully change it, then, we also have to re-
// exec the entire program.
static void
reexecute_with_malloc_check (char *const argv[])
{
  if (setenv (MALLOC_CHECK, REQUIRED_MALLOC_CHECK, 1) != 0)
    {
      perror ("setenv");
      exit (EXIT_FAILURE);
    }

  // No return expected from a successful exec().
  execv (argv[0], argv);
  perror ("execv");
  exit (EXIT_FAILURE);
}


// General buffer size, used as an allocation to corrupt heap.
static const size_t BUFFER_SIZE = 32;
// Eight bytes of overrun and underrun should nicely clobber the heap arena
// structures on either side of the allocated block, but not go beyond that.
static const size_t OVERRUN = 8;
static const size_t UNDERRUN = 8;
// Visibly poisonous data.
static const int POISON = 0xaa;


// Deliberately corrupt the heap.  Done by allocating a short buffer and
// both underrunning and overrunning on write.  The buffer's also leaked,
// but it's not a problem.
static void
corrupt_heap ()
{
  char *buffer = static_cast<char*>(std::malloc (BUFFER_SIZE));
  std::memset (buffer - UNDERRUN, POISON, BUFFER_SIZE + UNDERRUN + OVERRUN);
}


// Verify that demangling works when the heap is corrupted.  We aim to arrive
// in __gnu_cxx::__verbose_terminate_handler(), accomplished by throwing an
// unhandled exception.
static void
verify_demangler () {
  try {
    // Throwing std::bad_alloc() will cause memory allocations, so we want to
    // leave the heap intact until we've created the exception.
    throw std::bad_alloc ();
  }
  catch (std::exception e) {
    // Corrupt the heap, then rethrow the std::bad_alloc.  This should take
    // us into terminate() with a corrupt heap.  If the callback-based
    // demangler is working, we should still see a message indicating what
    // was thrown.
    corrupt_heap ();
    throw;
  }
}


// Main program.  Ensures MALLOC_CHECK_ is as expected, and then verifies
// demangling.
int
main (int, char *const argv[])
{
  // MALLOC_CHECK_ needs to be 3 for this test to work.  If it's not, re-
  // execute the program.
  const char *const malloc_check = getenv (MALLOC_CHECK);
  if (!malloc_check)
    reexecute_with_malloc_check (argv);
  else
    {
      const int value = strtol (malloc_check, NULL, 0);
      if (value != strtol (REQUIRED_MALLOC_CHECK, NULL, 0))
        reexecute_with_malloc_check (argv);
    }

  // Verify demangling works.  If it does, and if we've set up all of the
  // conditions required for the test, this call should not return.
  verify_demangler ();
  std::cout << "Fail" << std::endl;
  return EXIT_FAILURE;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]