AD3025: EnableExceptionHandling¶
Summary¶
| Property | Value |
|---|---|
| ID | AD3025 |
| Name | EnableExceptionHandling |
| Category | Security |
| Severity | Note |
| Applies to | ELF (Linux/Unix) |
Description¶
The -fexceptions compiler option generates frame unwind information for all functions. This allows glibc's implementation of POSIX thread cancellation to use proper stack unwinding instead of setjmp/longjmp, improving security for multi-threaded C code.
How It Works¶
The rule checks for the presence of exception handling sections:
.eh_frame- Exception handling frame information.eh_frame_hdr- Header for .eh_frame lookup.gcc_except_table- GCC exception table
At least one of these sections should be present for proper exception handling.
Why This Matters¶
Without proper exception handling support, pthreads cancellation in C code leaves unprotected function pointers on the stack. This creates additional exploitation targets that make buffer overflow attacks easier to execute.
The Thread Cancellation Problem¶
glibc's thread cancellation mechanism differs based on exception handling:
With -fexceptions:
pthread_cancel() → Uses C++ unwind mechanism
Stack frames properly unwound
No extra pointers on stack
Without -fexceptions:
pthread_cancel() → Uses setjmp/longjmp style
glibc places function pointers on stack
These pointers are NOT protected by canaries
Stack Layout Comparison¶
Without -fexceptions (vulnerable):
┌─────────────────┐
│ Return Address │ ← Protected by canary
├─────────────────┤
│ Canary │
├─────────────────┤
│ Cancel Handler │ ← UNPROTECTED function pointer!
├─────────────────┤
│ Local Buffer │ ← Overflow starts here
└─────────────────┘
Overflow can reach Cancel Handler WITHOUT touching canary!
With -fexceptions (safe):
┌─────────────────┐
│ Return Address │ ← Protected by canary
├─────────────────┤
│ Canary │
├─────────────────┤
│ Local Buffer │ ← No unprotected pointers
└─────────────────┘
Unwind info in .eh_frame, not on stack.
Exploitation Scenario¶
1. Multi-threaded C program without -fexceptions
2. Buffer overflow in one thread
3. Overflow overwrites cancel handler pointer
4. Another thread calls pthread_cancel()
5. glibc invokes the (corrupted) handler
6. Attacker gains code execution
7. Stack canary never detected corruption!
Who Is Affected¶
| Code Type | Risk |
|---|---|
| Multi-threaded C | Yes, if no -fexceptions |
| C++ code | No, exceptions enabled by default |
| Single-threaded C | Lower (no pthread_cancel) |
| C with no buffer ops | Lower |
Performance Overhead¶
| Aspect | Impact |
|---|---|
| Code size | ~2-5% larger (unwind tables) |
| Runtime | Zero for happy path |
| Stack unwind | Only on exceptions/cancel |
.eh_frame Section¶
With -fexceptions:
.eh_frame ← Contains unwind instructions
.eh_frame_hdr ← Lookup table for fast access
These sections enable proper stack unwinding
without storing pointers on the runtime stack.
- Thread cancellation safety: Enables proper POSIX thread cancellation handling
- Stack pointer protection: Without exception handling, glibc may spill unprotected function pointers onto the stack
- Exploit mitigation: Reduces attack surface for stack-based buffer overflows
- OpenSSF recommended: Part of the OpenSSF Compiler Hardening Guide
Vulnerability Background¶
When -fexceptions is not enabled for C code using pthreads:
- glibc's thread cancellation uses setjmp/longjmp-style unwinding
- This may place an unprotected function pointer on the stack
- Stack buffer overflows can more easily hijack control flow
- The function pointer becomes an exploitation target
Resolution¶
GCC/Clang¶
Enable exception handling for C code:
# Compile with -fexceptions
gcc -fexceptions source.c -o binary
# Especially important for multi-threaded code
gcc -fexceptions -pthread source.c -o binary
CMake¶
add_compile_options(-fexceptions)
# Or for specific targets
target_compile_options(myapp PRIVATE -fexceptions)
Makefile¶
When to Suppress¶
This rule may be suppressed for:
- Single-threaded code: Programs that don't use pthreads
- No thread cancellation: Code that doesn't use pthread_cancel
- Size-constrained: Embedded systems where binary size is critical
- Statically linked: With musl or other libcs that handle this differently
Caveats¶
- Exception handling increases binary size due to unwind tables
- The actual security benefit depends on how thread cancellation is used
- This is marked as Note level - informational, not a hard requirement
- C++ code typically has this enabled by default
Binary Size Impact¶
Without -fexceptions: Smaller binary, no .eh_frame
With -fexceptions: ~5-10% larger due to unwind tables
Checking Exception Handling¶
# Check for .eh_frame section
readelf -S binary | grep eh_frame
# Check section sizes
size --format=sysv binary | grep -E "eh_frame|gcc_except"
# Using objdump
objdump -h binary | grep eh_frame