stack overflow detection?

Does clang offer any tools for detecting when a program is about to
segfault due to stack overflow?

Thanks,
Greg

No such support exists at the moment.

-Eli

Does clang offer any tools for detecting when a program is about to
segfault due to stack overflow?

If you want dynamic detection, then Address Sanitizer (which is built into Clang) or SAFECode (which has its own version of Clang into which it is integrated) will do the trick.

If you're asking about the Clang static analyzer, then I do not know.

-- John T.

Thanks for the quick replies!

If you want dynamic detection, then Address Sanitizer (which is built into Clang) or SAFECode

Dynamic detection. I tried with Address Sanitizer and no luck
out-of-the-box. I think it can detect stack-buffer-overflow (aka
stack corruption), but not stack overflow. I also tried running the
code with ASan inside a pthread with heap-allocated memory for its
stack, but since the pthread library itself is not instrumented, it
did not detect the heap-buffer-overflow when the thread's stack
overflows.

or SAFECode

Can you point me to an example?

Eli Friedman wrote:

No such support exists at the moment.

Is anyone aware of another C compiler that adds instrumentation for
stack overflow detection?

Thanks,
Greg

I think I misunderstood. Are you asking about the case in which the stack is about to exceed the limit set by the operating system? I assumed you were talking about stack buffer overflows.

-- John T.

gcc support stack overflow, but I never try before.

http://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/Code-Gen-Options.html#Code-Gen-Options

-fstack-check
Generate code to verify that you do not go beyond the boundary of the stack. You should specify this flag if you are running in an environment with multiple threads, but only rarely need to specify it in a single-threaded environment since stack overflow is automatically detected on nearly all systems if there is only one stack.
Note that this switch does not actually cause checking to be done; the operating system must do that. The switch causes generation of code to ensure that the operating system sees the stack being extended.

在 2013年9月18日 上午6:22,“John Criswell” <criswell@illinois.edu>写道:

...

If you want dynamic detection, then Address Sanitizer (which is built into Clang) or SAFECode

Dynamic detection. I tried with Address Sanitizer and no luck
out-of-the-box. I think it can detect stack-buffer-overflow (aka
stack corruption), but not stack overflow.

You also have the more traditional tools like -fstack-protector or
-fstack-protector-all, FORTIFY_SOURCES=2. FORTIFY_SOURCES will help in
situations where the compiler can deduce destination buffer sizes. In
this case, the compiler will insert a call to a 'safer' variant of a
dangerous function (for example, strlcat/strcat_s instead of strcat).
It applies to both stack and heap. All of these should be present in
your production code.

If you're also concerned about what the overflow leads to (most often
code execution), then you can also use -Wl,-z,noexecstack and
-Wl,-z,noexecheap. The bad guy usually wants to corrupt memory and
execute his code, so these should probably be present in your
production code if available.

There are some others too.
C-Based Toolchain Hardening - OWASP.

Sorry to drift a bit from "what does Clang offer".

Jeff

Thanks for the quick replies!

> If you want dynamic detection, then Address Sanitizer (which is built
into Clang) or SAFECode

Dynamic detection. I tried with Address Sanitizer and no luck

AddressSanitizer does not try to detect stack overflow (not to be mixed
with stack-buffer-overflow).
The reason is simple: when stack overflow happens it is already detected
(you get a SEGV).
However, by default when stack overflow happens the SEGV kills the process
silently because
the signal handler has no stack to run on.
This can be solved with sigaltstack() and AddressSanitizer does this under
a separate
(experimental) flag ASAN_OPTIONS=use_sigaltstack=1

# Running with default 8Mb stack
% clang -g -fsanitize=address -O
~/llvm/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_call_stack.cc
; ./a.out
[40000] ptr: (nil)
...
[00000] ptr: 0x7fff0b7c4140
# Passed

# Running with a small stack
% (ulimit -s 1000; ./a.out; echo $? )
[40000] ptr: (nil)
...
[33000] ptr: 0x7fff0e155120
139 # FAILED

# Running with a small stack and with sigaltstack
% (ulimit -s 1000; ASAN_OPTIONS=use_sigaltstack=1 ./a.out; echo $? ) 2>&1

head

ASAN:SIGSEGV

Is there a bug tracking what is needed to enable this by default? Might be
a nice project for folks that want to improve support for this situation.

Not that I know of. There will be users who would not like this by default.
sigaltstack in asan is not cheap -- it currently costs 32K RAM per thread.
We have users with default 64K per thread stack and they set this strict
limit for a good reason -- they have thousands of threads.
So, adding 50% to their stack size is not great.
remember that sigaltstack does not help to detect any new bugs -- it just
makes the SEGV a bit more verbose.

Having said that, I am actually not opposed to setting use_sigaltstack=1 by
default.
It's just that we didn't spend much time evaluating it.

--kcc

I’ve filed https://code.google.com/p/address-sanitizer/issues/detail?id=224 for the record.

–kcc

Perfect! I can confirm this works exactly as described using clang
3.3 running on Ubuntu 12.04 on x86. Here's what I did:

$ export ASAN_SYMBOLIZER_PATH=`which llvm-symbolizer`
$ clang --version
clang version 3.3 (tags/RELEASE_33/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix

$ cat test.c
static void loop() {
  loop();
}

int main() {
  loop();
  return 0;
}

$ clang -g -o test test.c && ./test
Segmentation fault

$ clang -g -fsanitize=address -o test test.c && ./test
Segmentation fault

$ ASAN_OPTIONS=use_sigaltstack=1 clang -g -fsanitize=address -o test
test.c && ./test

==12279==ERROR: AddressSanitizer: SEGV on unknown address
0x7fffb5066960 (pc 0x00000042d4a7 sp 0x7fffb5066940 bp 0x7fffb5067970
T0)
AddressSanitizer can not provide additional info.
    #0 0x42d4a6 in loop test.c:1
    #1 0x42d4db in loop test.c:3
    #2 0x42d4db in loop test.c:3
    #3 0x42d4db in loop test.c:3
    ...

I agree that this option should be enabled by default. This is useful stuff!

-Greg

Minor correction (environment variable is for the runtime, not compile-time).

was:
$ ASAN_OPTIONS=use_sigaltstack=1 clang -g -fsanitize=address -o test
test.c && ./test

should be:
$ clang -g -fsanitize=address -o test test.c &&
ASAN_OPTIONS=use_sigaltstack=1 ./test

-Greg

Have you guys had any luck with the "use_sigaltstack=1" on Android?
I've attempted to use it two versions: the latest clang/compiler-rt
and clang 3.3. In both cases, the ASan runtime doesn't catch the
stack overflow. Turning on verbosity=1 reports "tsd_key_inited != 0".
Here's the full log:

==28386==Parsed ASAN_OPTIONS: use_sigaltstack=1:verbosity=1
==28386==AddressSanitizer: libc interceptors initialized

`[0x20000000, 0xffffffff]` || HighMem ||
`[0x04000000, 0x1fffffff]` || HighShadow ||
`[0x00040000, 0x03ffffff]` || ShadowGap ||

MemToShadow(shadow): 0x00000000 0x00000000 0x00800000 0x03ffffff
red_zone=16
quarantine_size=64M
malloc_context_size=30
SHADOW_SCALE: 3
SHADOW_GRANULARITY: 8
SHADOW_OFFSET: 0
==28386==AddressSanitizer CHECK failed:
llvm/projects/compiler-rt/lib/asan/asan_posix.cc:110
"((tsd_key_inited)) != (0)" (0x0, 0x0)
    #0 0x40298bbf in $a _asan_rtl_
    #1 0x4029cb37 in __sanitizer::CheckFailed(char const*, int, char
const*, unsigned long long, unsigned long long) ??:0
    #2 0x40294343 in __asan::AsanTSDGet() ??:0
    #3 0x4029a6f3 in __asan::GetCurrentThread() ??:0
    #4 0x4029a9ab in __asan::GetCurrentTidOrInvalid() ??:0
    #5 0x40293deb in __asan::SetAlternateSignalStack() ??:0
    #6 0x40293f5b in __asan::InstallSignalHandlers() ??:0
    #7 0x402984ff in __asan_init_v3 ??:0

I'm running on Android JellyBean and building with NDK r9.

-Greg

Have you guys had any luck with the "use_sigaltstack=1" on Android?
I've attempted to use it two versions: the latest clang/compiler-rt
and clang 3.3. In both cases, the ASan runtime doesn't catch the
stack overflow. Turning on verbosity=1 reports "tsd_key_inited != 0".

I've seen such assertion failure on Linux some time ago, but then it turned
into another failure.
The issue is tracked here:
https://code.google.com/p/address-sanitizer/issues/detail?id=224

--kcc

Meanwhile, could you try to reverse the order of these two call in asan/asan_rtl.cc and see if it helps?
InstallSignalHandlers();

AsanTSDInit(AsanThread::TSDDtor);

–kcc

That change fixes the assertion problem, but the stack overflow is
still not detected. Here's the code:

static void loop() {
  loop(); // BOOM
}

int main() {
  loop();
  return 0;
}

And here's how I execute it:

$ adb shell
# cd /data/data
# ASAN_OPTIONS=use_sigaltstack=1
LD_PRELOAD=/data/data/libclang_rt.asan-arm-android.so
./example_StackExhaustion

-Greg

Are you sure your test is correct?
It looks too simplistic and the compiler may turn it into something else.
On my x86 box:

% clang z.c ; ./a.out
Segmentation fault (core dumped)
% clang z.c -O; ./a.out

Are you sure your test is correct?
It looks too simplistic and the compiler may turn it into something else.
On my x86 box:

% clang z.c ; ./a.out
Segmentation fault (core dumped)
% clang z.c -O; ./a.out
# <passed>

> Meanwhile, could you try to reverse the order of these two call in
> asan/asan_rtl.cc and see if it helps?
> InstallSignalHandlers();
> AsanTSDInit(AsanThread::TSDDtor);

That change fixes the assertion problem, but the stack overflow is

Good. I'll submit the change late next week.

Sorry for delay, committed as r192892.
The failure only affected the combination
of ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 and so we didn't see it.

--kcc