$μ$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 μ\muVulDeePecker for short.

The innovations of the system are in three-fold. First, a conceptual innovation underlying μ\muVulDeePecker 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 μ\muVulDeePecker is redefining the concept and extraction method of code gadget by introducing control-dependence. The last innovation underlying μ\muVulDeePecker 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 μ\muVulDeePecker, 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) μ\muVulDeePecker 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 μ\muVulDeePecker in multiclass vulnerability detection. (iii) It is possible to further improve the effectiveness of μ\muVulDeePecker 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 μ\muVulDeePecker, 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 μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker to help pinpoint vulnerability types. Fourth, the neural network architecture underlying μ\muVulDeePecker is more involved than the use of a standard BLSTM in VulDeePecker. The innovation in μ\muVulDeePecker 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 μ\muVulDee-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 μ\muVulDeePecker 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 PP is an ordered set of statements, denoted by P={p1,p2,…,pε}P=\{p_{1},p_{2},\ldots,p_{\varepsilon}\}, where pip_{i} (1≤i≤ε1\leq i\leq\varepsilon) is a statement. A statement pip_{i} is an ordered set of tokens, denoted by pi={ti,1,ti,2,…,ti,w}p_{i}=\{t_{i,1},t_{i,2},\ldots,t_{i,w}\}, where token ti,jt_{i,j} (1≤j≤w1\leq j\leq w) can be a variable identifier, function identifier, constant, keyword or operator and so on.

Given a program P={p1,p2,…,pε}P=\{p_{1},p_{2},\ldots,p_{\varepsilon}\}, a statement pi∈Pp_{i}\in P, a variable identifier token ti,jt_{i,j} belonging to pip_{i}, statement pup_{u} (1≤u≤ε1\leq u\leq\varepsilon) is said to be data-dependent on token ti,jt_{i,j} if ti,jt_{i,j} is used in pup_{u}.

Consider two statements pi,pjp_{i},p_{j} (i≠ji\neq j) in a program P={p1,p2,…,pε}P=\{p_{1},p_{2},\ldots,p_{\varepsilon}\}. If the execution outcome of pip_{i} affects whether pjp_{j} will be executed or not, pjp_{j} is said to be control-dependent on pip_{i}.

Consider a program P={p1,p2,…,pε}P=\{p_{1},p_{2},\ldots,p_{\varepsilon}\}, a library/API function call denoted by fvif_{v_{i}}, and a statement pvp_{v} (1≤v≤ε1\leq v\leq\varepsilon) containing the function call fvif_{v_{i}}. Assume that pvp_{v} contains a total of mm function calls, then 1≤vi≤m1\leq v_{i}\leq m. Denote the arguments of the function call fvif_{v_{i}} by a set Avi={tvi,x1,tvi,x2,…,tvi,xη}A_{v_{i}}=\{t_{v_{i},x_{1}},t_{v_{i},x_{2}},\ldots,t_{v_{i},x_{\eta}}\}. A code gadget corresponding to function call fvif_{v_{i}} in statement pvp_{v}, denoted by svis_{v_{i}}, is an ordered set of statements in PP, each of which is either recursively data-dependent on one or multiple arguments in AviA_{v_{i}} or control-dependent on pvp_{v} in a recursive manner.

Consider a program P={p1,p2,…,pε}P=\{p_{1},p_{2},\ldots,p_{\varepsilon}\}, a library/API function call fvif_{v_{i}}, a statement pvp_{v} (1≤v≤ε1\leq v\leq\varepsilon) containing the function call fvif_{v_{i}}, and a code gadget svis_{v_{i}} corresponding to fvif_{v_{i}} in pvp_{v}. Let R={rk}1≤k≤hR=\{r_{k}\}_{1\leq k\leq h} 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 svis_{v_{i}}, denoted by cvic_{v_{i}}, is a subset of the statements in code gadget svis_{v_{i}} that match the syntax characteristics specified by some rk∈Rr_{k}\in R.

Design of μ𝜇\muVulDeePecker

Our objective is to design a multiclass vulnerability detection system. Let {0,1,2,…,m}\{0,1,2,\ldots,m\} denote a set of vulnerability types, where type-0 means “not vulnerable” and type-ii (1≤i≤m1\leq i\leq m) 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 μ\muVulDeePecker 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(a)(a), 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(a)(a) 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(a)(a) shows the SDG derived from the PDGs of functions mainmain and printMsgprintMsg 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(a)(a) contains the library/API function call strncpystrncpy 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 scanfscanf and mallocmalloc as examples, which represent the forward function of the input and the backward function of the non-input. The function scanfscanf 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 scanfscanf can only be contained in the backward slice of scanfscanf, 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 mallocmalloc, 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 mallocmalloc miss because these statements only can be sliced in the forward slice of mallocmalloc. 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 fvif_{v_{i}} made in a statement pvp_{v}, which is represented as a node nn in the SDG in question. For each argument tvi,xηt_{v_{i},x_{\eta}} of fvif_{v_{i}}, Algorithm 1 does the following: (i) using function get_for_sliceget\_for\_slice (Lines 1-10 in Algorithm 1) to identify successors of node nn in the SDG, namely the nodes that are data-dependent upon tvi,xδt_{v_{i},x_{\delta}} or control-dependent upon nn (either directly or indirectly), and then generates a forward slice sfvisf_{v_{i}}; (ii) using function get_back_sliceget\_back\_slice (Lines 11-20 in Algorithm 1) to identify predecessors of node nn recursively, upon which tvi,xδt_{v_{i},x_{\delta}} is data-dependent or nn is control-dependent (either directly or indirectly), and then generates a backward slice sbvisb_{v_{i}}. Finally, Algorithm 1 merges slices sfvisf_{v_{i}} and sbvisb_{v_{i}} (Line 29 in Algorithm 1) to obtain a code gadget svis_{v_{i}} corresponding to function call fvif_{v_{i}}. The last column in Fig 2(a)(a) shows the example of extracting the code gadget according to arguments datadata, destdest and nn of function call strncpystrncpy.

Step II: this step labels each code gadget as “0” (not vulnerable) or ii (type-ii vulnerability), where 1≤i≤m1\leq i\leq m. 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”, …\ldots; “func_0”, “func_1”, …\ldots). 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 r1r_{1}: if the attribute of a statement pip_{i} in code gadget svis_{v_{i}} is a definition statement and the variables defined in pip_{i} match the arguments in the library/API function call, then pip_{i} is a statement in code attention cvic_{v_{i}}. For example, statements 2, 4 and 5 in Fig 2(b)(b) are definition statements of arguments varb_0varb\_0, varb_1varb\_1 and varb_2varb\_2, which are used in function call strncpystrncpy.

Rule r2r_{2}: if the attribute of statement pip_{i} in code gadget svis_{v_{i}} is a control statement, then pip_{i} is a statement in code attention cvic_{v_{i}}. For example, statements 7 and 16 in Fig 2(b)(b) are such statements.

Rule r3r_{3}: if the attribute of statement pip_{i} in code gadget svis_{v_{i}} contains library/API function calls, then pip_{i} is a statement in code attention cvic_{v_{i}}. For example, statements 6, 8, and 17 in Fig 2(b)(b) 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 pip_{i} in svis_{v_{i}}, Algorithm 2 parses statement pip_{i} into an ordered set of tokens by a lexical analysis, infers attribute attrattr of statement pip_{i} (e.g., “def”, “cond”) by analyzing the context structure of these tokens, and uses each token and attrattr to match the rules in a given rule set R={rk}1≤k≤hR=\{r_{k}\}_{1\leq k\leq h} to generate code attentions. Currently, we only use a rule set R={r1,r2,r3}R=\{r_{1},r_{2},r_{3}\} 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 HiH_{i}’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 hih_{i}’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(b)(b), 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 μ\muVulDeePecker model, outputting the vulnerability types of code gadgets (recalling that type-0 means the code gadgets is not vulnerable).

3 Generalization Analysis

μ\muVulDeePecker 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). μ\muVulDeePecker 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, μ\muVulDeePecker 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 μ\muVulDeePecker.

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., m=40m=40 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-ii where 1≤i≤401\leq i\leq 40; 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 τ1\tau_{1} and τ2\tau_{2} 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 τ1\tau_{1} (τ2\tau_{2}), we pad 0 vectors at their tail to make their vectors of length τ1\tau_{1} (τ2\tau_{2}); for code gadgets (code attentions) containing more words or longer vectors than τ1\tau_{1} (τ2\tau_{2}), we cut their vectors at the tail of code gadgets (code attentions) to match length τ1\tau_{1} (τ2\tau_{2}).

