An Automated Approach for Software Reliability and Security
Zhen XiaoSenior Technical Staff Member
AT&T Labs – ResearchJoint work with Christof Fetzer
Motivation
• Software reliability is becoming increasingly important.– Financial transactions, virtual office environment, life
critical applications, etc.
• Does the software function correctly under exceptional or stressful settings?– Field experience indicates that the error handling paths
in the software typically contain most bugs.– Testing all boundary conditions before the official
release of the software can be prohibitively expensive.
Example: Robustness Violation
strcpy(destination, source)
destination
source length
unmapped pages
detectable withhelp of signal handler
Security Problems
strcpy(dst, attack_string)
destination
attack_string length
shell scriptattack code
We want to avoid all buffer overwrites!
Why C/C++?Sourceforge Projects 10/18/2000
(8,296 open source projects)
Java11%
Other41% C
27% C++21%C/C++
48%
Java
Other
C
C++
What is a good approach for increasing software reliability?
Challenges
• Transparency– Cannot assume access to the source code for
applications & libraries.
• Cost effectiveness & scalability– Large number of applications, shared libraries, and
functions.– Applications and libraries may change often.
• Flexibility– Different applications may need different levels of
protection
Most Popular Libraries
Distribution of #Libraries vs. #Applications
Linux SuSE 8.0
~49Number of undefined func/appl
15431Number of applications
933Number of shared libraries
Automation is essential for protecting a large number of libraries and applications!
The HEALERS Approach
• Fault-containment wrappers– Intercept function calls into the dynamic link libraries.– Provide transparent protection for software without
source code access.
• Automated fault-injection experiments– Find all functions defined in a library– Detect arguments that cause a function to crash– Derive safe argument types for each function.– Prevent heap and stack buffer overflows and a large set
of robustness violations.
The HEALERS Approach (Cont’d)
• Micro-generator architecture– Generate a variety of wrappers through a set of micro-
generators.
– Applications only pay the overhead they actually need.
Extracting Libraries and Undefined Functions
Example: Unix Wrappers
• Overwrite “exit” by “abort”• Wrapper:
void exit(int status) { abort(); }
• Start wrapper:> setenv LD_PRELOAD `pwd`/wrapper.so
• No need to change existing programs!> dateWed Oct 18 13:06:41 EDT 2000Abort
Approach: Wrap Functions
Application
SharedLibrary
strcpy(d,s)
strcpy(d,s)
Wrapper strcpy(d,s) check d and s
calls
calls
Wrap malloc, calloc, realloc, free Use Red/Black trees to keep meta-data for each
allocated block– Update Red/Black tree whenever such a function is called
– Red/Black node contains address range of each block
• Check if the destination buffer is sufficiently large:– Search Red/Black tree to find pointer
– returns #bytes between pointer and the end of the block
• Complexity:– Insert, Remove, Find: O(log(entries))
Keeping Track of the Heap
Wrapper Structure
Original Function
Wrapper needs to call the “original” functionAccess to original function by “dlsym ”
Example:–dlsym(RTLD_NEXT, “strcpy”);
– returns address of original “strcpy” function
Wrapper Structure
Wrapper Generation Process
Fault-InjectionExperiments
WrapperGeneration
Phase 1: Phase 2:
Function Spec.
Generation
Wrappergeneration
SharedLibrary
WrapperFunctionSpecification
Run-time checks [SRDS2001]
• Example: function asctime(d)– wrapper checks that d is buffer of sufficient
size– if not, return error code– otherwise, call original function
Asctime Wrapper
char* asctime (const struct tm* a1) {char* ret;if (!check_R_ARRAY_NULL(a1,44)) {
errno = EINVAL ;ret = (char*) (nil);goto PostProcessing;
}asm("movl %ebp,%esp");asm("popl %ebp");asm("jmp *libc_asctime_0");
PostProcessing: ;return ret;
}
Function Specification for asctime<function> <name>asctime</name> <argument>const struct tm* <robust_type>R_ARRAY_NULL[44]</robust_type> </argument> <return_type>char *</return_type> <error_value>NULL</error_value> <errors> <errno>EINVAL</errno> </errors> <attribute>unsafe</attribute></function>
Robust Argument Type
Generation of Function Specifications
HeaderFiles
ManualPages
SharedLibrary
Fault-Injector
Fault-Injector
Fault-Injector
Fault-Injector
FunctionDeclaration
FunctionDeclaration
FunctionDeclaration
FunctionDeclaration
Generator
Generator Algorithm
For each function in library
1. Find prototype of function:• Parse include files given in manual pages, or• Parse all header files that might contain prototype
2. Generate fault-injector using prototype• Define sequence of hypotheses• Perform automated fault injection experiments to test
each hypothesis• Select non-rejected hypothesis
Prototype Extraction for glibc2.2"External": no
prototype found3%
"Internal"34%
prototype found in manual
31%
prototype found
with grep32%
"External":prototype
found63%
Total: 1278 global functions
Errors in Manual430 of 840 functions in manual
1.4 10.5 88.1
0% 20% 40% 60% 80% 100%
No header file in manual Wrong header file in man Correct header file
Computation of Robust Argument Types
• Ideal Goal: determine set of argument values that crash a function
set of all argument values
no crashcrash
wrapper accepts these values, rejects others
Idea Goal Not Realistic
• Revised Goal: Accurate but not necessarily complete checks:
no crash
set of all argument values
crash
wrapper accepts these values, rejects others
Approach
• Divide argument value set in disjoint subsets
set of all argument values
Fundamental Type
Classify Fundamental Types Using Fault-Injections
• Divide argument values in disjoint subsets
no value crashes f
all values crash f
some values crash f
wrapper accepts these
values, rejects others
Example: Fixed Arraysset of all argument values
RONLY_FIXED
WONLY_FIXED RW_FIXED
RONLY_FIXED[1],
RONLY_FIXED[2],
RONLY_FIXED[3],
…
NULL
UNMAPPED
INVALID
not mappedread only
3
asctime: injection results
• Crashes for all test cases in:– RONLY_FIXED[i] for i < 44
– WONLY_FIXED[i] for any i
– RW_FIXED[i] for i < 44
– INVALID crashes
• Does not crash for test cases in:– RONLY_FIXED[i] for i >= 44
– RW_FIXED[i] for i >= 44
– NULL
Type Hierarchy
• Need to be able to compute union of value sets
• Define type hierarchy:– fundamental types: value sets of any two
fundamental types is non-overlapping– union types: value set of this type is the union
of the value set of its “subtypes”
Fixed Array Type HierachyUNCONSTRAINED
INVALID
RW_ARRAY_NULL[t]
NULLR_ARRAY [t]
t≤v
W_ARRAY[t]
t≤v
RONLY_FIXED[v] WONLY_FIXED[v]RW_FIXED[v]
RW_ARRAY [u]
u≤v
R_ARRAY_NULL[s] W_ARRAY_NULL[s]
s≤t
s≤t
s≤t
t≤ut≤u
asctime: Robust Argument TypeUNCONSTRAINED
RW_ARRAY_NULL[t]
R_ARRAY [44]
44≤v
W_ARRAY[t]
RONLY_FIXED[v]
RW_FIXED[v]
RW_ARRAY [44]
44≤v
R_ARRAY_NULL[s] W_ARRAY_NULL[s]
s≤t
s≤t
s≤t
44≤44 t≤u
asctime: Robust Argument TypeUNCONSTRAINED
RW_ARRAY_NULL[44]
NULLR_ARRAY [44]
44≤v
W_ARRAY[t]
RONLY_FIXED[v]
RW_FIXED[v]
RW_ARRAY [44]
44≤v
R_ARRAY_NULL[44] W_ARRAY_NULL[s]
44≤44
44≤44
s≤t
44≤44 t≤u
Phase 2: Generation of Wrappers
RetryWrapper
SecurityWrapper
RobustnessWrapper
…
WrapperGenerator
FunctionSpecification
Flags
Micro-Generators
Generator
Wrapper Types
Data Collection Wrappers:– collect failure data and usage data
Retry Wrapper: [ISSRE 2002]
–
retry failed function calls
Robustness Wrappers: [DSN2002]– prevent segmentation failures– try to keep applications running
Security Wrapper: [SRDS2001]
–
detect buffer overflows on stack and heap
…
Wrapper Types
Data Collection Wrappers:– collect failure data and usage data
Retry Wrapper: [ISSRE 2002]
–
retry failed function calls
Robustness Wrappers: [DSN2002]– prevent segmentation failures– try to keep applications running
Security Wrapper: [SRDS2001]
–
detect buffer overflows on stack and heap
…
Creating Profiling Wrappers
• Create Profile Wrapper:> profile_app ls
• Run wrapped ls:> wrapped_ls/ls
• Output of profile wrapper in XML
Profiling Wrapper
Wrapper Types
Data Collection Wrappers:– collect failure data and usage data
Retry Wrapper: [ISSRE 2002]
–
retry failed function calls
Robustness Wrappers: [DSN2002]– prevent segmentation failures– try to keep applications running
Security Wrapper: [SRDS2001]
–
detect buffer overflows on stack and heap
…
Security Wrapper
• Currently the wrapper can detect – heap smashing attacks (caused by C-library func.)– stack smashing attacks
• Stack Smashing Detection:– based on an approach by LibSafe– uses gcc frame pointers to check that return
address is not overwritten
Creating Security Wrappers
• Create Wrapper for individual library:> protect_library /lib/libc.so
• Create Wrapper for all libraries:> protect_all_libraries
• Info about Security Wrappers in XML
Wrapper Types
Data Collection Wrappers:– collect failure data and usage data
Retry Wrapper: [ISSRE 2002]
–
retry failed function calls
Robustness Wrappers: [DSN2002]– prevent segmentation failures– try to keep applications running
Security Wrapper: [SRDS2001]
–
detect buffer overflows on stack and heap
…
Robustness Wrapper tested with Ballista
Performance Measurements
• Pentium 3, 864Mhz, 384 Mbytes RAM
• Linux 2.4.4 kernel, SuSE 7.2
• Performance data:– each data point is 10% trimmed mean of 100
executions
Performance
Related Work
• Formal analysis– Can verify deep property of the system.– Usually abstract away many implementation details.
• Static analysis– Can check all the control paths in the program– Requires source code access.
• Ballista– Use fault-injection experiments to evaluate the robustness of
libraries and operating systems.
• Xept– Language/compiler to generate wrappers
Limitations
• Cannot prove correctness of the program.
• Only works for applications that are dynamically linked.
• Only detect faults related to library functions.
• The quality of fault-injection experiments depends on the coverage of the test cases.
Conclusion/Future Work
• Automation achieves scalability.• Flexible architecture pays off in the long run
– Facilitates code reuse.
– Easy debugging, optimization.
• Future work– Collect statistics on failure causes of common
applications.
– Generate better test hypothesis for fault-injection experiments.