constraint-based testing for buffer overflows · buffer overflow during execution. for this...
TRANSCRIPT
Constraint-based Testing for Buffer Overflows
Loui Al Sardy (✉), Francesca Saglietti (✉), Tong Tang, Heiko Sonnenberg
University of Erlangen-Nuremberg
Software Engineering (Informatik 11)
Martensstr. 3, 91058 Erlangen, Germany {loui.alsardy, francesca.saglietti, tong.tang,
heiko.sonnenberg}@fau.de
Abstract. This article proposes two heuristic approaches targeted at the
optimized generation of test cases capable of triggering buffer overflows resp.
underflows. Both testing techniques are based on guiding conditions statically
derived by Integer Constraint Analysis. First experimental evaluations
confirmed the superiority of local optimization algorithms over global ones.
Keywords: software vulnerability, buffer overflow, integer constraint analysis,
testing technique, global optimization, local optimization.
1 Introduction
It is well-known that the safe behaviour of critical cyber-physical systems may be
severely jeopardized by the intentional activation of system vulnerabilities, typically
software weaknesses known to the attacker and exploited during operation to trigger
unwarranted effects. A major part of such attacks relies on redirection techniques [1,
2, 15] aimed at shifting the instruction pointer to an illegal memory cell such as to
enforce the execution of an illegal piece of hidden code. For example, the attack
described in [12] and further analysed in [1] assumes a machine code allowing for
overlapping instructions such that shifting the instruction pointer to a cell inside an
instruction may cause the execution of malicious code hidden within a correct binary
code. A still widespread redirection technique consists of provoking buffer overflows
resp. underflows by writing data intended for a buffer into memory space lying
outside of this buffer [9].
Although buffer overflows resp. underflows may be constructively avoided during
development, e.g. by forbidding the use of widespread programming languages [18],
for a considerable part of already operating cyber-physical systems their occurrence
could not be systematically excluded a priori so far. The relevance of these
vulnerabilities is confirmed by a recent report [5] ranking stack-based resp. heap-
based buffer overflows among the four most frequent software vulnerabilities. In
addition, successful attacks on power plants based on buffer overflows were recently
reported [19].
Original source of publication:Computer Safety, Reliability, and Security - SAFECOMP 2018 WorkshopsB. Gallina et al. (Eds.)Lecture Notes in Computer Science, Volume 11094© Springer International Publishing Switzerland 2018The final publication is available at link.springer.com
On the whole, this confirms the need for systematic approaches capable of detecting
the potential of buffer overflows resp. underflows before allowing critical systems to
become operational. The design, development and evaluation of such approaches
build the goal of the present article which is structured as follows: after an overview
on existing related approaches and their limitations (section 2), classical theoretical
foundations of Integer Constraint Analysis (ICA) are introduced in section 3. The
outcome of such a preliminary static analysis is then exploited to provide systematic
guidance during a successive dynamic analysis. Heuristic test techniques are
subsequently designed in section 4 to take advantage of the insight gained by ICA
such as to optimize the chances to trigger potential overflows or underflow before
operation. Section 5 presents a comparative evaluation of two testing approaches on
the base of two different examples before the final section 6 summarizes the work
proposed and draws some conclusions.
2 Related Work
State-of-the-art approaches address the problem of detecting exploitable buffer
overflows in different ways.
Static analyses examine source code listings without requiring code execution. They
range from mere search of source code for buffer accesses in order to derive a list of
potentially vulnerable instructions to more sophisticated code analyses in order to
derive numerical constraints for variable values proven to be necessary and / or
sufficient for buffer overflow occurrence. Evidently, for well-known theoretical
reasons involving undecidability of program behaviour, in general static analysis
alone cannot grant the detection or the exclusion of buffer overflows resp.
underflows. In other words, in general the outcome of static analysis is doomed to
incompleteness. Among these static approaches are [3, 6, 7, 13, 14, 23].
Dynamic analyses, on the other hand, aim at detecting the occurrence of buffer
overflows by executing the program during testing. Hereby, the main challenge
concerns the intelligent generation of test data capable of triggering – if possible – a
buffer overflow during execution. For this purpose, test data generation may be based
on assumptions restricting the search to program patterns reported or experienced to
be affected by illegal buffering effects, e.g. [10, 21].
Hybrid approaches combining static and dynamic analyses [4, 17] let dynamic
analysis take advantage of the insight preliminarily gained during static analysis. This
insight may allow for a justified restriction of the testing scope by targeting only those
buffer accesses preliminarily identified as potentially vulnerable while ignoring those
already statically verified as being secure with respect to overflow or underflow.
The approach proposed in this article belongs to the latter class of hybrid approaches.
Its novelty lies in the implementation of intelligent testing strategies targeted at the
fulfilment of predicates derived from existing integer constraint analysis techniques.
In more detail, the intention of the suggested testing strategy is to make full use of the
static analysis outcome to guide the search for suitable test cases capable of fulfilling
conditions guaranteeing the triggering of a buffer overflow.
3 Integer Constraint Analysis (ICA)
In the following, classical Integer Constraint Analysis is illustrated based on [8, 20].
As already mentioned, it aims at extracting information as complete as possible, in
particular necessary and sufficient constraints, such as to provide a valuable guide in
the search for exploitable vulnerabilities.
3.1 Potential Overflow Statements
Initially, only those program instructions allowing for a modification of buffer
variables are selected for further consideration, as only their execution may trigger a
buffer over- or underflow. Any such instruction is denoted as Potential Overflow
Statement (in the following abbreviated by POS). More precisely, a POS denoted by
<instr(k), V>, is a pair consisting of an instruction instr(k) located at code line k with
writing access to buffer variables building a variable set V, where the access may be
of different type:
type 1: data may be directly written into buffer elements, e.g. by the assignment
list[i-1] = 1 the integer 1 is written into the i-th element of a buffer variable list;
type 2: alternatively, instr(k) may invoke a function parameterized by a buffer
and writing data into this buffer, e.g. the function call strcpy(des, src) reading
data from string src and writing them into the array referenced by des.
For any POS = <instr(k),V>, a subprogram results from the original program by
slicing, i.e. by deleting all instructions which do not influence the variables from V
[25]. By doing so, the search for test cases triggering buffer over- resp. underflows
can be reduced to the program slice derived.
3.2 Necessary and Sufficient Constraint Excluding Buffer Overflows
In the following, let C(NBO) (abbreviating “Constraint for No Buffer Overflow”)
represents a necessary and sufficient condition for buffer accesses at POS causing
neither overflows nor underflows.
In other words, fulfillment of C(NBO) upon execution of the POS instruction allows
to exclude buffer overflows and underflows, while, on the contrary, violation of
C(NBO) guarantees the occurrence of at least one overflow or underflow.
Depending on the type of writing data into buffer elements (s. above), the
determination of C(NBO) may vary:
type 1 (direct writing): absence of buffer overflowing or underflowing requires
the index of overwritten elements to lie in the range between 0 and the allocated
buffer memory size (in the following denoted as buf.aSize); for example, if the
POS instruction assigns a new value to the element buf[i] of a buffer buf, then
C(NBO) reads 0 i < buf.aSize.
type 2 (indirect writing by call): if buffer variable(s) are parameter(s) of a
function, constraint C(NBO) can be analogously derived, in particular for the C
standard functions modifying buffer variables, as listed in [7, 11]; for example,
for a function call gets(str) reading a string input from stdin and writing this
string into the buffer str, the C(NBO) reads str.aSize stdin.len.
3.3 Necessary Condition C(POS) upon Reaching a POS
In the following, let C(POS) (abbreviating “Constraint for reaching POS”) represent a
predicate on program variables and buffer parameters always fulfilled upon reaching
POS, but before executing the given POS instruction. The determination of a C(POS)
requires an accurate analysis of the control and data flow of the corresponding
program slice. More precisely, analysis of data flow along all control flow paths
reaching the POS results in path-specific constraints whose logical disjunction may be
taken to provide C(POS).
3.4 Logical Relations between C(POS) and C(NBO)
Since C(POS) holds upon reaching POS and C(NBO) is sufficient and necessary for
the exclusion of buffer overflows and underflows, the ICA focuses on examining
whether C(POS) may imply C(NBO) or not (s. Fig. 1), where the outcome can be
classified as follows:
if C(POS) implies C(NBO), absence of buffer overflows at POS is proven;
if C(POS) implies ¬C(NBO), a buffer over- or underflow is guaranteed to occur
whenever POS is reached;
if C(POS) neither implies C(NBO) nor ¬C(NBO), by executing the POS
instruction a buffer over- or underflow might occur. In order to provoke it, test
cases must be generated such as to reach POS and to violate C(NBO).
These three possibilities are summarized in Table 1.
Fig. 1. Overview of integer constraint analysis
Program slice execution
reaching POS
Execution of POS instruction
does not cause any over-/underflow
C(POS) C(NBO) ?
Table 1. Possible outcomes of Integer Constraint Analysis
Implications Buffer over-/underflow
occurrence
Test
need
Test goal
I1: C(POS) C(NBO) never no need ---
I2: C(POS) C(NBO) whenever
POS executed
need reach POS
neither I1 nor I2 if POS executed such that
C(NBO) violated
need reach POS such that
C(NBO)
4 ICA-Guided Dynamic Analysis
4.1 Test Case Generation based on Guided Evolution
The test goal is to generate test cases triggering a buffer over- or underflow at a given
POS, i.e. test cases which upon reaching POS fulfill ¬C(NBO). Evidently, upon
reaching POS, C(POS) is fulfilled. In order to enforce coincidentally the validity of
¬C(NBO), the conjunction C(POS) ¬C(NBO), can provide guidance for the
optimization of variables contained both in C(POS) and in ¬C(NBO).
The heuristic test generation technique considered in the following is based on genetic
algorithms [16]: in general, it starts with an initial random population which
successively evolves into new generations by making use of the classical genetic
operators selection, mutation and crossover. The evolution is controlled via fitness
evaluation. The process is repeated until one of the following termination conditions
is fulfilled:
a given maximal number of iterations is reached;
an individual with a given maximum fitness is generated.
4.2 Heuristic Test Case Generation based on Global Optimization
The first approach developed is based on global optimization. In this case, the fitness
of a test case should reflect the degree of fulfilment of the target constraint C(POS)
¬C(NBO) upon reaching POS. Therefore, for individuals reaching POS the fitness is
maximized by minimizing the degree of violation of the same guidance condition
which can be captured by a distance metric P as proposed in [22]. Such a metric
depends on the type of the underlying predicate P, as summarized in Table 2 for some
typical predicates, where K denotes a positive constant intended to reflect a
penalization in case of a predicate violation.
Table 2. Atomic predicates and distance metrics as proposed in [22]
atomic predicate P distance metric P
integer a, b
a = b if abs(a – b) = 0 then 0 else abs(a – b) + K
a ≠ b if abs(a – b) ≠ 0 then 0 else K
a < b if a – b < 0 then 0 else (a – b) + K
a ≤ b if a – b ≤ 0 then 0 else (a – b) + K
a > b if b a < 0 then 0 else (b – a) + K
a ≥ b if b – a ≤ 0 then 0 else (b – a) + K
boolean A, B
A if A = TRUE then 0 else K
A B min (A, B)
A B (A + B)
A conjunction AA k
n
1k of predicates Ak (1 k n) has the distance metric
n
1iA Ai
(1)
If the POS instruction is executed more than one time, upon reaching POS the
variables may have different values depending on the number of times POS has
already been reached in advance. As buffer overflowing resp. underflowing can be
critical as soon as it happens, fulfilling the guidance conditions once at any time
during execution is enough to trigger a buffer overflow. Therefore, in such cases the
distance metric is taken as the minimum distance metric evaluated during execution.
Formally, let t denote a test case that reaches POS m times during the whole
execution and let A(t, j) denote the distance metric w.r.t. a guidance condition A
upon reaching POS for the j-th time. The metric capturing the distance of test case t is
then evaluated by:
)j,t(min)t( A
mj1A
(2)
4.3 Heuristic Test Case Generation based on Local Optimization
Although the aforementioned approach based on global optimization revealed as
successful in some cases (s. section 5 for an example), it failed to detect a buffer
overflow for a particularly complex program. Therefore, this approach was modified
by refining the original overall test goal “reach POS while fulfilling the guidance
condition” via two different sub-goals addressing control flow and predicate
fulfilment separately. To do so, the original program was first transformed into a new
program by insertion of an additional branch, as shown in Fig. 2. As both outgoing
arcs of the new branching node lead to identical copies of the POS instruction, both
programs are behaviourally equivalent.
By this semantics-preserving program transformation the original optimization
problem is transformed into a classical test coverage problem demanding to cover all
nodes, in particular the POS-node highlighted by a thick border on the right of Fig. 2.
Fig. 2. Original program slice (left); transformed program slice (right).
According to previous work carried out in local optimization [16, 24], the following
distance metric can be used to reflect the quality of test progress:
distance = sub-path distance + branch distance (3)
where
the sub-path distance captures the number of branching nodes separating the
closest node already reached from the target node annotated by C(NBO); by its
nature, this metric is a natural number;
the branch distance captures the degree of violation of the branching condition
C(NBO) in terms of the distance metric for the predicate ¬C(NBO); in order to
enforce the dominance of the sub-path distance over the branch distance, this
metric can be normalized to enforce a range between 0 and 1.
Start
End
C(NBO)
POS-
instruction
false
true POS-
instruction
Start
End
POS-
instruction Transformation
With respect to the examples analyzed, this locally optimized refinement revealed to
be superior both in performance and in detection capability, as reported in the next
section.
5 Examples
Both testing approaches proposed in section 4 were evaluated by means of different
examples. The applicability of ICA is first demonstrated via Example 1 (s. Appendix,
Fig. 3) which contains two global variables, namely an integer array buffer for 100
integers and an integer variable nE indicating the number of elements already inserted
in the buffer; function is the program under test, invoked by the integer array values
and its size valuesNumber. All instructions of function lie within a for loop such that
any of them might influence any of the buffer variables. Therefore, slicing the
program does not help reducing the original program.
Five alternative Potential Overflow Statements were identified; only two of them
require further testing. In the following, due to its nesting depth and complexity, the
analysis presented focuses on the POS located at code line 10 for the purpose of
demonstrating the application of ICA to testing. The corresponding instruction reads:
buffer[i+1] = buffer[i]. Evidently, regular buffer access at this code position requires
to fulfil the inequalities:
0 i+1 < buffer.aSize (4)
Conversely, a buffer overflow would occur if and only if i+1 buffer.aSize;
therefore, ¬C(NBO) reads:
99 i (5)
On the other hand, a buffer underflow would assume i+1 < 0 which is evidently
untrue. For this reason, in the following only the option of a potential buffer overflow
will be further investigated taking into account that upon reaching POS variable i is
constrained to assume a value over 98. Based on this insight, the control and data flow
of the program under test, i.e. function is analysed for further variable values on
which i depends: among them is evidently nE which provides the initial value of i
before i is successively decremented within the internal loop (s. LOC #8); in
particular this implies:
i nE (6)
On the other hand, nE is subject to changes within the external loop (s. LOC #4);
more precisely, during each iteration of the external loop nE may remain unchanged
or increase by a maximum of 1 (s. LOC #13 and LOC #23); in particular, upon
reaching POS in the course of the (v+1)-th iteration, nE can have incremented at most
by v, i.e.
nE v (7)
On the whole, the guidance provided by the overall constraint C(POS) ¬C(NBO)
reads:
99 i nE v (8)
As the inequality chain i nE v always holds by semantic analysis for the
aforementioned reasons, the guiding constraint A was captured by the conjunction of
three atomic conditions with distance metric:
)j,t(min)t( A
mj1A
(9)
where
)j,t()j,t()j,t()j,t( i99nE99v99A (10)
and j denotes the case where execution of t by function has just reached POS for the j-
th time. Each test cases t = (values, valuesNumber) consists of an integer array values
and an integer valuesNumber providing its size which in this case is 300 in view of
the call in main.
In order to evaluate the fitness for any test case t, function is instrumented to return
the minimum distance value (t) of t upon reaching POS. For example, when
executing a test case with values = {150, 151, 149, 152, 148, 153, 147, 154, 146} and
valuesNumber = 9, POS is accessed 3 times. Table 3 shows the variable values and
the distance metric each time POS is reached. Evidently, the minimum distance for
this test case is 283.
Table 3. Variable values and distances upon reaching POS
i-th access
of POS
instruction
Variable values
v nE i
distance
1st 6 2 2 287
2nd 8 3 3 283
3rd 8 3 2 284
Similar procedures can be applied to the other four POS alternatives. On the whole,
only those located at lines 10 and 20 require further testing (s. Table 4).
Both global and local algorithms were subsequently applied 10 times each with
different initial random populations of size 70, where distance metrics were evaluated
with a penalty constant K = 1; the minimum distance over all individuals within a
population was taken as the fitness value of that population.
All 20 test optimizations were successful in detecting an exploitable buffer overflow.
Performance was evaluated by comparing the number of iterations required to trigger
the vulnerability; this allows to exclude the influence of the underlying computer
hardware as would be the case if running time was measured instead. Local
optimization hereby performed considerably better, the slowest local optimization
requiring only ca. 53% of the effort needed by the fastest global one.
Table 4. Results of Integer Constraint Analysis for POS alternatives of Example 1
POS = <instr(k), {buffer}>
C(NBO)
testing required? k instr(k)
10 buffer[i+1] = buffer[i] i+1 < buffer.aSize Yes
15 buffer[1] = value 1 < buffer.aSize No
16 buffer[0] = 0 0 < buffer.aSize No
19 buffer[0] = 1 0 < buffer.aSize No
20 buffer[nE] = value nE < buffer.aSize Yes
The superiority of local optimization was revealed also in the context of a further,
more complex example involving higher execution times and including non-
monotonic jumps in buffer accesses. For this Example 2, all global approach variants
failed to exploit the existing buffer overflow within the affordable time, while the
vulnerability was easily detected by all locally optimizing instances, the slowest one
demanding only an effort of ca. 12% of the maximum affordable.
A comparison of the experimental results obtained is shown in Table 5.
Table 5. Comparison of global and local optimization techniques for both examples
Examples
Global Optimization
(10 executions)
Local Optimization
(10 executions)
fastest slowest fastest slowest
Example 1 55869 196389 3846 29781
Example 2 no buffer overflow detection after
10000 iterations
199 1197
6 Conclusions and Future Work
This article presented two testing approaches targeted at the triggering of potential
buffer overflows resp. underflows before deployment. Both approaches combine
existing static analyses based on Integer Constraints with heuristics guided by the
constraints derived.
The applicability was illustrated by an example; a comparative evaluation of this and
of a further example showed that the local optimization techniques were superior both
in detection capability and in performance.
In view of the promising results it is planned to apply similar guided test concepts to
further classes of software vulnerability.
Acknowledgement. The authors gratefully acknowledge that a major part of the work
presented was supported by the German Federal Ministry for Economic Affairs and
Energy (BMWi), project no. 1501502C (SMARTEST). They also thank Marc
Spisländer for his support in providing the code examples considered in this article.
Appendix
Fig. 3. Code of Example 1
1: int buffer[100];
int nE = 0;
3: void function(int *values, int valuesNumber) {
for (int v = 0; v < valuesNumber; v++) {
5: int value = values[v];
if (value < buffer[1]) {
7: if (buffer[0] == 1) {
for (int i = nE; i > 1; i--) {
9: if (buffer[i+1] > buffer[i]){
buffer[i+1] = buffer[i];
11: }
}
13: nE++;
}
15: buffer[1] = value;
buffer[0] = 0;
17: } else {
if (buffer[nE] < value)
19: buffer[0] = 1;
buffer[nE] = value;
21: } if (nE == 0)
23: nE++;
}
25: } int main(void) {
27: for (int i = 0; i < 100; i++)
buffer[i] = (i+1)∗1000;
29: int values[300];
...
31: function(values, 300); return EXIT_SUCCESS; 33: }
References
1. Al Sardy, L., Tang, T., Spisländer, M., Saglietti, F.: Analysis of Potential Code Vulnerabilities Involving Overlapping Instructions. In: Computer Safety, Reliability, and Security (SAFECOMP Workshops). LNCS, vol. 10489, pp. 103-113. Springer, (2017). doi: 10.1007/978-3-319-66284-8_10
2. Andriesse, D., Bos, H.: Instruction-Level Steganography for Covert Trigger-Based Malware. In: Detection of Intrusions and Malware, and Vulnerability Assessment (DIMVA 2014). LNCS, vol. 8550, pp. 41-50. Springer (2014). doi:
10.1007/978-3-319-08509-8_3
3. Chess, B., McGraw, G.: Static Analysis for Security. In: IEEE Security & Privacy, vol. 2, pp. 76–79. IEEE (2004). doi: 10.1109/MSP.2004.111
4. Del Grosso, C., Antoniol, G., Merlo, E., Galinier, P.: Detecting Buffer Overflow via Automatic Test Input Data Generation. In: Computers & Operations Research, vol. 35, pp. 3125–3143. Elsevier (2008)
5. Department of Homeland Security (U.S.): Annual Vulnerability Coordination Report. National Cybersecurity and Communications Integration Center / Industrial Control Systems Cyber Emergency Response Team (2016)
6. Dor, N., Rodeh, M., Sagiv, M.: Cleanness Checking of String Manipulations in C Programs via Integer Analysis. In: Static Analysis (SAS). LNCS, vol. 2126, pp. 194–212. Springer (2001). doi: 10.1007/3-540-47764-0_12
7. Dor, N., Rodeh, M., Sagiv, M.: CSSV: Towards a Realistic Tool for Statically Detecting all Buffer Overflows. In: Programming Language Design and Implementation (PLDI), vol. 38, pp. 155-167. ACM (2003). doi: 10.1145/780822.781149
8. Evans, D., Larochelle, D.: Improving Security Using Extensible Lightweight Static Analysis. In: IEEE Software, vol. 19, pp. 42–51, (2002). doi: 10.1109/52.976940
9. Foster, J.C., Osipov, V., Bhalla, N., Heinen, N.: Buffer Overflow Attacks: Detect, Exploit, Prevent. Syngress, Rockland (2005)
10. Haugh, E., Bishop, M.: Testing C Programs for Buffer Overflow Vulnerabilities. In: Network and Distributed System Security Symposium (2003)
11. International Organization for Standardization (ISO): Programming Languages ─ C, International Standard ISO / IEC 9899:TC3 (E). ISO (2007). http://www.open-std.org
12. Jämthagen, C., Lantz, P., Hell, M.: Exploiting Trust in Deterministic Builds. In: Computer Safety, Reliability, and Security (SAFECOMP 2016). LNCS, vol. 9922, pp. 238–249. Springer (2016). doi:10.1007/978-3-319-45477-1_19
13. Larochelle, D., Evans D.: Statically Detecting Likely Buffer Overflow Vulnerabilities. In: 10th conference on USENIX Security Symposium, vol. 10, pp. 177-190. ACM (2001)
14. Le. W., Soffa, M.L.: Marple: a Demand-driven Path-sensitive Buffer Overflow Detector. In: 16th ACM SIGSOFT Int. Sym. on Foundations of Software Engineering. ACM (2008). doi: 10.1145/1453101.1453137
15. Lhee, K., Chapin, S.: Buffer Overflow and Format String Overflow Vulnerabilities. Journal of Software: Practice and Experience, vol. 33, pp. 423-460 Wiley (2003). doi: 10.1002/spe.515
16. Oster N., Saglietti, F.: Automatic Test Data Generation by Multi-objective Optimisation. In: Computer Safety, Reliability, and Security (SAFECOMP). LNCS, vol. 4166, pp.426-
438. Springer (2006). doi: 10.1007/11875567_32
17. Padmanabhuni, B.M., Tan, H.B.K.: Auditing Buffer Overflow Vulnerabilities using Hybrid Static–Dynamic Analysis. In: 38th Annual Int. Computers, Software and Applications Conf., vol. 10, pp. 54–61, 2014. doi: 10.1109/COMPSAC.2014.62
18. Saglietti, F., Meitner, M., v. Wardenburg, L., Richthammer, V.: Analysis of Informed
Attacks and Appropriate Countermeasures for Cyber-Physical Systems. In: SAFECOMP
Workshops 2016. LNCS, vol. 9923, pp. 222-233 Springer (2016). doi: 10.1007/978-3-
319-45480-1_18
19. Schneider Electric Software Security Response Center: InduSoft Web Studio and InTouch
Machine Edition – Remote Code Execution Vulnerability, Security Bulletin
LFSEC00000125 (2018)
20. Shahriar, H., Zulkernine, M.: Classification of Static Analysis-Based Buffer Overflow
Detectors. In: 4th International Conference on Secure Software Integration and Reliability
Improvement Companion (SSIRI-C). IEEE (2010). doi: 10.1109/SSIRI-C.2010.28
21. Shahriar, H., Zulkernine, M.: Mutation-based Testing of Buffer Overflow Vulnerabilities.
In: Computer Software and Applications (COMPSAC’08), pp. 979–984. IEEE (2008)
22. Tracey, N., Clark, J., Mander, K., McDermid, J.: An Automated Framework for Structural
Test-data Generation. In: 13th IEEE Int. Conf. on Automated Software Engineering, pp.
285–288. IEEE (1998). doi: 10.1109/ASE.1998.732680
23. Wagner, D., Foster, J.S., Brewer, E.A., Aiken, A.: A First Step Towards Automated
Detection of Buffer Overrun Vulnerabilities. In: Network and Distributed System Security
Symposium (NDSS), pp. 3–17. (2000)
24. Wegener, J., Baresel, A., Sthamer, H.: Evolutionary Test Environment for Automatic
Structural Testing. In: Information and Software Technology, vol. 43, pp. 841-854.
Elsevier (2001). doi: 10.1016/S0950-5849(01)00190-2
25. Weiser, M.: Program Slicing. In: 5th International Conference on Software Engineering.
pp. 439–449, IEEE Press (1981)