Skip to content

segfault when creating an active span in an exit handler #3361

@mwaw-osl

Description

@mwaw-osl

Using the default RuntimeContextStorage (ThreadLocalContextStorage) causes a segfault when a span is made active in an exit handler registered with atexit(). This is due to thread_local storage being cleaned up before exit handlers are called, destroying the context storage Stack and causing the next Context push (in the exit handler) to segfault.

A lightly modified version of otel's simple example demonstrates this:

#include <iostream>

#include "opentelemetry/exporters/ostream/span_exporter_factory.h"
#include "opentelemetry/sdk/trace/exporter.h"
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/provider.h"
#include "opentelemetry/sdk/trace/simple_processor_factory.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
#include "opentelemetry/trace/tracer_provider.h"
#include "opentelemetry/trace/provider.h"

namespace trace_api      = opentelemetry::trace;
namespace trace_sdk      = opentelemetry::sdk::trace;
namespace trace_exporter = opentelemetry::exporter::trace;

namespace
{
void InitTracer()
{
  auto exporter  = trace_exporter::OStreamSpanExporterFactory::Create();
  auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter));

  std::shared_ptr<opentelemetry::sdk::trace::TracerProvider> sdk_provider =
      trace_sdk::TracerProviderFactory::Create(std::move(processor));

  const std::shared_ptr<opentelemetry::trace::TracerProvider> &api_provider = sdk_provider;
  trace_sdk::Provider::SetTracerProvider(api_provider);
}

void CleanupTracer()
{
  std::shared_ptr<opentelemetry::trace::TracerProvider> noop;
  trace_sdk::Provider::SetTracerProvider(noop);
}

void tracedFunction() {
auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("simpleTest");
auto span = tracer->StartSpan("testSpan");
auto scope = tracer->WithActiveSpan(span);
}

void exitHandler() {
    std::cout << "Exit handler" << std::endl;
    tracedFunction();  // THIS WILL CAUSE A SEGFAULT
    CleanupTracer();
}
}  // namespace

int main()
{
  InitTracer();

  tracedFunction();
  atexit(exitHandler);
  tracedFunction();
}

This is seen using otel 1.19.0 with g++ 13.3.0. Is opentelemetry expected to crash a program if an exit handler is registered that happens to do something that activates a span? One work around we're considering (to avoid the segfault) is using a sentinel thread_local bool that is set when the Stack is destructed to signal the end of thread_local storage (and then either create ad hoc storage or stop trying to interact with the Stack; it's not clear we'll be able to collect any meaningful trace information at this point anyways).

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions