Skip to content

Bug: AddressSanitizer global-buffer-overflow in VersionInfo::getCurrentVersion when built from release tarball #2526

@MerinoSheep

Description

@MerinoSheep

Required information

Operating system:
Ubuntu 22.04 (also reproducible on any Linux with Clang/GCC + ASan)

Compiler version:
Clang 14+ / GCC 11+ with -fsanitize=address

Eclipse iceoryx version:
Reproduced on v2.0.6. The bug is present on main (commit 8c614d1) — the offending line in iceoryx_posh/source/version/version_info.cpp:111 is unchanged.

Observed result or behaviour:
AddressSanitizer reports a global-buffer-overflow inside iox::version::VersionInfo::getCurrentVersion() whenever iceoryx is built from a release tarball (i.e. without a .git directory) with -fsanitize=address.

iceoryx_posh/cmake/iceoryxversions.cmake runs git describe to populate ICEORYX_SHA1. When the source tree has no .git (release tarball, vendored copy, distro package, Bazel http_archive, etc.), the command fails silently and ICEORYX_SHA1 is defined as the empty string literal "" — that's 1 byte in the binary, including the trailing NUL.

getCurrentVersion() then constructs a CommitIdString_t like this:

CommitIdString_t shortCommitIdString(TruncateToCapacity,
                                     ICEORYX_SHA1,
                                     COMMIT_ID_STRING_SIZE /*=12*/);

The matching cxx::string TruncateToCapacity ctor in iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/string.inl (~line 90) does not check the actual length of the source pointer when an explicit count argument is supplied — it memcpy()s count bytes unconditionally. So the ctor reads 12 bytes from a 1-byte literal.

ASan output:

==X==ERROR: AddressSanitizer: global-buffer-overflow on address ...
    READ of size 12 at 0x... thread T0
      #0 __asan_memcpy
      #1 iox::cxx::string<12u>::string<...>(... ICEORYX_SHA1, 12)
            iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/string.inl:90
      #2 iox::version::VersionInfo::getCurrentVersion()
            iceoryx_posh/source/version/version_info.cpp:111
      #3 iox::roudi::RouDi setup / iox::PoshRuntime::PoshRuntime
0x... is located 0 bytes to the left of global variable ""...
    ICEORYX_SHA1 ... of size 1

Expected result or behaviour:
VersionInfo::getCurrentVersion() should not read past the end of ICEORYX_SHA1, regardless of how the build environment defined that literal (full 40-char SHA, short SHA, or empty string).

Conditions where it occurred / Performed steps:

  1. Build iceoryx from a release tarball (no .git/) — for example via Bazel http_archive of v2.0.6, distro package, or tar xf v2.0.6.tar.gz && cmake ....
  2. Compile the consuming binary with -fsanitize=address.
  3. Initialize iox::PoshRuntime (or call VersionInfo::getCurrentVersion() directly).

In our case the consumer is a ROS 2 workspace using rmw_cyclonedds_cpp + iceoryx. The first thing iox::PoshRuntime does at startup is call getCurrentVersion() to compare its version info against RouDi's. With ASan enabled, every node aborts inside rmw_create_node() before user code ever runs. Without a fix, an entire AV / robotics stack built with --config=asan cannot bring up a single node.

Additional helpful information

This is a real out-of-bounds read (not just an ASan annotation issue): memcpy is reading bytes from the rodata section past the end of the "" literal. It happens to be benign on non-instrumented builds because the read lands in adjacent rodata padding/symbols and the resulting iox::string is then truncated at the first NUL — but it is still UB, and ASan correctly reports it.

A fix is straightforward: cap the byte count to the actual length of the source via strnlen(ICEORYX_SHA1, COMMIT_ID_STRING_SIZE). This is a no-op on real git builds (where ICEORYX_SHA1 is the full 40-char SHA) since strnlen is bounded by COMMIT_ID_STRING_SIZE, so the truncation behavior is identical for any source string of length ≥ 12.

I have a PR ready with the fix and two regression tests. Will link once I have the issue number.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions