Skip to content

Commit

Permalink
Fix: generate probe registration constructor as a C++ constuctor
Browse files Browse the repository at this point in the history
Observed issue
==============

Applications which transitively dlopen() a library which, in turn,
dlopen() providers crash when they are compiled with clang or
if LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP is defined.

  Core was generated by `././myapp.exe'.
  Program terminated with signal SIGSEGV, Segmentation fault.
  #0  0x00007fa94f860bc2 in check_event_provider (probe_desc=<optimized out>) at lttng-probes.c:153
  153				if (!check_type_provider(field->type)) {
  [Current thread is 1 (Thread 0x7fa94fcbc740 (LWP 511754))]

  (gdb) bt
  #0  0x00007fa94f860bc2 in check_event_provider (probe_desc=<optimized out>) at lttng-probes.c:153
  #1  lttng_ust_probe_register (desc=0x7fa94fe9dc80 <lttng_ust__probe_desc___embedded_sys>)
      at lttng-probes.c:242
  #2  0x00007fa94fe9ba3c in lttng_ust__tracepoints__ptrs_destroy ()
      at /usr/include/lttng/tracepoint.h:590
  #3  0x00007fa94fedfe2e in call_init () from /lib64/ld-linux-x86-64.so.2
  #4  0x00007fa94fedff1c in _dl_init () from /lib64/ld-linux-x86-64.so.2
  #5  0x00007fa94fdf7d45 in _dl_catch_exception () from /usr/lib/libc.so.6
  #6  0x00007fa94fee420a in dl_open_worker () from /lib64/ld-linux-x86-64.so.2
  #7  0x00007fa94fdf7ce8 in _dl_catch_exception () from /usr/lib/libc.so.6
  #8  0x00007fa94fee39bb in _dl_open () from /lib64/ld-linux-x86-64.so.2
  #9  0x00007fa94fe8d36c in ?? () from /usr/lib/libdl.so.2
  #10 0x00007fa94fdf7ce8 in _dl_catch_exception () from /usr/lib/libc.so.6
  #11 0x00007fa94fdf7db3 in _dl_catch_error () from /usr/lib/libc.so.6
  #12 0x00007fa94fe8db99 in ?? () from /usr/lib/libdl.so.2
  #13 0x00007fa94fe8d3f8 in dlopen () from /usr/lib/libdl.so.2
  #14 0x00007fa94fecc647 in mon_constructeur () at mylib.cpp:20
  #15 0x00007fa94fedfe2e in call_init () from /lib64/ld-linux-x86-64.so.2
  #16 0x00007fa94fedff1c in _dl_init () from /lib64/ld-linux-x86-64.so.2
  #17 0x00007fa94fdf7d45 in _dl_catch_exception () from /usr/lib/libc.so.6
  #18 0x00007fa94fee420a in dl_open_worker () from /lib64/ld-linux-x86-64.so.2
  #19 0x00007fa94fdf7ce8 in _dl_catch_exception () from /usr/lib/libc.so.6
  #20 0x00007fa94fee39bb in _dl_open () from /lib64/ld-linux-x86-64.so.2
  #21 0x00007fa94fe8d36c in ?? () from /usr/lib/libdl.so.2
  #22 0x00007fa94fdf7ce8 in _dl_catch_exception () from /usr/lib/libc.so.6
  #23 0x00007fa94fdf7db3 in _dl_catch_error () from /usr/lib/libc.so.6
  #24 0x00007fa94fe8db99 in ?? () from /usr/lib/libdl.so.2
  #25 0x00007fa94fe8d3f8 in dlopen () from /usr/lib/libdl.so.2
  #26 0x00005594f478c18c in main ()

Cause
=====

Building tracepoint instrumentation as C++ using clang causes
LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP to be defined due to a
compiler version detection problem addressed by another patch.

However, building with LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP
defined still results in the crash.

When LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP is defined, the
lttng_ust_event_field lttng_ust__event_fields__[...] structure is
initialized by dynamically-allocating field structures for the various
fields.

As the initialization can't be performed statically, it is performed at
run-time _after_ the execution of the library constructors has
completed.

Moreover, the generated initialization
function of the provider (lttng_ust__events_init__[...]) is declared as being a library
constructor. Hence, this causes it to run before the
tracepoint fields structures has a chance to be initialized.

This all results in a NULL pointer dereference during the validation of
the fields.

Solution
========

When building providers as C++, the initialization function is defined
as the constructor of a class. This class is, in turn, instantiated in
an anonymous namespace.

For the purposes of this patch, the use of an anonymous namespace is
equivalent to declaring the instance as 'static', but it is preferred in
C++11.

Known drawbacks
===============

None.

References
==========

A reproducer is available:
https://github.com/jgalar/ust-clang-reproducer

Problem initially reported on dotnet/runtime's issue tracker:
dotnet/runtime#62398

Relevant LTTng-UST issue:
https://bugs.lttng.org/issues/1339

Fixes: #1339
Change-Id: I51cfbe74729bd45e2613a30bc8de17e08ea8233d
Signed-off-by: Jérémie Galarneau <[email protected]>
Signed-off-by: Mathieu Desnoyers <[email protected]>
  • Loading branch information
jgalar authored and compudj committed Dec 9, 2021
1 parent 59e5703 commit 90fe47e
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 4 deletions.
52 changes: 52 additions & 0 deletions include/lttng/ust-compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,56 @@
typedef char lttng_ust_static_assert_##c_identifier_msg[2*!!(predicate)-1]
#endif

/*
* Wrap constructor and destructor functions to invoke them as functions with
* the constructor/destructor GNU C attributes when building as C, or as the
* constructor/destructor of a variable defined within an anonymous namespace
* when building as C++.
*/
#ifdef __cplusplus
#define LTTNG_UST_DECLARE_CONSTRUCTOR_DESTRUCTOR(name, constructor_func, \
destructor_func, ...) \
namespace lttng { \
namespace ust { \
namespace details { \
class LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_constructor_destructor_, \
name) { \
public: \
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_constructor_destructor_, \
name)() __VA_ARGS__ \
{ \
constructor_func(); \
} \
~LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_constructor_destructor_, \
name)() __VA_ARGS__ \
{ \
destructor_func(); \
} \
}; \
} \
} \
} \
\
namespace { \
const lttng::ust::details::LTTNG_UST__TP_COMBINE_TOKENS( \
lttng_ust_constructor_destructor_, name) \
LTTNG_UST__TP_COMBINE_TOKENS(name, registration_instance); \
}
#else /* __cplusplus */
#define LTTNG_UST_DECLARE_CONSTRUCTOR_DESTRUCTOR(name, constructor_func, \
destructor_func, ...) \
static void LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_constructor_, name)(void) \
__attribute__((constructor)) __VA_ARGS__; \
static void LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_constructor_, name)(void) \
{ \
constructor_func(); \
} \
static void LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_destructor_, name)(void) \
__attribute__((destructor)) __VA_ARGS__; \
static void LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust_destructor_, name)(void) \
{ \
destructor_func(); \
}
#endif

#endif /* _LTTNG_UST_COMPILER_H */
24 changes: 20 additions & 4 deletions include/lttng/ust-tracepoint-event.h
Original file line number Diff line number Diff line change
Expand Up @@ -1169,13 +1169,24 @@ static struct lttng_ust_registered_probe *LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust
* linking the probe statically.
*
* Register refcount is protected by libc dynamic loader mutex.
*
* Note that when building this code as C++, the definition of constructors
* and destructors invoking the registration and unregistration functions
* must be performed _after_ the initialization of the probes.
*
* This is because we rely on the order of initialization of static variables
* and anonymous namespaces (their order of declaration) to ensure probes are
* fully initialized, see
* https://en.cppreference.com/w/cpp/language/initialization. This is especially
* important when LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP is defined as
* compound literals are then dynamically initialized.
*/

/* Reset all macros within LTTNG_UST_TRACEPOINT_EVENT */
#include <lttng/ust-tracepoint-event-reset.h>

static void
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_init__, LTTNG_UST_TRACEPOINT_PROVIDER)(void)
lttng_ust_notrace __attribute__((constructor));
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_init__, LTTNG_UST_TRACEPOINT_PROVIDER)(void) lttng_ust_notrace;
static void
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_init__, LTTNG_UST_TRACEPOINT_PROVIDER)(void)
{
Expand Down Expand Up @@ -1204,8 +1215,7 @@ LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_init__, LTTNG_UST_TRACEPOINT_PROV
}

static void
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_exit__, LTTNG_UST_TRACEPOINT_PROVIDER)(void)
lttng_ust_notrace __attribute__((destructor));
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_exit__, LTTNG_UST_TRACEPOINT_PROVIDER)(void) lttng_ust_notrace;
static void
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_exit__, LTTNG_UST_TRACEPOINT_PROVIDER)(void)
{
Expand All @@ -1217,6 +1227,12 @@ LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_exit__, LTTNG_UST_TRACEPOINT_PROV
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__probe_register_cookie___, LTTNG_UST_TRACEPOINT_PROVIDER) = NULL;
}

LTTNG_UST_DECLARE_CONSTRUCTOR_DESTRUCTOR(
LTTNG_UST_TRACEPOINT_PROVIDER,
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_init__, LTTNG_UST_TRACEPOINT_PROVIDER),
LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__events_exit__, LTTNG_UST_TRACEPOINT_PROVIDER),
lttng_ust_notrace)

/*
* LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION: Define this before
* including a tracepoint instrumentation header to hide symbols
Expand Down

0 comments on commit 90fe47e

Please sign in to comment.