llvm / llvm/llvm-project

ASan does not report passing a non-null-terminated char array to std::ostream << const char*

Open
#198,201 2 comments 0 reactions 0 assignees View on GitHub
compiler-rt:asan
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

I would like to clarify whether this is expected behavior or a bug / limitation in AddressSanitizer.

The program below passes a non-null-terminated character array to `std::ostream << const char*`. My understanding is that this is undefined behavior, because `operator<<(std::ostream&, const char*)` expects a null-terminated C string.

## Code

```cpp
#include

struct Test
{
static constexpr char kString[] = { '1', '2', '3', '4', '5', '6', '7', '8' };
};

int main()
{
std::cout << Test::kString << '\n';
return 0;
}
```

## Build Command

I compiled it with:

```sh
clang++ main.cpp \
-std=c++20 -Wall -Wextra -Wpedantic \
-g -O0 -fno-omit-frame-pointer \
-fsanitize=address,leak,undefined
```

## Actual Behavior

At runtime, the output looks consistent with reading past the end of `kString`:

```
12345678��
```

However, I do not get an AddressSanitizer report.

## Notes

The same behavior is reproducible on Compiler Explorer: https://godbolt.org/z/cGa66Y3vc

In the generated assembly, I do not see any obvious ASan redzone around `Test::kString`:

```asm
Test::kString:
.ascii "12345678"
```

For comparison, ASan does report the error for this variant compiled with the same compiler and flags:
Compiler Explorer link for this variant: https://godbolt.org/z/KaoP113rW

```cpp
#include

int main()
{
static constexpr char kString[] = { '1', '2', '3', '4', '5', '6', '7', '8' };
std::cout << kString << '\n';

return 0;
}
```

That version produces an `AddressSanitizer: global-buffer-overflow` report:

```text
READ of size 9 at 0x... thread T0
#0 ... in strlen
#1 ... in std::operator<<(std::basic_ostream>&, char const*)
#2 ... in main /app/example.cpp:6:15

0x... is located 0 bytes after global variable 'main::kString' defined in '/app/example.cpp:5' ... of size 8
SUMMARY: AddressSanitizer: global-buffer-overflow ... in std::operator<<(std::basic_ostream>&, char const*)
...
Global redzone: f9
```

This difference is what makes me think I may be missing something about how ASan handles these two cases.

## Question

1. Is it expected that ASan does not diagnose this case?
2. If this is a known limitation, is there a recommended way to catch such cases?

Contributor guide

Open the contributing guide

Research direction

Reproduce the two examples from main.cpp using the stated clang++ sanitizer command, then compare the static member case with the local static case and their Compiler Explorer outputs. Start by tracing the AddressSanitizer handling of the two global objects. Done means establishing whether the missing report is expected and documenting the limitation or identifying a concrete sanitizer change.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
testing-qa, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.