Step VI: recall that we use the BLSTM as a building-block for constructing μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker 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 LL be a set of vulnerability types (in this paper, ∣L∣=m=40|L|=m=40), Let TPlTP_{l}, FPlFP_{l}, FNlFN_{l}, TNlTN_{l} and XlX_{l} 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 ll, where 1≤l≤m=401\leq l\leq m=40.

The metrics for evaluating multiclass vulnerability detectors are M_FPRM\_FPR (the multiclass counterpart of the false positive rate based on the total number of vulnerability classes), M_FNRM\_FNR (the multiclass counterpart of the false negative rate based on the total number of vulnerability classes), M_F1M\_F1 (the multiclass counterpart of the F1 measure based on the total number of vulnerability classes), W_FPRW\_FPR (the multiclass counterpart of the false positive rate based on the weight of each vulnerability class), W_FNRW\_FNR (the multiclass counterpart of the false negative rate based on the weight of each vulnerability class), W_F1W\_F1 (the multiclass counterpart of F1 measure based on the weight of each vulnerability class). The M_∗M\_* metrics are arithmetic average of the metrics of the overall vulnerability classes. The W_∗W\_* 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 ∣L∣=1|L|=1, M_FPRM\_FPR naturally degenerates to the false positive rate (FPRFPR) of binary vulnerability detector, M_FNRM\_FNR naturally degenerates to the false negative rate (FNRFNR) of binary vulnerability detector, and M_F1M\_F1 naturally degenerates to the F1F1 measure of binary vulnerability detector. However, W_FPRW\_FPR, W_FNRW\_FNR, and W_F1W\_F1 do not have their counterparts when ∣L∣=1|L|=1.

Experiments and Results

Our experiments are centered at answering the following Research Questions (RQs):

RQ1: how effective is μ\muVulDeePecker for multiclass vulnerability detection?

RQ2: does accommodating control-dependence indeed improve the effectiveness of μ\muVulDeePecker in multiclass vulnerability detection?

RQ3: can μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker, 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 μ\muVulDeePecker are 0.02%, 5.73%, and 94.22%, respectively. When compared with VulDeePecker+, μ\muVulDeePecker 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 μ\muVulDeePecker is more effective than VulDeePecker+. As to the test time, although the time of μ\muVulDeePecker is slightly higher than VulDeePecker+, considering the detection effectiveness of μ\muVulDeePecker, the time consumption is tolerable.

Fig 5 plots the detection result of μ\muVulDeePecker and VulDeePecker+, in terms of the F1, with respect to each of the 40 vulnerability types. We observe that μ\muVulDeePecker 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 sizeof()sizeof(), which cannot be recognized by VulDeePecker+ because it often occurs in conjunction with buffer overflows (i.e., another type of vulnerabilities). In contrast, μ\muVulDeePecker can cope with sizeof()sizeof() by leveraging its arguments, which are captured by code attentions.

1.2 Experiments on Real-World Software

We apply μ\muVulDeePecker 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 μ\muVulDeePecker 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:

μ\muVulDeePecker 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 μ\muVulDeePecker. The same 80% of the dataset (i.e., the code gadgets corresponding to the same programs in the training set for learning μ\muVulDeePecker) 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, μ\muVulDeePecker 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 μ\muVulDeePecker in multiclass vulnerability detection.

3 Experiments for Answering RQ3

In order to answer RQ3, we use μ\muVulDeePecker 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 μ\muVulDeePecker detects it as type-ii with probability pip_{i} and VulDeePecker+ detects it as type-jj with probability qjq_{j}, where 0≤i,j≤m0\leq i,j\leq m (with m=40m=40 corresponding to our dataset). Then, the vulnerability type of the sample is set to be ii or jj corresponding to the maximum probability, namely

Table VI summarizes the results, which show that using μ\muVulDeePecker and VulDeePecker+ together indeed leads to better detection accuracy (2.65% and 1.59% higher in M_F1 and W_F1). This suggests that μ\muVulDeePecker and VulDeePecker+ accommodate different kinds of information useful for multiclass vulnerability detection. In summary, we draw:

μ\muVulDeePecker 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, μ\muVulDeePecker 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 . μ\muVulDeePecker 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, μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker 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 μ\muVulDeePecker, 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 μ\muVulDeePecker is effective, that accommodating control-dependence can lead to higher detection capabilities, and that μ\muVulDeePecker 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.

References