Skip to content

🐞 Bug: Detached thread in ApplyCallback may access invalidated pointers (lifecycle violation) #858

@Silver-Yang

Description

@Silver-Yang

Related issue:

Issue 852: Core-dump after SandboxDestroyCallback on arm32 plateform with version 1.2.0

Description

In ApplyCallback, a detached worker thread is created to perform the async apply operation:

std::thread worker{ [token, workCompletionData, workflowData] {
    const ADUC_Result result{ ADUC::ExceptionUtils::CallResultMethodAndHandleExceptions(
        ADUC_Result_Failure, [&token, &workCompletionData, &workflowData]() -> ADUC_Result {
            return static_cast<LinuxPlatformLayer*>(token)->Apply(workflowData);
        }) };

    workCompletionData->WorkCompletionCallback(
        workCompletionData->WorkCompletionToken, result, true /* isAsync */);
} };

worker.detach();

However, this introduces a lifecycle issue:

  • The thread is detached, meaning it runs independently of the caller.
  • The lambda captures raw pointers (workCompletionData, workflowData) which are not owned by the thread.
  • If the main thread (or workflow) exits before the worker thread completes, these pointers may become invalid.

This contradicts the comment in the code:

// Pointers passed to this method are guaranteed to be valid until WorkCompletionCallback is called.

There is currently no mechanism enforcing this guarantee when using a detached thread.


Impact

This can lead to:

  • Use-after-free
  • Undefined behavior
  • Crash / core dump during async apply
  • Non-deterministic failures depending on timing

This is especially likely when:

  • The agent shuts down during an ongoing apply
  • The workflow lifecycle ends early
  • Errors or reboots occur before callback execution

Expected Behavior

One of the following should be guaranteed:

  1. The lifetime of workflowData and workCompletionData is extended until the worker thread completes and calls WorkCompletionCallback, or
  2. The worker thread must not outlive the owning context (i.e., should not be detached without lifecycle control)

Actual Behavior

  • The worker thread is detached
  • No ownership or lifetime management is enforced
  • The thread may access invalid memory if the main thread exits early

Root Cause Analysis

  • Use of std::thread::detach() without lifecycle synchronization
  • Capturing raw pointers into an async execution context
  • Lack of ownership model (no shared_ptr / ref-counting / cancellation)

Suggested Fixes

Possible approaches:

  1. Avoid detach()

    • Use joinable threads or a managed thread pool
  2. Introduce shared ownership

    • Use std::shared_ptr for workflowData and related structures
  3. Agent-managed async execution

    • Let the agent manage thread lifecycle instead of handler
  4. Shutdown / cancellation support

    • Ensure worker threads are stopped or joined during agent shutdown

Additional Notes

This issue appears to be a general concurrency/lifecycle design flaw and may affect other async handlers using similar patterns.


Environment

  • Component: LinuxPlatformLayer / ApplyCallback
  • Version: (please fill in)
  • Reproducibility: Intermittent (timing-dependent)

Summary

Detached threads combined with raw pointer capture can lead to use-after-free and crashes. This is a lifecycle management bug rather than intended behavior.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions