Skip to content

[Bug]: ASan use-after-poison in absl::Status::SetPayload with small absl::Cord payload #2049

Description

@dmoody256

Describe the issue

absl::Status::SetPayload() can trigger an AddressSanitizer use-after-poison report when passed a small inline absl::Cord payload by value.

The payload is valid user code:

status.SetPayload("type.googleapis.com/reproducer",
                  absl::Cord(std::to_string(123)));

I expected this to run cleanly under ASan. Instead, ASan reports a read from user-poisoned stack memory inside:

absl::status_internal::StatusRep::SetPayload(...)
absl::Status::SetPayload(...)
main

The shadow byte is f7 (Poisoned by user). My read is that absl::Cord intentionally poisons unused inline storage for small payloads, then the Status::SetPayload by-value path / move into the payload container reads through that poisoned inline representation.

This reproduced with Abseil 20250512.1, aarch64 Linux, Clang/LLVM 22.1.0, C++20, -fsanitize=address, -Og, and -ftrivial-auto-var-init=pattern.

It did not reproduce for me with Ubuntu Clang 14.0.0.

Steps to reproduce the problem

repro.cc:

#include <string>

#include "absl/status/status.h"
#include "absl/strings/cord.h"

int main() {
  absl::Status status(absl::StatusCode::kInternal, "force non-ok heap rep");

  // Small payloads are stored inline by absl::Cord. Under ASan, Cord poisons
  // unused inline bytes. The payload is then passed by value through
  // Status::SetPayload().
  status.SetPayload("type.googleapis.com/reproducer",
                    absl::Cord(std::to_string(123)));

  return status.ok() ? 1 : 0;
}

CMakeLists.txt:

  cmake_minimum_required(VERSION 3.16)

  project(absl_status_cord_asan_repro CXX)

  set(CMAKE_CXX_STANDARD 20)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  set(CMAKE_CXX_EXTENSIONS OFF)

  if(NOT DEFINED ABSL_SOURCE_DIR)
    message(FATAL_ERROR "Pass -DABSL_SOURCE_DIR=/path/to/abseil-cpp")
  endif()

  add_compile_options(
    -fsanitize=address
    -g
    -Og
    -fno-omit-frame-pointer
    -ftrivial-auto-var-init=pattern
  )
  add_link_options(-fsanitize=address)

  add_subdirectory("${ABSL_SOURCE_DIR}" abseil-build EXCLUDE_FROM_ALL)

  add_executable(absl_status_cord_asan_repro repro.cc)
  target_link_libraries(absl_status_cord_asan_repro PRIVATE absl::status absl::strings)

  Build and run:

  git clone https://github.com/abseil/abseil-cpp.git
  cd abseil-cpp
  git checkout 20250512.1
  cd ..

  cmake -S absl_status_cord_asan -B build-absl-status-cord \
    -DABSL_SOURCE_DIR="$PWD/abseil-cpp" \
    -DCMAKE_CXX_COMPILER=clang++

  cmake --build build-absl-status-cord -j

  env LSAN_OPTIONS=detect_leaks=0 ./build-absl-status-cord/absl_status_cord_asan_repro

Expected: program exits cleanly.

Actual:

  ERROR: AddressSanitizer: use-after-poison
  READ of size 8
      #0 ... absl::status_internal::StatusRep::SetPayload(...)
      #1 ... absl::Status::SetPayload(...)
      #2 ... main ... repro.cc

  Address ... is located in stack of thread T0 at offset 32 in frame
      #0 ... absl::Status::SetPayload(...)

    This frame has 1 object(s):
      [32, 48) 'agg.tmp' <== Memory access at offset 32 is inside this variable

  Shadow byte legend:
    Poisoned by user: f7

What version of Abseil are you using?

Abseil 20250512.1,

What operating system and version are you using?

Ubuntu 22.04 aarch64 Linux

What compiler and version are you using?

Clang/LLVM 22.1.0, C++20, -fsanitize=address, -Og, and -ftrivial-auto-var-init=pattern.

What build system are you using?

cmake

Additional context

absl_status_cord_asan.tar.gz

Metadata

Metadata

Assignees

No one assigned

    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