$μ$VulDeePecker: A Deep Learning-Based System for Multiclass Vulnerability Detection
Deqing Zou, Sujuan Wang, Shouhuai Xu, Zhen Li, Hai Jin
Introduction
Most cyber attacks are caused by the exploitation of one or multiple vulnerabilities . It is unfortunate that vulnerabilities are inevitable, for many reasons (e.g., software complexity, steady growth in internet complexity). Given that vulnerabilities cannot be prevented, an alternate defense approach is to detect and patch vulnerabilities sooner rather than later, leading to the field of vulnerability detection. This problem has received a due amount of attention, leading to many approaches. A popular approach is to use manually-defined patterns to detect vulnerabilities , which attained a limited success. Another promising approach is to use machine learning for vulnerability detection (see, for example, ). These solutions reduce the workload on human experts because they only need to roughly define features for learning machine learning-based models that can detect vulnerabilities, rather than defining vulnerability patterns manually. Compared with traditional machine learning techniques, researchers have started using deep learning for detecting vulnerabilities and software defects .
The state-of-the-art of deep learning-based vulnerability detection is a system called VulDeePecker , which uses Bidirectional Long-Short Time Memory (BLSTM) neural network to detect software vulnerabilities. However, VulDeePecker is a binary classifier or detector, meaning that it can tell whether a piece of code (i.e., multiple lines of code) is vulnerable or not, but cannot pinpoint the type of the vulnerability in question. The type of a vulnerability is important because this information will tell the vulnerability principles and help quickly pin down the precise location of a vulnerability and reduce the workload of developers and code auditors, which is especially important when the piece of code has a substantial number of lines (e.g., tens number of lines of code), which is also common to machine learning-based vulnerability detection systems . In this paper, we move a step towards ultimately tackling this problem by investigating multiclass vulnerability detection, which not only can tell whether a piece of code is vulnerable or not, but also can pinpoint the type of a vulnerability.
We initiate the investigation on multiclass vulnerability detection and present a system that uses deep learning for this purpose. The system is called multiclass Vulnerability Deep Pecker, or VulDeePecker for short.
The innovations of the system are in three-fold. First, a conceptual innovation underlying VulDeePecker is the introduction of the concept we call code attention, which can accommodate information useful for learning local features and helping pinpoint types of vulnerabilities. It refines the concept of code gadget which is a number of statements that are semantically related to each other. Second, another innovation underlying VulDeePecker is redefining the concept and extraction method of code gadget by introducing control-dependence. The last innovation underlying VulDeePecker is a new neural network architecture. The architecture is different from the models used in previous vulnerability detection methods. It is mainly constructed from building-block BLSTM networks and aims to fuse different kinds of features from code gadget and code attention to accommodate different kinds of information. This neural network architecture may be of independent value because it provides an effective fused idea for different code features and can be referenced in other scenarios.
In order to evaluate the effectiveness of VulDeePecker, we create a dataset that contains 181,641 pieces of code (called code gadgets, which are units for vulnerability detection) from 33,409 programs. Among them, 138,522 are non-vulnerable (i.e., not known to contain vulnerabilities) and the other 43,119 are vulnerable and contain 40 types of vulnerabilities in total. This dataset should be useful to other researchers and is available at https://github.com/muVulDeePecker/muVulDeePecker. Systematic experiments using this dataset show the following: (i) VulDeePecker is effective for multiclass vulnerability detection. In particular, the use of code attention can indeed help recognize types of vulnerabilities, even if for small samples. It is significant because deep learning often requires large amounts of data in order to be effective. (ii) The accommodation of control-dependence can indeed improve the capability of VulDeePecker in multiclass vulnerability detection. (iii) It is possible to further improve the effectiveness of VulDeePecker by using it together with other deep learning based detection systems, which may be able to accommodate different kinds of information useful for multiclass vulnerability detection.
2 μ𝜇\muVulDeePecker vs. VulDeePecker
We name our multiclass vulnerability detection system, namely VulDeePecker, after the system known as VulDeePecker , which is the first deep learning-based binary vulnerability detection system (i.e., only able to tell whether a piece of code is vulnerable or not, but not the type of a vulnerability in question). The reason we are so named is that our system is inspired by VulDeePecker; indeed, we refine the concept of code gadget introduced in , which only captures data dependence, by additionally accommodating control dependence, which leads to higher effectiveness in multiclass vulnerability detection. However, we stress that VulDeePecker is not a simple incremental work over VulDeePecker .
The reasons are as follows. First, our experiments show that it is not effective to extend VulDeePecker for multiclass vulnerability detection. For showing this, we consider two variants of VulDeePecker. One variant, called VulDeePecker+, directly modifies the neural network architecture of VulDeePecker. The other variant is to train a VulDeePecker-based classifier for each type of vulnerabilities and each trained model is applied every sample for vulnerability detection. The latter variant has significant weaknesses (e.g., not scalable because there are many types of vulnerabilities; not effective when the samples of one vulnerability type are small), but is investigated for comparison purposes. Second, we use new methods to prepare a dataset from scratch to evaluate the effectiveness of VulDeePecker since the dataset published by the authors of VulDeePecker is not sufficient for our purposes. This is because (i) their dataset does not contain information on vulnerability types, (ii) their dataset only accommodates data-dependence but not control-dependence, and (iii) their dataset loses some statements in the code gadgets, while these statements may contribute to pinpoint vulnerability types (as elaborated later). Third, a novel concept of code attention and its extraction method are proposed in VulDeePecker to help pinpoint vulnerability types. Fourth, the neural network architecture underlying VulDeePecker is more involved than the use of a standard BLSTM in VulDeePecker. The innovation in VulDeePecker architecture lies in the fusion of different kinds of features; to the best of our knowledge, we are the first to use the fusion idea in the context of vulnerability detection.
3 Paper Organization
Section 2 discusses the basic ideas underlying VulDee-Pecker and the terminology used in the paper. Section 3 explains the detailed design of the system. Section 4 introduces the implementation of the system. Section 5 is the systematic experiment and analysis of the results. Section 6 is the related work. Section 7 discusses the limitation and makes an explanation of the future work. Section 8 concludes the paper.
Basic Ideas and Terminology
In this section we describe the key idea underlying VulDeePecker and rigorously define the terminology used in the present paper.
In order to detect the specific types of vulnerabilities, we propose using the concept of code attention, which is inspired by the notion of region attention in the image processing . The notion of region attention in image processing was introduced to capture the insight that some regions in an image provide more discriminative information for accurate classification of the image. For example, when human eyes observe an image containing a bird, the bird’s eyes, mouth, and coat color provide more information than other regions for identifying the type of the bird. Similarly, code attention is composed of multiple program statements in a piece of code and would provide more information for classifying the type of a vulnerability. For example, when a piece of code is detected as a vulnerability caused by illegal library/API function calls, argument definition statements in library/API function calls, control statements, and statements containing library/API function calls may provide more information for classifying the types of vulnerabilities.
The concept of code attention refines the concept of code gadget, which is the key idea underlying the first deep learning-based vulnerability detection system known as VulDeePecker . A code gadget, as defined in , is a number of (not necessarily consecutive) statements that accommodate the data-dependence relation. A code gadget captures some degree of syntax and semantic information of vulnerabilities, explaining why it helps detect whether a piece of code is vulnerable or not. As we will discuss later, we further use a refined version of the notion of code gadget, which captures not only the data-dependence relation but also the control-dependence relation.
We propose using both the aforementioned refined notion of code gadget and the aforementioned code attention for training the multiclass vulnerability detection model. Intuitively, the refined notion of code gadget captures more “global” semantics information, which is conveyed by the control-dependence and data-dependence relations between the statements in a program, and thus would help achieve a higher capability in detecting whether a piece of code is vulnerable or not. The notion of code attention captures more “localized” information within a statement (e.g., arguments in a specific library/API function call) and would help recognize the specific type of a vulnerability. More specifically, code attention enforces data source checking, critical path checking (condition statements), and dangerous function usage checking; these factors are closely related to the types of vulnerabilities. In order to attain a full-fledged solution, we propose fusing these two kinds of information into a more comprehensive feature representation.
2 Definitions
In order to make our description precise, we use the following definitions.
A program is an ordered set of statements, denoted by , where () is a statement. A statement is an ordered set of tokens, denoted by , where token () can be a variable identifier, function identifier, constant, keyword or operator and so on.
Given a program , a statement , a variable identifier token belonging to , statement () is said to be data-dependent on token if is used in .
Consider two statements () in a program . If the execution outcome of affects whether will be executed or not, is said to be control-dependent on .
Consider a program , a library/API function call denoted by , and a statement () containing the function call . Assume that contains a total of function calls, then . Denote the arguments of the function call by a set . A code gadget corresponding to function call in statement , denoted by , is an ordered set of statements in , each of which is either recursively data-dependent on one or multiple arguments in or control-dependent on in a recursive manner.
Consider a program , a library/API function call , a statement () containing the function call , and a code gadget corresponding to in . Let be a set of rules describing vulnerability syntax characteristics, which are vulnerability-specific and thus elaborated later. A code attention with respect to code gadget , denoted by , is a subset of the statements in code gadget that match the syntax characteristics specified by some .
Design of μ𝜇\muVulDeePecker
Our objective is to design a multiclass vulnerability detection system. Let denote a set of vulnerability types, where type-0 means “not vulnerable” and type- () corresponds to a Common Weakness Enumeration IDentifier or CWE-ID (which is the outcome of a community effort at categorizing vulnerabilities ). As the first study on multiclass vulnerability detection, we focus on vulnerabilities related to library/API function calls in C/C++ programs, while leaving the extension to accommodating other vulnerabilities to future work.
We design VulDeePecker in three modules: (i) parser, which parses programs into code gadgets and code attentions; (ii) vector representation extractor, which generates vector representation of code gadgets and code attentions; (iii) detector, which learns a vulnerability detection model and uses this model for multiclass vulnerability detection. As highlighted in Fig 1 and elaborated below, both the training phase and the detection phase contain these three modules, except that Step II (i.e., ground-truth labeling in the training phase) is not relevant to the detection phase because labels are pursued as the output of the detection phase and that Step VI is about training a detector and Step VI’ is about using the trained detector.
As highlighted in Fig 1, the parser is divided into Steps I-IV, the vector representation extractor corresponds to Step V, and the detector corresponds to Step VI. These steps are elaborated below.
Step I: this step generates code gadgets from training programs. Since this step is quite involved, we use the example shown in Fig 2 to further illustrate ideas. It can be divided into 3 sub-steps.
Step I.1: this step generates a System Dependency Graph (SDGs) for each training program. A SDG is derived from a set of Program Dependence Graphs (PDGs) , which represent data-dependence and control-dependence relations. A PDG is a directed graph, wherein a node (or vertex) represents a statement or control predicate and an arc (or directed edge) represents a data- or control-dependence relation between two nodes. There are standard algorithms for generating PDGs for functions (see, for example, ). A SDG can be derived from PDGs via the caller-callee relation. Specific to the purpose of the present paper, we propose associate to each node with attributes, including the statement text corresponding to the node and the type of the statement (e.g. “expr”, “def”, “assign”). The second column in Fig 2 shows the SDG derived from the PDGs of functions and in the example program, where each node is only highlighted with the corresponding statement (represented by the Line number) for succinctness.
Step I.2: this step identifies library/API function calls in programs of interest. It extracts library/API function calls by matching statements associated to nodes to known library/API function calls. For example, the third column in Fig 2 contains the library/API function call in Line 8 of the example program.
Step I.3: this step extracts code gadgets using a method that is based on but is different from the method used by VulDeePecker . We note that VulDeePecker (i) only considers data-dependence, (ii) divides functions into forward functions (which receive external inputs) and backward functions (which do not receive external inputs), and (iii) extracts forward (backward) slices from forward (backward) function calls. The forward slice is obtained by slicing the successors of the forward function call. Conversely, the backward slice is obtained by slicing the precursors of the backward function call. In contrast, we accommodate both data-dependence and control-dependence when extracting code gadgets in a bi-direction manner (i.e., considering both forward and backward slices). Our approach to extracting code gadgets is advantageous to the approach used by VulDeePecker because (i) our approach accommodates both data-dependence and control-dependence in code gadgets, rather than data-dependence only, and (ii) our approach considers all of the forward and backward slices rather than extracting forward or backward slices by selecting some forward or backward functions, which is the case in VulDeePecker .
In order to see the preceding (ii), we consider the library functions and as examples, which represent the forward function of the input and the backward function of the non-input. The function would be defined as a forward function and therefore lead to a forward slice in VulDeePecker. However, the definition statements corresponding to the arguments of can only be contained in the backward slice of , which leads to these statements miss. These statements missed by VulDeePecker would be important because they provide the information about the types of these arguments and their memory sizes, which can be leveraged as a reference when examining whether these arguments have improper operations or not. For another example of library function , which is defined as a backward function in , resulting in generating a backward slice. It also causes that the operation statements of the memory allocated by miss because these statements only can be sliced in the forward slice of . But these operations are important as they are the basis for judging whether memory leak or buffer overflow has occurred. Therefore, we believe that only considering forward slice or backward slice will cause information loss, which may be useful in vulnerability detection. That is why we consider both forward and backward slices in the present paper.
Algorithm 1 elaborates Step I.3. Consider a library/API function call made in a statement , which is represented as a node in the SDG in question. For each argument of , Algorithm 1 does the following: (i) using function (Lines 1-10 in Algorithm 1) to identify successors of node in the SDG, namely the nodes that are data-dependent upon or control-dependent upon (either directly or indirectly), and then generates a forward slice ; (ii) using function (Lines 11-20 in Algorithm 1) to identify predecessors of node recursively, upon which is data-dependent or is control-dependent (either directly or indirectly), and then generates a backward slice . Finally, Algorithm 1 merges slices and (Line 29 in Algorithm 1) to obtain a code gadget corresponding to function call . The last column in Fig 2 shows the example of extracting the code gadget according to arguments , and of function call .
Step II: this step labels each code gadget as “0” (not vulnerable) or (type- vulnerability), where . These labels are the ground truth for training a multiclass vulnerability detection model.
Step III: we observe that different programmers have different coding habits (e.g., format, variables, and function naming), which may affect the capability of multiclass vulnerability detection models. This motivates us to normalize code gadgets by (i) mapping same variables and functions to same values and (ii) renaming different variables and functions according the order of their appearance (i.e., “varb_0”, “varb_1”, ; “func_0”, “func_1”, ). However, reserved words, library/API function names, and constant are not mapped because they are standard. Fig 3 gives an example of code gadget normalization.
Step IV: this step parses the normalized code gadgets to generate code attentions according to vulnerability syntax characteristics, which would contribute to the identification of vulnerability types. For the vulnerabilities caused by improper library/API function calls, we observe that the arguments and usage of the library/API function calls affect the occurrence of vulnerabilities. Taking into consideration data source checking, data sanitized operation checking, and library/API function calls usage plausibility checking, we propose utilizing the following three syntax characteristics: (i) definition statements of arguments in library/API function calls, which would provide information useful to identify improper use of library/API function calls (e.g., type and memory size of an argument) and distinguish whether the vulnerability caused by data source; (ii) control statements, which would help determine whether a program has conducted proper bounds-checking and security screening before executing the target library/API function calls; (iii) statements containing library/API function calls, which would directly help recognize vulnerability types. Corresponding to these vulnerability syntax characteristics, we define rules as follows:
Rule : if the attribute of a statement in code gadget is a definition statement and the variables defined in match the arguments in the library/API function call, then is a statement in code attention . For example, statements 2, 4 and 5 in Fig 2 are definition statements of arguments , and , which are used in function call .
Rule : if the attribute of statement in code gadget is a control statement, then is a statement in code attention . For example, statements 7 and 16 in Fig 2 are such statements.
Rule : if the attribute of statement in code gadget contains library/API function calls, then is a statement in code attention . For example, statements 6, 8, and 17 in Fig 2 are such statements.
Using the aforementioned vulnerability syntax characteristics, Algorithm 2 extracts a code attention from a normalized code gadget. The algorithm can be understood as follows. For each statement in , Algorithm 2 parses statement into an ordered set of tokens by a lexical analysis, infers attribute of statement (e.g., “def”, “cond”) by analyzing the context structure of these tokens, and uses each token and to match the rules in a given rule set to generate code attentions. Currently, we only use a rule set of the three rules mentioned above.
Step V: this step converts normalized code gadgets and code attentions respectively to fixed-length code gadget vectors and code attention vectors. The conversion is attained by a simple word embedding . These vectors are the input for training neural networks.
Step VI: inspired by the multi-feature fusion method in image and video recognition , we propose a new neural network architecture that uses the BLSTM as a building-block because BLSTM is shown to be capable of detecting vulnerabilities . The network, as highlighted in Fig 4, deals with two kinds of features: global features, which are denoted by ’s in Fig 4, are learned from code gadgets, and accommodate some broader semantics about the relations between statements in a program; local features, which are denoted by ’s in Fig 4, are learned from code attentions, and are specific to individual statements in a program (e.g., arguments in a specific library/API function call). Since these two kinds of features accommodate different kinds of information, we use a feature fusion to learn comprehensive features. As a result, the network consists of the following three BLSTM networks: global-feature learning model, local-feature learning model, and feature-fusion model. For learning global features from code gadgets, the model uses a pre-processing layer and deep BLSTM layers, where the preprocessing layer mainly filters the vectors and the deep BLSTM layers actually learn global features. Each neuron is equivalent to a time step and receives a word in a code gadget. For learning local features from code attentions, the model uses the same network (for learning global features) but at a smaller scale (because vectors representing code attentions are shorter than vectors representing code gadgets). For fusing global and local features, the model uses a merge layer, a BLSTM layer and a softmax classifier, where the merge layer connects the learned global and local features and the BLSTM layer adjusts the spliced features. Finally, the fused features are loaded into the classifier.
Summarizing the preceding discussion, we first train a global-feature learning model as well as a local-feature learning model and then train the feature-fusion model. The resulting system is used for multiclass vulnerability detection. Details of the training strategy are elaborated in Section 4.2 with respect to a specific training phase.
2 Detection Phase
As highlighted in Fig 1, this phase contains the following steps.
Step I: this step generates code gadgets from target programs for vulnerability detection (and is similar to Step I in the training phase).
Step III: this step normalizes code gadgets (and is similar to Step III in the training phase).
Step IV: this step extracts code attentions from code gadgets (similar to Step IV in the training phase).
Step V: this step transforms the normalized code gadgets and code attentions to vector representations (and is similar to step V in the training phase).
Step V’: this step classifies vector representations using the learned VulDeePecker model, outputting the vulnerability types of code gadgets (recalling that type-0 means the code gadgets is not vulnerable).
3 Generalization Analysis
VulDeePecker can only detect vulnerabilities caused by C/C++ library/API function calls currently, but the techniques used are extensible (e.g., extracting code gadgets and code attentions). VulDeePecker can be adopted/adapted to accommodate other programming languages and other types of vulnerabilities, but the generalization should adhere to the following premise:
The software source code to be generalized must be able to be parsed into SDG structures.
The type of vulnerability to be generalized must have an interesting entity. For example, the entity of interest in this paper is C/C++ library/API function calls.
The type of vulnerability to be generalized must exhibit the following 3 perspectives: data source checking, data sanitized operation checking, and interested entity usage plausibility checking.
If the target language and target vulnerability type to be generalized satisfy the above premises, VulDeePecker can be extended, which is also the future work of us.
Implementation and Evaluation Metrics
In this section, we describe the preparation of the dataset for training and testing and the implementation of VulDeePecker.
Our sources of vulnerabilities are Software Assurance Reference Dataset (SARD) and National Vulnerability Database (NVD) .
SARD provides a large set of production, synthetic, and academic programs (a.k.a. test cases) with known vulnerabilities. A program is labeled as “good” (i.e., not vulnerable), “bad”(i.e., vulnerable), or “mixed” (i.e., both vulnerable and patched versions are available). For a vulnerable program, SARD provides the statements containing the vulnerability and the vulnerability type in CWE-ID (Common Weakness Enumeration IDentifier) . NVD contains a large number of vulnerabilities in production software and provides the software products affected, the vulnerable versions, the CWE-IDs, and the patch files. In total, we collect 33,409 programs written in C/C++ corresponding to 116 CWE-IDs, including (i) 57 “good” programs, 925 “bad” programs, and 3,2104 “mixed” programs collected from SARD and (ii) 323 vulnerable programs collected from NVD. Since CWE-IDs are hierarchic (e.g., CWE-121 contains CWE-787 and CWE-788 as sub-types), we aggregate CWE-IDs to the third level of the CWE-ID tree (i.e., the research concept view) and use these third-level CWE-IDs as vulnerability types. For example, CWE-121 is a sub-type of CWE-119, which is at the third level of the CWE-ID tree, and as such we use CWE-119 as the vulnerability type for a CWE-121 vulnerability as well. This leads to 40 different vulnerability types (i.e., plus the non-vulnerability type), which are listed in Table I. We observe that most vulnerabilities do not contain vulnerability sub-types, but some of them do (e.g., Type-7 or CWE-415 is a sub-type contained in three types CWE-119, CWE-666, and CWE-573); this is caused by the CWE-ID tree structure mentioned above. We randomly respectively select 80% SARD programs and 80% NVD programs as the training set and the rest as the testing set for experiments.
2 Training Phase
This phase is divided into the following six Steps.
Step I: for generating code gadgets from training programs, we use the open source C/C++ code analysis tool Joern to construct PDGs for functions in each training program. Then, we generate an SDG according to the caller-callee relation between these functions, and traverse each node in the SDG to identify the nodes that contain any of the 811 C/C++ library/API function calls related to security which are publicly available in the rules of Checkmarx . Finally, we extract the arguments of each function call and use Algorithm 1 to generate code gadgets.
Step II: the vulnerable statements in a vulnerable program collected from SARD are readily available from the dataset. The vulnerable statements in a vulnerable program collected from NVD are the statements that are deleted in the patch files. If a code gadget contains one or more vulnerable statements, it is labeled as vulnerable with vulnerability type- where ; otherwise, it is labeled as type- (i.e., non-vulnerable).
Step III and Step IV: for each code gadget, we write an automate lexical-analysis-based program to analyze each token (e.g., variables, operators, keywords) in statements. We mainly analyze the types and the context structures of them for identifying the variable and function names and rename them. Finally, we use Algorithm 2 on the normalized code gadgets to extract code attentions.
Step V: for normalized code gadgets, we use a lexical analysis to generate a corpus of program tokens. Then, we use the word-to-vector tool to generate the vectors for representing those tokens. The word-to-vector model we use is skip-gram , with window size 10 and vector dimension 50. Since the vector input to the neural network is fixed-length, we use and respectively for the length of code gadgets and the length of code attentions, while noting that these hyperparameters are tuned in the training phase. For code gadgets (code attentions) containing fewer words or shorter vectors than (), we pad 0 vectors at their tail to make their vectors of length (); for code gadgets (code attentions) containing more words or longer vectors than (), we cut their vectors at the tail of code gadgets (code attentions) to match length ().
Step VI: recall that we use the BLSTM as a building-block for constructing VulDeePecker neural network architecture. Our implementation of the model uses Keras . We first train global-feature and local-feature learning models by tuning parameters — “optimizer”, “learning rate”, “batch size”, “dropout”, “layers”, “the number of nodes in a hidden layer”, and “epochs” — to achieve the optimal results via the method called grid search, which searches the optimal values of these hyperparameters by performing an exhaustive search. Next, we search for the best fusion method via the permutation and combination of global-feature and local-feature models, while fixing the hyperparameters. Finally, we tune the hyperparameters of the feature-fusion model and obtain the VulDeePecker neural network. We train the three networks separately so as to prevent the features learned from one network from being destroyed by the others. The specific hyperparameter values of each model obtained in training phase are summarized in Table II.
3 Detection Phase
In the detection phase, we perform 5 steps for target programs, of which the first 4 steps are the same as Step I, III, IV, V in the training phase. In Step VI’, we input the code gadget vectors and their corresponding code attention vectors to be tested into the trained VulDeePecker model. The model outputs whether the code gadgets contain vulnerabilities and if so, outputs their classes.
Totally, we extract 181,641 code gadgets (145,353 in the training set and 36,288 in the testing set) whose average/median number of lines-of-code is 140/120, among which 175,415 are from SARD (including 42795 vulnerable and 132620 non-vulnerable) and 6,226 from NVD (including 324 vulnerable and 5902 non-vulnerable). To sum up, there are 138,522 non-vulnerable code gadgets and 43,119 vulnerable code gadgets covering 40 vulnerability types which are listed in Table I. This labeled dataset, called Multiclass Vulnerability Dataset (MVD), is available at https://github.com/muVulDeePecker/muVulDeePecker.
4 Evaluation Metrics
For evaluating multiclass vulnerability detectors, we use the following metrics (see, for example, ). Let be a set of vulnerability types (in this paper, ), Let , , , and respectively be the number of true positive samples, false positive samples, false negative samples, true negative samples, and the total number of samples with respect to vulnerability type , where .
The metrics for evaluating multiclass vulnerability detectors are (the multiclass counterpart of the false positive rate based on the total number of vulnerability classes), (the multiclass counterpart of the false negative rate based on the total number of vulnerability classes), (the multiclass counterpart of the F1 measure based on the total number of vulnerability classes), (the multiclass counterpart of the false positive rate based on the weight of each vulnerability class), (the multiclass counterpart of the false negative rate based on the weight of each vulnerability class), (the multiclass counterpart of F1 measure based on the weight of each vulnerability class). The metrics are arithmetic average of the metrics of the overall vulnerability classes. The metrics are the average in which metrics of each vulnerability class are multiplied by the weight before summing. The weight here refers to the proportion of the number of each vulnerability class in the total number of vulnerabilities. Specifically, we have:
Note that when , naturally degenerates to the false positive rate () of binary vulnerability detector, naturally degenerates to the false negative rate () of binary vulnerability detector, and naturally degenerates to the measure of binary vulnerability detector. However, , , and do not have their counterparts when .
Experiments and Results
Our experiments are centered at answering the following Research Questions (RQs):
RQ1: how effective is VulDeePecker for multiclass vulnerability detection?
RQ2: does accommodating control-dependence indeed improve the effectiveness of VulDeePecker in multiclass vulnerability detection?
RQ3: can VulDeePecker be used with other deep-learning-based systems (e.g., VulDeePecker) to obtain higher effectiveness?
Whenever appropriate, we will consider the following extension of VulDeePecker (which was designed for detecting whether a program is vulnerable or not), dubbed VulDeePecker+, for multiclass vulnerability detection: Unlike VulDeePecker, which uses the sigmoid activation function and the binary crossentropy loss function, we propose using the softmax activation function and the categorical crossentropy loss function, leading to a variant which we call VulDeePecker+. We should compare VulDeePecker against with other multiclass detectors. Since open-source tools (e.g., Flawfinder) have poor detection capabilities and we have no access to commercial tools (e.g., Fortify and Coverity, no budget to buy), we only compare VulDeePecker against VulDeePecker+. Our experiments are performed on a computer with an Intel Xeon E5-1620 CPU operating at 3.50GHz and an NVIDIA GeForce GTX 1080 GPU. The operating system is Linux 3.10.0-514.6.2.el7.x86_64.
In order to evaluate the effectiveness of VulDeePecker, we apply it to (i) the testing set mentioned above and (ii) real-world product software.
Table III summarizes the experimental results. We observe that M_FPR, M_FNR, and M_F1 of VulDeePecker are 0.02%, 5.73%, and 94.22%, respectively. When compared with VulDeePecker+, VulDeePecker is 0.01% lower in terms of M_FPR, 10.75% lower in terms of M_FNR, and 8.72% higher in terms of M_F1. This might be explained by the fact that the local features learned from code attentions accommodate much information about vulnerability types, leading to much smaller multiclass false negative rate. Moreover, measurements of W_FPR, W_FNR, and W_F1 also indicate that VulDeePecker is more effective than VulDeePecker+. As to the test time, although the time of VulDeePecker is slightly higher than VulDeePecker+, considering the detection effectiveness of VulDeePecker, the time consumption is tolerable.
Fig 5 plots the detection result of VulDeePecker and VulDeePecker+, in terms of the F1, with respect to each of the 40 vulnerability types. We observe that VulDeePecker is overall more effective than VulDeePecker+, especially for vulnerability types CWE-673, CWE-362, CWE-170, sub-type of CWE-662 and CWE-573, and CWE-467. In order to explain this discrepancy, we observe that these five types have small numbers of vulnerabilities: 16 vulnerabilities for CWE-673, 211 for CWE-362, 45 for CWE-170, 33 for the sub-type of CWE-662 and CWE-573, and 55 for CWE-467; these vulnerabilities might have been ignored by VulDeePecker+ as noise. For example, CWE-467 is a type of vulnerabilities related to function call , which cannot be recognized by VulDeePecker+ because it often occurs in conjunction with buffer overflows (i.e., another type of vulnerabilities). In contrast, VulDeePecker can cope with by leveraging its arguments, which are captured by code attentions.
1.2 Experiments on Real-World Software
We apply VulDeePecker to 10 versions of 3 real-world software products, namely Libav, Seamonkey, and Xen. Since we do not know whether these products contain vulnerabilities or not (i.e., the ground truth is not available), we manually examine and confirm the detected vulnerabilities. Experimental results show that VulDeePecker detects 16 vulnerabilities, among which 14 vulnerabilities correspond to patterns of known vulnerabilities. Among these 14 vulnerabilities, 6 vulnerabilities (respectively corresponding to the patterns of CVE-2013-0866, CVE-2013-4264, CVE-2013-7012, CVE-2013-7022, CVE-2013-7023, and CVE-2014-9319) are detected in Libav, 3 vulnerabilities (respectively corresponding to the patterns of CVE-2015-4511, CVE-2015-4513, and CVE-2015-4517) are detected in Seamonkey, and 5 vulnerabilities (respectively corresponding to the patterns of CVE-2013-4149, CVE-2013-4150, CVE-2014-5263, CVE-2016-4952, and CVE-2016-9923) are detected in Xen. These vulnerabilities include out-of-bounds read, use-after-free, and improper restriction of operations within the bounds of a memory buffer. The other 2 vulnerabilities are new because they are not known to exist in Xen 4.4.0 to the public until now (despite that they respectively correspond to the patterns of CVE-2015-7512 and CVE-2016-5126), but have been silently fixed by the software vendor when releasing newer versions. These 2 vulnerabilities are caused by buffer overflow and are highlighted in Table IV (for ethical reasons, we anonymized the file name of the vulnerable program). We map CVE# to them in Table IV because they were detected via the patterns corresponding to those CVE#’s. Summarizing preceding discussions, we draw:
VulDeePecker is effective for multiclass vulnerability detection, especially its use of local features learned from code attentions makes it effective even when samples are small.
2 Experiments for Answering RQ2
In order to quantify the value of control-dependence in multiclass vulnerability detection, we prepare a dataset that only contains data-dependence, which is indeed obtained by considering data-dependence relation only in Step I.1 of VulDeePecker. The same 80% of the dataset (i.e., the code gadgets corresponding to the same programs in the training set for learning VulDeePecker) is used for training, and the rest 20% is used for testing.
Table V summarizes the experimental results. We observe that when additionally accommodating control-dependence, VulDeePecker can improve M_F1 by 12.63%, W_F1 by 11.13% and decrease M_FNR by 12.27%, W_FNR by 6.9%. This justifies the value of accommodating control-dependence relation in multiclass vulnerability detection, which is intuitive. In summary, we draw:
Accommodating control-dependence enhances the capability of VulDeePecker in multiclass vulnerability detection.
3 Experiments for Answering RQ3
In order to answer RQ3, we use VulDeePecker and VulDeePecker+ together with respect to the dataset accommodating both data-dependence and control-dependence, as follows: For a code gadget sample (including its code attention), suppose VulDeePecker detects it as type- with probability and VulDeePecker+ detects it as type- with probability , where (with corresponding to our dataset). Then, the vulnerability type of the sample is set to be or corresponding to the maximum probability, namely
Table VI summarizes the results, which show that using VulDeePecker and VulDeePecker+ together indeed leads to better detection accuracy (2.65% and 1.59% higher in M_F1 and W_F1). This suggests that VulDeePecker and VulDeePecker+ accommodate different kinds of information useful for multiclass vulnerability detection. In summary, we draw:
VulDeePecker can be used together with other deep-learning-based systems such as VulDeePecker+ to accommodate more, useful information for multiclass vulnerability detection.
Related Work
Vulnerability detection in the source code is a fundamental problem of software security. Many scientists have conducted research on this issue and many good methods have been proposed. We divide prior studies on source-code vulnerability detection into three approaches: rule-based vs. similarity-based vs. pattern-based.
In this approach, vulnerability detection is based on some rules that are often defined by human experts. Open-source systems under this approach include Flawfinder , Cvechecker , Cppcheck , FindBugs , and Splint . These tools use simple rules to characterize vulnerabilities and have limited successes. Commercial systems under this approach include Checkmarx , Coverity , IBM Security AppScan Source , and CodeSonar . These commercial tools are more capable than the open-source tools mentioned above. The third category is the academic research methods. These academic research results are generally targeted at certain aspects of vulnerability detection and propose better vulnerability rules . This approach largely relies on human experts to define rules. In contrast, VulDeePecker aims to automate the vulnerability detection process as much as possible, especially without relying on human experts to define rules with respect to every vulnerability.
2 Similarity-Based Vulnerability Detection
Since code-cloning is widely observed in practice, this approach aims to detect vulnerabilities that are often caused by code-cloning. The basic idea is that when a piece of code is cloned, the vulnerability in it is automatically replicated; this explains why detecting a piece of code that similar to a piece of vulnerable code could detect vulnerabilities. In this approach, similarity can be measured using tokens , strings , trees , graphs , or their hybrids . This approach cannot detect vulnerabilities that are not caused by code-cloning . VulDeePecker does not follow this approach.
3 Pattern-Based Vulnerability Detection
Rather than relying on human experts for defining vulnerability rules, this approach is intended to define features and use machine learning techniques to automatically learn vulnerability patterns . These studies still rely on human experts to define features to characterize vulnerabilities. A recent development is to exploit deep learning, which has a great potential in reducing the burden on human experts for defining features. VulDeePecker is the first system using deep learning to detect vulnerabilities at slice-level, while noting that there are also related studies on using deep learning for vulnerability discovery at the function level , defect prediction and related tasks . Above systems are binary classifiers by telling whether a piece of code is vulnerable or not.
The present study follows this approach and more specifically extends VulDeePecker to detect multiclass vulnerabilities. As discussed above, the extension is based on enhanced concept of code gadget, novel concept of code attention and a novel neural network architecture because a straightforward extension does not lead to good accuracy.
Limitations
The present study has limitations. First, VulDeePecker can detect vulnerability types, but cannot pin down the precise location of a vulnerability at a granularity finer than code gadget. Although the granularity of code gadget, which consists of a number of statements, is substantially finer than the widely used granularity of programs, files or functions, it is an interesting future work to precisely pin down the location (e.g., exactly the vulnerable statements but nothing more) of a vulnerability. This is important because it will alleviate human analysts in pinning down locations of vulnerabilities. Second, the current design and implementation of VulDeePecker are geared towards programs written in C/C++. future research needs to consider programs written in other programming languages. Third, the current design and implementation of VulDeePecker focus on vulnerabilities that are related to library/API function calls. Future research needs to consider vulnerabilities that are not associated to library/API functions.
Conclusion
We have presented VulDeePecker, which is the first deep learning-based multiclass vulnerability detection system. Its capability in pinning down the type of vulnerability in a code gadget (i.e., a number of statements) helps human analysts in recognizing vulnerabilities. The multiclass detection capability largely comes from the use of code attentions. Systematic experiments show that VulDeePecker is effective, that accommodating control-dependence can lead to higher detection capabilities, and that VulDeePecker can be used together with other systems (e.g., VulDeePecker+) to capture more useful information for multiclass vulnerability detection. The limitations discussed above are interesting open problems for future investigations.
Acknowledgments
We thank the anonymous reviewers for their constructive comments that guided us in improving the paper. The authors at Huazhong University of Science and Technology is supported in part by the National Key Research and Development Plan of China under Grant No.2017YFB0802205, in part by the National Natural Science Foundation of China under Grant No.61672249 and No.61802106, and in part by the Shenzhen Fundamental Research Program under Grant No.JCYJ20170413114215614. Shouhuai Xu is supported in part by NSF CREST Grant No. 1736209. The opinions expressed in the paper are those of the authors’ and do not reflect the funding agencies’ policies in any sense.