pex: a permission check analysis framework for linux kernellinux, inode_permission (as listed in...

17
This paper is included in the Proceedings of the 28th USENIX Security Symposium. August 14–16, 2019 • Santa Clara, CA, USA 978-1-939133-06-9 Open access to the Proceedings of the 28th USENIX Security Symposium is sponsored by USENIX. PeX: A Permission Check Analysis Framework for Linux Kernel Tong Zhang, Virginia Tech; Wenbo Shen, Zhejiang University; Dongyoon Lee, Stony Brook University; Changhee Jung, Purdue University; Ahmed M. Azab and Ruowen Wang, Samsung Research America https://www.usenix.org/conference/usenixsecurity19/presentation/zhang-tong

Upload: others

Post on 27-Jun-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

This paper is included in the Proceedings of the 28th USENIX Security Symposium.

August 14–16, 2019 • Santa Clara, CA, USA

978-1-939133-06-9

Open access to the Proceedings of the 28th USENIX Security Symposium

is sponsored by USENIX.

PeX: A Permission Check Analysis Framework for Linux Kernel

Tong Zhang, Virginia Tech; Wenbo Shen, Zhejiang University; Dongyoon Lee, Stony Brook University; Changhee Jung, Purdue University; Ahmed M. Azab and

Ruowen Wang, Samsung Research America

https://www.usenix.org/conference/usenixsecurity19/presentation/zhang-tong

Page 2: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

PeX: A Permission Check Analysis Framework for Linux Kernel

Tong Zhang*

Virginia TechWenbo Shen†

Zhejiang UniversityDongyoon Lee

Stony Brook UniversityChanghee Jung

Purdue University

Ahmed M. Azab‡

Samsung Research AmericaRuowen Wang‡

Samsung Research America

AbstractPermission checks play an essential role in operating systemsecurity by providing access control to privileged functionali-ties. However, it is particularly challenging for kernel develop-ers to correctly apply new permission checks and to scalablyverify the soundness of existing checks due to the large codebase and complexity of the kernel. In fact, Linux kernel con-tains millions of lines of code with hundreds of permissionchecks, and even worse its complexity is fast-growing.

This paper presents PeX, a static Permission check errordetector for LinuX, which takes as input a kernel source codeand reports any missing, inconsistent, and redundant permis-sion checks. PeX uses KIRIN (Kernel InteRface based In-direct call aNalysis), a novel, precise, and scalable indirectcall analysis technique, leveraging the common programmingparadigm used in kernel abstraction interfaces. Over the inter-procedural control flow graph built by KIRIN, PeX automati-cally identifies all permission checks and infers the mappingsbetween permission checks and privileged functions. For eachprivileged function, PeX examines all possible paths to thefunction to check if necessary permission checks are correctlyenforced before it is called.

We evaluated PeX on the latest stable Linux kernel v4.18.5for three types of permission checks: Discretionary AccessControls (DAC), Capabilities, and Linux Security Modules(LSM). PeX reported 36 new permission check errors, 14 ofwhich have been confirmed by the kernel developers.

1 IntroductionAccess control [38] is an essential security enforcementscheme in operating systems. They assign users (or processes)different access rights, called permissions, and enforce thatonly those who have appropriate permissions can access criti-cal resources (e.g., files, sockets). In the kernel, access control

*This work was started when Tong Zhang interned at Samsung ResearchAmerica, mentored by Wenbo Shen and Ahmed M. Azab.†Corresponding author.‡Now at Google.

is often implemented in the form of permission checks beforethe use of privileged functions accessing the critical resources.

Over the course of its evolution, Linux kernel has employedthree different access control models: Discretionary AccessControls (DAC), Capabilities, and Linux Security Modules(LSM). DAC distinguishes privileged users (a.k.a., root) fromunprivileged ones. The unprivileged users are subject to vari-ous permission checks, while the root bypasses them all [4].Linux kernel v2.2 divided the root privilege into small unitsand introduced Capabilities to allow more fine-grained accesscontrol. From kernel v2.6, Linux adopted LSM in which vari-ous security hooks are defined and placed on critical paths ofprivileged operations. These security hooks can be instanti-ated with custom checks, facilitating different security modelimplementations as in SELinux [41] and AppArmor [3].

Unfortunately, for a new feature or vulnerability found,these access controls have been applied to the Linux kernelcode in an ad-hoc manner, leading to missing, inconsistent, orredundant permission checks. Given the ever-growing com-plexity of the kernel code, it is becoming harder to manuallyreason about the mapping between permission checks andprivileged functions. In reality, kernel developers rely on theirown judgment to decide which checks to use, often leadingto over-approximation issues. For instance, Capabilities wereoriginally introduced to solve the “super” root problem, butit turns out that more than 38% of Capabilities indeed checkCAP_SYS_ADMIN, rendering it yet another root [5].

Even worse, there is no systematic, sound, and scalable wayto examine whether all privileged functions (via all possiblepaths) are indeed protected by correct permission checks. Thelack of tools for checking the soundness of existing or newpermission checks can jeopardize the kernel security puttingthe privileged functions at risk. For example, DAC, CAP andLSM introduce hundreds of security checks scattered overmillions of lines of the kernel code, and it is an open problemto verify if all code paths to a privileged function encounter itscorresponding permission check before reaching the function.Given the distributed nature of kernel development and thesignificant amount of daily updates, chances are that some

USENIX Association 28th USENIX Security Symposium 1205

Page 3: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

parts of the code may miss checks on some paths or introducethe inconsistency between checks, weakening the operatingsystem security.

This paper presents PeX, a static permission check analysisframework for Linux kernel. PeX makes it possible to soundlyand scalably detect any missing, inconsistent and redundantpermission checks in the kernel code. At a high level, PeXstatically explores all possible program paths from user-entrypoints (e.g., system calls) to privileged functions and detectspermission check errors therein. Suppose PeX finds a path inwhich a privileged function, say PF, is protected (preceded)by a check, say Chk in one code. If it is found that any otherpaths to PF bypass Chk, then it is a strong indication of amissing check. Similarly, PeX can detect inconsistent andredundant permission checks. While conceptually simple, itis very challenging to realize a sound and precise permissioncheck error detection at the scale of Linux kernel.

In particular, there are two daunting challenges that PeXshould address. First, Linux kernel uses indirect calls veryfrequently, yet its static call graph analysis is notoriouslydifficult. The latest Linux kernel (v4.18.5) contains 15.8MLOC, 247K functions, and 115K indirect callsites, renderingexisting precise solutions (e.g., SVF [43]) unscalable. Onlyworkaround available to date is either to apply the solutionsunsoundly (e.g., only on a small code partition as with K-Miner [22]) or to rely on naive imprecise solutions (e.g., type-based analysis). Either way leads to undesirable results, i.e.,false negatives (K-Miner) or positives (type-based one).

For a precise and scalable indirect call analysis, we intro-duce a novel solution called KIRIN (Kernel InteRface basedIndirect call aNalysis), which leverages kernel abstraction in-terfaces to enable precise yet scalable indirect call analysis.Our experiment with Linux v4.18.5 shows that KIRIN allowsPeX to detect many previously unknown permission checkbugs, while other existing solutions either miss many of themor introduce too many false warnings.

Second, unlike Android which has been designed with thepermission-based security model in mind [2], Linux kerneldoes not document the mapping between a permission checkand a privileged function. More importantly, the huge Linuxkernel code base makes it practically impossible to reviewthem all manually for the permission check verification.

To tackle this problem, PeX presents a new technique whichtakes as input a small set of known permission checks andautomatically identifies all other permission checks includ-ing their wrappers. Moreover, PeX’s dominator analysis [31]automates the process of identifying mappings between per-mission checks and their potentially privileged functions aswell. Our experiment with Linux kernel v4.18.5 shows thatstarting from a small set of well-known 3 DAC, 3 Capacities,and 190 LSM checks, PeX automatically (1) identifies 19, 16,and 53 additional checks, respectively, and (2) derives 9243pairs of permission checks and privileged functions.

The contributions of this paper are summarized as follows:

Table 1: Commonly used permission checks in Linux.

Type Total # Permission ChecksDAC 3 generic_permission, sb_permission, inode_permissionCapabilities 3 capable, ns_capable, avc_has_perm_noauditLSM 190 security_inode_readlinkat, security_file_ioctl, etc..

• New Techniques: We proposed and implemented PeX, astatic permission check analysis framework for Linux ker-nel. We also developed new techniques that can performscalable indirect call analysis and automate the process ofidentifying permission checks and privileged functions.

• Practical Impacts: We analyzed DAC, Capabilities, andLSM permission checks in the latest Linux kernel v4.18.5using PeX, and discovered 36 new permission check bugs,14 of which have been confirmed by kernel developers.

• Community Contributions: We will release PeX as anopen source project, along with the identified mapping be-tween permission checks and privileged functions. Thiswill allow kernel developers to validate their codes withPeX, and to contribute to PeX by refining the mappingswith their own domain knowledge.

2 Background: Permission Checks in LinuxThis section introduces DAC, Capabilities, and LSM in Linuxkernel. Table 1 lists practically-known permission checks inLinux. Unfortunately, the full set is not well-documented.

2.1 Discretionary Access Control (DAC)DAC restricts the accesses to critical resources based on theidentity of subjects or the group to which they belong [36,46].In Linux, each user is assigned a user identifier (uid) and agroup identifier (gid). Correspondingly, each file has prop-erties including the owner, the group, the rwx (read, write,and execute) permission bits for the owner, the group, andall other users. When a process wants to access a file, DACgrants the access permissions based on the process’s uid,gid as well as the file’s permission bits. For example inLinux, inode_permission (as listed in Table 1) is often usedto check the permissions of the current process on a giveninode. More precisely speaking, however, it is a wrapper ofposix_acl_permission, which performs the actual check.

In a sense, DAC is a coarse-grained access control model.Under the Linux DAC design, the “root” bypasses all per-mission checks. This motivates fine-grained access controlscheme—such as Capabilities—to reduce the attack surface.

2.2 CapabilitiesCapabilities, since Linux kernel v2.2 (1999), enable a fine-grained access control by dividing the root privilege into smallsets. As an example, for users with the CAP_NET_ADMIN ca-pability, kernel allows them to use ping, without the needto grant the full root privilege. Currently, Linux kernelv4.18.5 supports 38 Capabilities including CAP_NET_ADMIN,

1206 28th USENIX Security Symposium USENIX Association

Page 4: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

CAP_SYS_ADMIN, and so on. Functions capable and ns_capable

are the most commonly used permission checks for Capabili-ties (as listed in Table 1). Both determine whether a processhas a particular capability or not, while ns_capable performsan additional check against a given user namespace. They in-ternally use security_capable as the basic permission check.

Capabilities are supposed to be fine-grained and distinct [4].However, due to the lack of clear scope definitions, the choiceof specific Capability for protecting a privileged functionhas been made based on kernel developers’ own understand-ing in practice. Unfortunately, this leads to frequent use ofCAP_SYS_ADMIN (451 out of 1167, more than 38%), and it isjust treated as yet another root [5]; grsecurity points out that19 Capabilities are indeed equivalent to the full root [1].

2.3 Linux Security Module (LSM)LSM [51], introduced in kernel v2.6 (2003), provides a setof fine-grained pluggable hooks that are placed at varioussecurity-critical points across the kernel. System administra-tors can register customized permission checking callbacks tothe LSM hooks so as to enforce diverse security policies. Thelatest Linux kernel v4.18.5 defines 190 LSM hooks. One com-mon use of LSM is to implement Mandatory Access Control(MAC) [8] in Linux (e.g., SELinux [40, 41], AppArmor [3]).MAC enforces more strict and non-overridable access controlpolicies, controlled by system administrators. For example,when a process tries to read the file path of a symbolic link,security_inode_readlink is invoked to check whether theprocess has read permission to the symlink file. The SELinuxcallback of this hook checks if a policy rule can grant thispermission (e.g., allow domain_a type_b:lnk_file read). Itis worth noting that the effectiveness of LSM and its MACmechanisms highly depend on whether the hooks are placedcorrectly and soundly at all security-critical points. If a hookis missing at any critical point, there is no way for MAC toenforce a permission check.

3 Examples of Permission Check ErrorsThis section illustrates different kinds of permission checkerrors, found by PeX and confirmed by the Linux kernel de-velopers. We refer to those functions, that validate whether aprocess (a user or a group) has proper permission to do certainoperations, as permission checks. Similarly, we define privi-leged functions to be those functions which only a privilegedprocess can access and thus require permission checks.

3.1 Capability Permission Check ErrorsFigure 1 shows real code snippets of Capability permissioncheck errors in Linux kernel v4.18.5. Figure 1a shows thekernel function scsi_ioctl, in which sg_scsi_ioctl (Line7) is safeguarded by two Capability checks, CAP_SYS_ADMINand CAP_SYS_RAWIO (Line 5). To the contrary, scsi_cmd_ioctlin Figure 1b calls the same function sg_scsi_ioctl (Line

1 int scsi_ioctl(struct scsi_device *sdev, int cmd,void __user *arg)↪→

2 {3 ...4 case SCSI_IOCTL_SEND_COMMAND:5 if (!capable(CAP_SYS_ADMIN) ||

!capable(CAP_SYS_RAWIO))↪→6 return -EACCES;7 return sg_scsi_ioctl(sdev->request_queue, NULL,

0, arg);↪→8 ...9 }

(a) sg_scsi_ioctl (Line 7) is called with CAP_SYS_ADMIN andCAP_SYS_RAWIO capability checks (Line 5). arg is user space con-trollable.

1 int scsi_cmd_ioctl(struct request_queue *q, ...,void __user *arg)↪→

2 {3 ...4 case SCSI_IOCTL_SEND_COMMAND:5 ...6 if (!arg)7 break;8 err = sg_scsi_ioctl(q, bd_disk, mode, arg);9 break;

10 ...11 return err;12 }

(b) sg_scsi_ioctl (Line 8) is called without capability checks.arg is user space controllable.

1 int sg_scsi_ioctl(struct request_queue *q, structgendisk *disk, fmode_t mode, structscsi_ioctl_command __user *sic)

↪→↪→

2 {3 ...4 err = blk_verify_command(req->cmd, mode);5 ...6 return err;7 }89 int blk_verify_command(unsigned char *cmd, fmode_t

mode)↪→10 {11 ...12 if (capable(CAP_SYS_RAWIO))13 return 0;14 ...15 return -EPERM;16 }

(c) sg_scsi_ioctl calls blk_verify_command, which checksCAP_SYS_RAWIO capability.

Figure 1: Capability check errors discovered by PeX.

8) without any Capability check. These two functionsshare three similarities. First, both of them are reachablefrom the userspace by ioctl system call. Second, bothcall sg_scsi_ioctl with a userspace parameter, void __user

*arg. Last, there is no preceding Capability check on all possi-ble paths to them (though scsi_ioctl performs two checks).

The kernel is supposed to sanitize userspace inputs andcheck permissions to ensure that only users with appropriatepermissions can conduct certain privileged operations. AsSCSI (Small Computer System Interface) functions manipu-late the hardware, they should be protected by Capabilities.At first glance, scsi_ioctl seems to be correctly protected(while scsi_cmd_ioctl misses two Capability checks).

However, delving into sg_scsi_ioctl ends up with a differ-ent conclusion. As shown in Figure 1c, sg_scsi_ioctl callsblk_verify_command, which in turn checks CAP_SYS_RAWIO.Considering all together, scsi_ioctl checks CAP_SYS_ADMIN

once but CAP_SYS_RAWIO “twice”, leading to a redundant per-mission check. On the other hand, scsi_cmd_ioctl checks

USENIX Association 28th USENIX Security Symposium 1207

Page 5: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

1 static int do_readlinkat(int dfd, const char __user*pathname, char __user *buf, int bufsiz)↪→

2 {3 ...4 error = security_inode_readlink(path.dentry);5 if (!error) {6 touch_atime(&path);7 error = vfs_readlink(path.dentry, buf, bufsiz);8 }9 ...

10 }

(a) Kernel LSM usage in system call readlinkat.vfs_readlink (Line 7) is protected bysecurity_inode_readlink (Line 4). Both pathnameand buf (Line 1 and Line 7) are user controllable.1 int ksys_ioctl(unsigned int fd, unsigned int cmd,

unsigned long arg)↪→2 {3 ...4 error = security_file_ioctl(f.file, cmd, arg);5 if (!error)6 error = do_vfs_ioctl(f.file, fd, cmd, arg);7 ...8 }9

10 int xfs_readlink_by_handle(struct file *parfilp,xfs_fsop_handlereq_t *hreq)↪→

11 {12 ...13 error = vfs_readlink(dentry, hreq->ohandle, olen);14 ...15 }

(b) Kernel LSM usage in system call ioctl. It callssecurity_file_ioctl (Line 4) to protect do_vfs_ioctl(Line 6). hreq->ohandle and olen are also user controllable.

Figure 2: LSM check errors discovered by PeX.

only CAP_SYS_RAWIO, resulting in a missing permission checkfor CAP_SYS_ADMIN. In particular, PeX detects this bug as aninconsistent permission check because the two paths disagreewith each other, and further investigation shows that one isredundant and the other is missing.

3.2 LSM Permission Check Errors

The example of LSM permission check errors is related tohow LSM hooks are instrumented for two different systemcalls readlinkat and ioctl.

Figure 2a shows the LSM usage in the readlinkat systemcall. On its call path, vfs_readlink (Line 7) is protected bythe LSM hook security_inode_readlink (Line 4) so that aLSM-based MAC mechanism, such as SELinux or AppArmor,can be realized to allow or deny the vfs_readlink operation.

Figure 2b presents two sub-functions for the system callioctl. Similar to the above case, ioctl calls ksys_ioctl,which includes its own LSM hook security_file_ioctl

(Line 4) before do_vfs_ioctl (Line 6). This is proper design,and there is no problem so far. However, it turns out that thereis a path from do_vfs_ioctl to xfs_readlink_by_handle

(Line 10), which eventually calls the same privileged func-tion vfs_readlink (see Line 7 in Figure 2a and Line 13in Figure 2b). While this function is protected by thesecurity_inode_readlink LSM hook in readlinkat, thatis not the case for the path to the function going throughxfs_readlink_by_handle. The problem is that SELinux main-tains separate ‘allow’ rules for read and ioctl. With the miss-ing LSM security_inode_readlink check, a user only with

1 struct file_operations {2 ...3 ssize_t (*read_iter) (struct kiocb *, struct

iov_iter *);,!4 ssize_t (*write_iter) (struct kiocb *, struct

iov_iter *);,!5 ...6 }

(a) The Virtual File System (VFS) kernel interface.

const struct file_operations ext4_file_operations{

. . .

.read_iter = ext4_file_read_iter,

.write_iter = ext4_file_write_iter,

. . .}

syscall(1, fd, buffer, count)

write(fd, buffer, count)

SyS_write(fd, buffer, count)vfs_write(fd.file, buffer, count, fd.pos)

file->f_op->write_iter(kio, iter);

User space

Kernel space syscall dispatcher

const struct file_operations nfs_file_operations{

. . .

.read_iter = nfs_file_read,

.write_iter = nfs_file_write,

. . .}

(b) VFS indirect calls in Linux kernel.Figure 3: Indirect call examples via the VFS kernel interface.

the ‘ioctl allow rule’ may exploit the ioctl system call totrigger the vfs_readlink operation, which should only bepermitted by the different ‘read allow rule’.

The above two Capability and LSM examples show howchallenging it is to ensure correct permission checks. Thereare no tools available for kernel developers to rely on tofigure out whether a particular function should be protectedby a permission check; and, (if so) which permission checksshould be used.

4 ChallengesThis section discusses two critical challenges in designingstatic analysis for detecting permission errors in Linux kernel.

4.1 Indirect Call Analysis in KernelThe first challenge lies in the frequent use of indirect calls inLinux kernel and the difficulties in statically analyzing themin a scalable and precise manner. To achieve a modular de-sign, the kernel proposes a diverse set of abstraction layersthat specify the common interfaces to different concrete im-plementations. For example, Virtual File System (VFS) [12]abstracts a file system, thereby providing a unified and trans-parent way to access local (e.g., ext4) and network (e.g., nfs)storage devices. Under this kernel programming paradigm,an abstraction layer defines an interface as a set of indirectfunction pointers while a concrete module initializes thesepointers with its own implementations. For example, as shownin Figure 3a, VFS abstracts all file system operations in a ker-

1208 28th USENIX Security Symposium USENIX Association

Page 6: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

nel interface struct file_operations that contains a set offunction pointers for different file operations. When a filesystem is initialized, it initializes the VFS interface with theconcrete function addresses of its own. For instance, Figure 3bshows that ext4 file system sets the write_iter functionpointer to ext4_file_write_iter, while nfs sets the pointerto nfs_file_write.

However, kernel’s large code base challenges the resolutionof these numerous function pointers within kernel interfaces.For example, the kernel used in our evaluation (v4.18.5) in-cludes 15.8M LOC, 247K functions, and 115K indirect call-sites. This huge code base makes existing precise pointeranalysis techniques [23–25, 35, 43] unscalable. In fact, StaticValue Flow (SVF) [43], i.e., the state-of-the-art analysis thatuses flow- and context-sensitive value flow for high preci-sion, failed to scale to the huge Linux kernel. That is becauseSVF is essentially a whole program analysis, and its indirectcall resolution thus requires tracking all objects such as func-tions, variables, and so on, making the value flow analysisunscalable to the large-size Linux kernel. In our experimentof running SVF for the kernel on a machine with 256GBmemory, SVF was crashed due to an out of memory error1.

Alternatively, one may opt for a simple “type-based” func-tion pointer analysis, which would scale to Linux kernel. How-ever, the type-based indirect call analysis would suffer fromserious imprecision with too many false targets, because func-tion pointers in the kernel often share the same type. Forexample, in Figure 3a, two function pointers read_iter andwrite_iter share the same function type. Type based pointeranalysis will even link write_iter to ext4_file_read_iter

falsely, which may lead to false permission check warnings.PeX addresses this problem with a new kernel-interface

aware indirect call analysis technique, detailed in §5.

4.2 The Lack of Full Permission Checks, Priv-ileged Functions, and Their Mappings

The second challenge lies in soundly enumerating a set ofpermission checks and inferring correct mappings betweenpermission checks and privileged functions in Linux kernel.

Though some commonly used permission checks forDAC, Capabilities, and LSM are known (Table 1), kerneldevelopers often devise custom permission checks (wrap-pers) that internally use basic permission checks. Unfor-tunately, the complete list of such permission checks hasnever been documented. For example, ns_capable is a com-monly used permission check for Capabilities, but it callsns_capable_common and security_capable in sequence. It isthe last security_capable that performs the actual capabilitycheck. In other words, all the others are “wrappers” of the“basic” permission check security_capable. This example

1SVF internally uses LLVM SparseVectors to save memory overhead byonly storing the set bits. However, it still blows up both the memory and thecomputation time due to the expensive insert, expand and merge operations.

shows how hard it is for a permission check analysis tool tokeep up with all permission checks.

To make matters worse, Linux kernel has no explicit docu-mentation that specifies which privileged function shouldbe protected by which permission checks. This is differ-ent from Android [2], which has been designed with thepermission-based security model in mind from the begin-ning. Take the Android LocationManager class as an example;for the getLastKnownLocation method, the API documentstates explicitly that permission ACCESS_COARSE_LOCATION orACCESS_FINE_LOCATION is required [7].

Unfortunately, existing static permission error checkingtechniques are not readily applicable in order to address theseproblems. Automated LSM hook verification [44] works onlywith clearly defined LSM hooks, which would miss manywrappers in the kernel setting. Many other tools require heavymanual efforts such as user-provided security rules [20, 56],authorization constraints [33], annotation on sensitive ob-jects [21]. These manual processes are particularly error-prone when applied to huge Linux code base. Alternatively,some works such as [18, 32] rely on dynamic analysis. How-ever, such run-time approaches may significantly limit thecode coverage being analyzed, thereby missing real bugs.

Moreover, all of above existing works cannot detect permis-sion checks soundly. Their inability to recognize permissionchecks or wrappers leads to missing privileged functions orfalse warnings for those that are indeed protected by wrappers.Since the huge Linux kernel code base makes it practicallyimpossible to review them all manually, reasoning about themapping is considered to be a daunting challenge.

In light of this, PeX presents a novel static analysis tech-nique that takes as input a small set of known permissionchecks to identify their basic permission checks and leveragesthem as a basis for finding other permission check wrappers(§6.2). In addition, PeX proposes a dominator analysis basedsolution to automatically infer the mappings between permis-sion checks and privileged functions (§6.3).

5 KIRIN Indirect Call Analysis

PeX proposes a precise and scalable indirect call analysistechnique, called KIRIN (Kernel InteRface based Indirectcall aNalysis), on top of the LLVM [27] framework. KIRINis inspired by two key observations: (1) almost all (95%)indirect calls in the Linux kernel are originated from ker-nel interfaces (§4.1) and (2) the type of a kernel interfaceis preserved both at its initialization site (where a functionpointer is defined) and at the indirect callsite (where a func-tion pointer is used) in LLVM IR. For example in Figure 3b,the kernel interface object ext4_file_operations of thetype struct file_operations is statically initialized whereext4_file_write_iter is assigned to the field of write_iter.For the indirect call site file→f_op→write_iter, one canidentify that f_op is of the type struct file_operations and

USENIX Association 28th USENIX Security Symposium 1209

Page 7: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

1 @ext4_file_operations = dso_local local_unnamed_addrconstant %struct.file_operations {,!

2 %struct.module* null,3 i64 (%struct.file*, i64, i32)* @ext4_llseek,4 i64 (%struct.file*, i8*, i64, i64*)* null,5 i64 (%struct.file*, i8*, i64, i64*)* null,6 i64 (%struct.kiocb*, %struct.iov_iter*)*

@ext4_file_read_iter,,!7 i64 (%struct.kiocb*, %struct.iov_iter*)*

@ext4_file_write_iter,,!

(a) LLVM IR of ext4_file_operations initialization.

1 %25 = load %struct.file_operations*,%struct.file_operations** %f_op, align 8,!

2 %write_iter.i.i = getelementptr inbounds%struct.file_operations,%struct.file_operations* %25, i64 0, i32 5

,!,!

3 %26 = load i64 (%struct.kiocb*, %struct.iov_iter*)*,i64 (%struct.kiocb*, %struct.iov_iter*)**%write_iter.i.i, align 8

,!,!

4 %call.i.i = call i64 %26(%struct.kiocb* nonnull%kiocb.i, %struct.iov_iter* nonnull %iter.i) #10,!

(b) LLVM IR of callsite file→f_op→write_iter in vfs_write.

Figure 4: Indirect callsite resolution for vfs_write.

infer that ext4_file_write_iter is one of potential call tar-gets. Based on this observation, PeX first collects indirect calltargets at kernel interface initialization sites (§5.1) and thenresolves them at indirect callsites (§5.2).

5.1 Indirect Call Target Collection

In Linux kernel, a kernel interface is often defined ina C struct comprised of function pointers (§4.1): e.g.,struct file_operations in Figure 3a. Many kernel inter-faces (C structs) are statically allocated and initializedas with ext4_file_operations and nfs_file_operations inFigure 3b. Some interfaces may be dynamically allocated andinitialized at run time for reconfiguration.

For the former, KIRIN scans all Linux kernel code linearlyto find all statically allocated and initialized struct objectswith function pointer fields. Then, for each struct object,KIRIN keep tracks of which function address is assigned towhich function pointers field using an offset as a key for thefield. For instance, Figure 4a shows the LLVM IR of staticallyinitialized ext4_file_operations. KIRIN finds that the ker-nel interface type is struct file_operations (Line 1), andext4_file_write_iter is assigned to the 5th field write_iter

(Line 7). Therefore, KIRIN figures out that write_iter maypoint to ext4_file_write_iter, not ext4_file_read_iter

(even though they have the same function type).

For the rest dynamically initialized kernel interfaces,KIRIN performs a data flow analysis to collect any assign-ment of a function address to the function pointer inside akernel interface. KIRIN’s field-sensitive analysis allows thecollected targets to be associated with the individual field ofa kernel interface.

1 struct usb_driver* driver =container_of(intf->dev.driver, structusb_driver, drvwrap.driver);

,!,!

2 retval = driver->unlocked_ioctl(intf,ctl->ioctl_code, buf);,!

(a) C code of a container_of usage, followed by an indirect call.

1 #define container_of(ptr, type, member) ({2 void *__mptr = (void *)(ptr);3 ((type *)(__mptr - offsetof(type, member))); })

\\4 %unlocked_ioctl = getelementptr inbounds i8*, i8**

%add.ptr76, i64 3,!

(b) Original container_of and the LLVM IR for the callsite.

1 #define container_of(ptr, type, member) ({2 type* __res;3 void* __mptr = ((void *)((void*)(ptr) -

offsetof(type, member)));,!4 memcpy(&__res, &__mptr, sizeof(void*));5 (__res);})

\\\\\

6 %unlocked_ioctl = getelementptr inbounds%struct.usb_driver, %struct.usb_driver* %20, i640, i32 3

,!,!

(c) Modified container_of and the LLVM IR for the callsite.

Figure 5: Fixing container_of missing struct type problem.

5.2 Indirect Callsite ResolutionKIRIN stores the result of the above first pass in a key-valuemap data structure in which the key is a pair of kernel interfacetype and an offset (a field), and the value is a set of calltargets. At each indirect callsite, KIRIN retrieves the type ofa kernel interface and the offset from LLVM IR, looks upthe map using them as a key, and figures out the matchedcall targets. For example, Figure 4b shows the LLVM IRsnippet in which an indirect call file→f_op→write_iter ismade inside of vfs_write. When an indirect call is made(Line 4), KIRIN finds that the kernel interface type is structfile_operations (Line 1) and the offset is 5 (Line 2). In thisway, KIRIN reports that ext4_file_write_iter (assigned atLine 7 in Figure 4a) is one of potential call targets that areindirectly called by dereferencing write_iter.

When applying KIRIN to Linux kernel, we found in cer-tain callsites, the kernel interface type is not presented in theLLVM IR, making their resolution impossible. For example,the macro container_of is commonly used in order to getthe starting address of a struct object by using a pointer toits own member field. Figure 5a shows an example of usingcontainer_of (Line 1). It calculates the starting address ofusb_driver through its own member drvwrap.driver. Basedon the address, the code at Line 2 makes an indirect call by us-ing a function pointer unlocked_ioctl that is another memberof the struct usb_driver object.

Figure 5b shows the original macro container_of (Lines1-3) and resulting LLVM IR (Line 4). The problem of thismacro is that it involves a pointer manipulation, the LLVMIR of which voids the struct type information, i.e., the sec-ond argument of the macro. To solve this problem, KIRINredefines container_of in a way that the struct type is pre-served in the LLVM IR (on which KIRIN works), as in Fig-ure 5c (Lines 1-5). This adds back the kernel interface type

1210 28th USENIX Security Symposium USENIX Association

Page 8: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

struct.usb_driver in the LLVM IR (Line 6), thereby en-abling KIRIN to infer the correct type of driver and resolvethe targets for unlocked_ioctl.

Our experiment (§7.2) shows that KIRIN resolves 92%of total indirect callsites for allyesconfig. PeX constructs amore sound (less missing edges) and precise (less false edges)call graph than other existing workarounds (e.g., [22]).

6 Design of PeXFigure 6 shows the architecture of PeX. It takes as inputkernel source code (in the LLVM bitcode format) and com-mon permission checks (Table 1), analyzes and reports alldetected permission check errors, including missing, incon-sistent, and redundant permission checks. In addition, PeXproduces the mapping of permission checks and privilegedfunctions, which has not been formally documented.

At a high-level, PeX first resolves indirect calls with ournew technique called KIRIN (§5). Next, PeX builds an aug-mented call graph—in which indirect callsites are connectedto possible targets—and cuts out only the portion reachablefrom user space (§6.1). Based on the partitioned call graph,PeX then generates the interprocedural control flow graph(ICFG) where each callsite is connected to the entry and theexit of the callee [17]. Then, starting from a small set of (user-provided) permission checks, PeX automatically detects theirwrappers (§6.2). After that, for a given permission check,PeX identifies its potentially privileged functions on top ofthe ICFG (§6.3), followed by a heuristic-based filter to pruneobviously non-privileged functions (§6.4). Finally, for eachprivileged function, PeX examines all user space reachablepaths to it to detect any permission checks error on the paths(§6.5). The following section describes these steps in detail.

6.1 Call Graph Generation and PartitionPeX generates the call graph leveraging the result of KIRIN(§5), and then partitions it into two groups.

User Space Reachable Functions: Starting from func-tions with the common prefix SyS_ (indicating system callentry points), PeX traverses the call graph, marks all visitedfunctions, and treats them as user space reachable functions.The user reachable functions in this partition are investigatedfor possible permission check errors.

Kernel Initialization Functions: Functions that are usedonly during booting are collected to detect redundant checks.The Linux kernel boots from the start_kernel function,and calls a list of functions with the common prefix __init.PeX performs multiple call graph traversals starting fromstart_kernel and each of the __init functions to collectthem.

Other functions such as IRQ handlers and kernel threadfunctions are not used in later analysis since they cannot bedirectly called from user space. The partitioned call graphserves as a basis for building an interprocedural control flow

graph (ICFG) [31] used in the inference of the mapping be-tween permission checks and privileged functions (§6.3).

6.2 Permission Check Wrapper DetectionSound and precise detection of permission check errors re-quires a complete list of permission checks, but they are notreadily available (§4.2). One may name some commonly usedpermission checks, as in Table 1. However, they are often thewrapper of basic permission checks, which actually performthe low-level access control, and even worse there could beother wrappers of the wrapper.

PeX solves this by automating the process of identifyingall permission checks including wrappers. PeX takes an in-complete list of user-provided permission checks as input.Starting from them, PeX detects basic permission checks, byperforming the forward call graph slicing [26, 37, 45] overthe augmented call graph. For a given permission check func-tion, PeX searches all call instructions inside the function forthe one that passes an argument of the function to the callee.In other words, PeX identifies the callees of the permissioncheck function which take its actual parameter as their ownformal parameter. Similarly, PeX then conducts backwardcall graph slicing [26, 37, 45] from these basic permissionchecks to detect the list of their wrappers. PeX refers to onlythose callers that pass permission parameters as wrappers,excluding other callers just using the permission checks.

Figure 7 shows an example of the permission check wrap-per detection. Given a known permission check ns_capable

(Lines 10-13), PeX first finds security_capable (Line 4) asa basic permission check, and then based on it, PeX detectsanother permission check wrapper has_ns_capability (Lines14-20). Note that the parameter cap is passed from both theparents ns_capable_common and has_ns_capability to thechild security_capable; and the result of security_capableis returned to them. Our evaluation (§7.3) shows that based on196 permission checks in Table 1, PeX detects 88 wrappers.

6.3 Privileged Function DetectionIt is important to understand the mappings between permis-sion checks and privileged functions for effective detectionof any permission check errors therein. However, the lack ofclear mapping in Linux kernel complicates the detection ofpermission check errors (§4.2).

To address this problem, PeX leverages an interproceduraldominator analysis [31] that can automatically identify theprivileged functions protected by a given permission check.PeX conservatively treats all targets (callees) of those callinstructions, that are dominated by each permission check(§6.2) on top of the ICFG (§6.1), as its potential privilegedfunctions. The rationale behind the dominator analysis isbased on the following observation: since there is no singlepath that allows the dominated call instruction to be reachedwithout visiting the dominator (i.e., the permission check),

USENIX Association 28th USENIX Security Symposium 1211

Page 9: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

KIRIN Indirect Call

Pointer Analysis (§5)

Call Graph Generation & Partitioning

(§6.1)

Privileged Function Detection

(§6.3)

Permission Check Wrapper

Detection (§6.2)

Non-privileged Function

Filter (§6.4)

Permission Check Error

Detection (§6.5)

Permission Checks

(Table 1)

KernelSource

(IR)

ICFG

all permission checks

potentialprivileged functions

privileged functions

pointertargets Permission

CheckErrors

Privileged Functions

PermissionChecks

Figure 6: PeX static analysis architecture. PeX takes as input kernel source code and permission checks, and reports as outputpermission check errors. PeX also produces mappings between identified permission checks and privileged functions as output.

1 static bool ns_capable_common(struct user_namespace*ns, int cap, bool audit),!

2 {3 ....4 capable = audit ?

security_capable(current_cred(), ns, cap) :,!5 security_capable_noaudit(current_cred(), ns,

cap);,!6 if (capable == 0)7 return true;8 return false;9 }

10 bool ns_capable(struct user_namespace *ns, int cap)11 {12 return ns_capable_common(ns, cap, true);13 }14 bool has_ns_capability(struct task_struct *t,15 struct user_namespace *ns, int cap)16 {17 ...18 ret = security_capable(__task_cred(t), ns, cap);19 ...20 }

Figure 7: Permission check wrapper examples.

Algorithm 1 Privileged Function DetectionINPUT:

pc f uncs - all permission checking functionsOUTPUT:

pv f uncs - privileged functions1: procedure PRIVILEGED FUNCTION DETECTION2: for f ← pc f uncs do3: for u←User( f ) do4: CallInst←CallInstDominatedBy(u) . Inter-procedural analysis, for

full program path5: callee← getCallee(CallInst)6: pv f uncs.insert(callee)7: end for8: end for9: return pv f uncs

10: end procedure

the callee is likely to be the one that should be protected bythe check on all paths 2.

Algorithm 1 shows how PeX uses the dominator analysisto find potential privileged functions pvfuncs for a given listof permission check functions pcfuncs. For each permissioncheck function f (Line 2), PeX finds all users of f, i.e., thecallsite invoking f (Line 3). For each user (callsite) u, PeXperforms interprocedural dominator analysis over the ICFG tofind all dominated call instructions (Line 4). All their calleesare then added to pvfuncs (Lines 5-6).

Note that the call graph generated by KIRIN (§5) hasresolved most of the indirect calls, which allows PeX to

2This does not necessarily mean that the permission check dominates allcall instructions of ICFG which invoke the resulting privileged function. Aslong as some call instructions are dominated by the check, their callees aretreated as privileged functions.

perform—on top of the resulting ICFG—more sound privi-leged function detection. For example, our experiment (§7.3)shows that KIRIN can identify ecryptfs_setxattr (reachablevia indirect calls over the ICFG) as a privileged function anddetect its missing permission check bug (Table 6, LSM-17).Note that if some other unsound workaround such as [22] hadbeen used, this bug could not have been detected.

6.4 Non-privileged Function FilterThe conservative approach in §6.3 may lead to too many po-tential privileged functions. In this step, PeX applies heuristic-based filters to prune out false privileged functions. In thecurrent prototype, the filter contains a set of kernel libraryfunctions which are not privileged functions, e.g., kmalloc,strcmp, kstrtoint. Though PeX is currently designed toavoid false negatives (and thus leverages a small set of libraryfilters only), one can add more aggressive filters to purge morefalse privileged functions. With releasing PeX, we expect agood opportunity for the kernel development community tocontribute to the design of non-privileged function filterswhere domain knowledge is helpful.

6.5 Permission Check Error DetectionThis last step is validating the use of privileged functions todetect any potential permission check errors. For a given map-ping between a permission check and a privileged function,PeX performs a backward traversal of the ICFG, starting fromthe privileged functions with the corresponding permissioncheck in mind. Note that PeX validates every possible path toeach privileged function of interest.

Algorithm 2 shows PeX’s permission check error detec-tion algorithm. Recall that PeX treats user reachable kernelfunctions and kernel initialization functions separately anddetects different forms of errors (§6.1). Lines 2-12 shows howPeX detects missing, redundant, and inconsistent checks inuser reachable kernel functions. For each privileged functionf (Line 5) in a mapping, PeX finds all possible paths allpathfrom user entry points to that privileged function f over theICFG (Line 6). Line 7-18 checks each path p for the precedingpermission check function, the lack of which should be re-ported as a bug. If the call to the privileged function (pvcall)is not preceded by the corresponding permission check func-

1212 28th USENIX Security Symposium USENIX Association

Page 10: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

Algorithm 2 Permission Check Error DetectionINPUT:

pc− pv - permission check function to privileged function mappingpc f uncs - all permission check functionskinit f uncs - kernel init functions

1: procedure PERMISSION CHECK ERROR DETECTION2: for pair← pc− pv do3: pv f uncs← pair.pv . privileged functions4: pc f unc← pair.pc . permission check functions5: for f ← pv f uncs do6: all path← getAllPathUseFunc( f ) . get all user reachable paths that

call the privileged function f7: for p← all path do8: pvcall← PrivilegeFunctionCallInPath(p)9: if pvcall not Preceded by pc f unc then

10: if pvcall not Preceded by any pc f uncs then11: report(p) . Report missing checks12: else13: report(p) . Report inconsistent check14: end if15: else if pvcall Preceded by multiple same pc f unc then16: report(p) . Report redundant checks17: end if18: end for19: end for20: end for21: for f ← kinit f uncs do22: if f uses any pc f uncs then23: report( f ) . Report unnecessary checks during kernel boot24: end if25: end for26: end procedure

tion (pcfunc) and any other check functions (those in pcfuncs)over a given path p, then PeX reports a missing check (Lines6-7). And if pvcall is preceded not by the correspondingcheck (pcfunc) but other check in pcfuncs, PeX reports aninconsistent check. Finally, if PeX discovers that pvcall isindeed preceded by pcfunc checks but multiple times, then itreports a redundant check (Lines 15-17). Besides, Lines 21-25shows how PeX detects redundant checks in kernel initial-ization functions. As kinitfuncs includes a conservative listof functions that can only be executed during booting (thusobviating the need of any checks), all detected permissionchecks are marked as redundant (Lines 22-24).

7 Implementation and Evaluation

PeX was implemented using LLVM [27]/Clang-6.0. It con-tains about 7K lines of C/C++ code. Clang was modified topreserve the kernel interface type at allocation/initializationsites by using an identified struct type instead of using un-named literal struct type. We also automated the generation ofthe single-file whole vmlinux LLVM bitcode vmlinux.bc us-ing wllvm [13]. This avoids building each kernel module sep-arately or changing kernel build infrastructures, as observedin prior kernel static analysis works [22, 49]. We evaluatedPeX on the latest stable Linux kernel v4.18.5. In summary,KIRIN resolves 86%–92% of indirect callsites depending onits compilation configurations. PeX reported 36 permissioncheck errors warnings to the Linux community, 14 of whichhave been confirmed as real bugs.

Table 2: Input Statistics for Kernel v4.18.5.

defconfig allyesconfig# of yes(=y) config 1284 9939# of compiled LOC 2,414,772 15,881,692vmlinux size 481 MB 3.8 GBvmlinux.bc size 387 MB 3.3 GB# of total functions 42,264 247,465# of syscall entries 857 1,027# of init functions 1,570 9,301# of indirect callsites (ICS) 20,338 115,537

Table 3: Indirect Call Pointer Analysis.

defconfig allyesconfigKIRIN TYPE KM KIRIN TYPE KM

% of ICS resolved 86 100 1 92 100 na# of avg target 3.6 10K 3.6 6.2 81K naanalysis time (min) 1 1 9,869 6.6 1 na

7.1 Evaluation MethodologyWe evaluated PeX with two different kernel configurations: (1)defconfig, the (commonly-used) default configuration, and(2) allyesconfig with all non-conflict configuration optionsenabled. The use of allyesconfig not only stress-tests PeX(including KIRIN) with a larger code base than defconfig,but also covers the majority of kernel code, allowing PeX todetect more bugs. In addition, we used 3 DAC, 3 Capabilities,and 190 LSM permission checks(Table 1) as input permissionchecks, from which PeX finds other wrappers. For the non-privileged function filter, we collected 1827 library functionsfrom lib directory in the kernel source code. All experimentswere carried out on a machine running Ubuntu 16.04 withtwo Intel Xeon E5-2650 2.20GHz CPU and 256GB DRAM.

7.2 Evaluation of KIRINWe compared the effectiveness and efficiency of KIRIN withtype-based approach and SVF-based K-Miner approach.

K-Miner [22] works around the scalability problem in SVFby analyzing the kernel on a per system call basis, rather thantaking the entire kernel code for analysis. K-Miner generatesa (small-size) partition of kernel code which can be reachedfrom a given system call, and (unsoundly) applies SVF for thatpartition. For comparison, we took K-Miner’s implementationfrom the github [6] and added the logic to count the number ofresolved indirect callsites and the average number of targetsper callsite. As K-Miner was originally built on LLVM/Clang-3.8.1, we recompiled the same kernel v4.18.5 using wllvm

with the same kernel configurations.Table 3 summaries evaluation results of KIRIN, compar-

ing it to the type-based approach and K-Miner approach interms of the percentage of indirect callsite (ICS) resolved, theaverage number of targets per ICS, and the total analysis time.

7.2.1 Resolution Rate

For K-Miner, we observe somewhat surprising results: it re-solves only 1% of all indirect callsites. After further inves-

USENIX Association 28th USENIX Security Symposium 1213

Page 11: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

tigation, we noticed that SVF runs on each partition whosecode base is smaller than the whole kernel, its analysis scopeis significantly limited and unable to resolve function pointersin other partitions, leading to the poor resolution rate.

Besides, we found out that K-Miner does not work forallyesconfig which contains a much larger code base thandefconfig. Note that K-Miner evaluated its approach only fordefconfig in the original paper [22]. The K-Miner approachturns out to be not scalable to handle allyesconfig whichends up encountering out of memory error even for analyzinga single system call.

7.2.2 Resolved Average Targets

For KIRIN, the number of average indirect call targets perresolved indirect callsite is much smaller than that of thetype-based approach as shown in the second row of Table 3.The reason is that the type-based approach classifies all func-tions (not only address-taken functions) into different setsbased on the function type. This implies that all functions inthe set are regarded as possible call targets of that functionpointer. For example, as shown in Figure 3a, two functionsext4_file_read_iter and ext4_file_write_iter share thesame type. As a result, the type-based approach incorrectlyidentifies both functions as possible call targets of the functionpointer f_ops→write_iter.

7.2.3 Analysis Time

The total analysis times of each ICS resolution approachare shown in the last row of Table 3. As expected, the type-based approach is the fastest, finishing analysis in 1 minutefor both configurations. KIRIN runs slower than the type-based approach. However, the analysis time of KIRIN (≈1minute) is comparable to that of the type-based approach fordefconfig, while KIRIN takes 6.6 minutes for allyesconfig.

For a fair comparison with K-Miner, care must be takenwhen we collect its indirect call analysis time. For a givensystem call, we measured K-Miner’s running time from thebeginning until it produces the SVF point-to result, whichdoes not include the later bug detection time. To obtain thetotal analysis time of K-Miner, we summed up the runningtimes of all system calls. The result shows that SVF based K-Miner takes about 9,869 minutes to finish analyzing all systemcalls of defconfig, which is much slower than KIRIN’s.

7.3 PeX ResultTable 4 summarizes PeX’s intermediate program analyses.As allyesconfig subsumes defconfig in static analysis, wefocus on discussing allyesconfig results here. Overall, PeXfinishes all analyses within a few hours and reports abouttwo thousand groups of warnings, which are classified byprivileged functions. One may implement a multi-threadedversion of PeX to further reduce the analysis time.

Given the small number of input DAC, CAP, and LSM per-mission checks (3, 3, and 190 each), PeX’s permission check

Table 4: PeX Results.

defconfig allyesconfigDAC CAP LSM DAC CAP LSM

# of input checks 3 3 190 3 3 190# of detected wrappers 11 13 34 19 16 53# of priv func detected 174 869 2030 631 3770 10915# of priv func after filter 116 582 1635 537 3245 10260# of warnings groupedby priv func 72 210 853 221 850 1017

total time (min) 6 8 11 83 247 169

Table 5: Comparison of PeX warnings when used with differ-ent indirect call analyses.

defconfig allyesconfigDAC CAP LSM Bugs DAC CAP LSM Bugs

KIRIN 72 210 853 21 221 850 1017 36TYPE 218 348 1319 21 164 964 4364 19 (PeX Timeout)KM 54 196 241 6 na na na na (SVF Timeout)

detection (§6.2) was able to identify 19, 16 and 53 permissioncheck wrappers. For example, PeX detects wrappers such asnfs_permission and may_open for DAC; sk_net_capable andnetlink_capable for Capabilities; and key_task_permission

and __ptrace_may_access for LSM.Table 4 also shows the number of potentially privileged

functions detected by PeX (§6.3) and the number of remain-ing privileged functions after kernel library filtering (§6.4).We found that there are typically 1-to-1 or 2-to-1 mappingbetween permission checks and privileged functions. Over-all, PeX reports 221, 850, and 1017 warnings (grouped byprivileged functions) for DAC, CAP, and LSM, respectively.

Table 6 shows the list of 36 bugs we reported, 14 of whichhave been confirmed by Linux kernel developers. Kernel de-velopers ignored some bugs and decided not to make changesbecause they believe that the bugs are not exploitable. Wediscuss them in detail in §7.5.

Comparison. To highlight the effectiveness of KIRIN,we repeated the end-to-end PeX analysis using type-based(PeX+TYPE) and K-Miner-style (PeX+KM) indirect call anal-yses. Table 5 shows the resulting number of warnings anddetected bugs when the 36 bugs— shown in Table 6—areused as an oracle for false negative comparison.

For allyesconfig, PeX+TYPE and PeX+KM could notcomplete the analysis within the 12-hour experiment limit.PeX+TYPE generated too many (false) edges in ICFG andsuffered from path explosion during the last phase of PeXanalysis; only 19 bugs were reported before the timeout. In themean time, PeX+KM timed out on an earlier pointer analysisphase, thereby failing to report any bug.

When defconfig is used for comparison, PeX+TYPE andPeX+KM were able to complete the analysis. In this setting,PeX+KIRIN (original) and PeX+TYPE both report 21 bugs(a subset of 36 bugs detected with allyesconfig). ThoughPeX+TYPE can capture them all (as type-based analysis is

1214 28th USENIX Security Symposium USENIX Association

Page 12: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

sound yet imprecise), it generates up to 3x more warnings,placing a high burden on the users side for their manual review.On the other hand, as an unsound solution, PeX+KM producesa limited number of warnings, resulting in the detection ofonly 6 bugs missing the rest.

7.4 Manual Review of WarningsThe manual review process of reported warnings is to deter-mine whether a privileged function identified by PeX (§6.3)is a true privileged function or not. As long as one can con-firm that a function is indeed privileged, reported warningsregarding its missing, inconsistent, and redundant permissionchecks should be true positives from PeX’s point of view.

Though kernel developers with domain knowledge may beable to discern them with no complication, we (as a third-party) try to understand whether a given function can be usedto access critical resources (e.g., device, file system, etc.). Asa result, we conservatively reported 36 bug warnings to thecommunity; we suspect that there could be more true warn-ings missed due to our lack of domain knowledge. We planto release PeX and the list of potential privileged functions,hoping kernel developers will contribute to identify privilegedfunctions and fix more true permission errors.

Certain static paths reported by PeX may not be feasibledynamically during program execution, resulting in false pos-itives. One may devise a solution solving path constraints asin symbolic execution engines [16] to address this problem,PeX currently does not do so.

7.5 Discussion of Security Bug Findings7.5.1 Missing Check

Figure 2b is one of the confirmed missing LSM checks (LSM-21). We discuss two more confirmed cases.

The CAP-4 missing check in kernel random device driveris particularly critical and triggered active discussion in thekernel developer community (including Torvalds). Randomnumber generator serves as the foundation of many cryp-tography libraries including OpenSSL, and thus the qualityof the random number is very critical. This security bug al-lows attackers to manipulate entropy pool, which can poten-tially corrupt many applications using cryptography libraries.Specifically, a problematic path starts from evdev_write andreaches the privileged function credit_entropy_bits, whichcan control the entropy in the entropy pool, while bypassingthe required CAP_SYS_ADMIN permission check.

The LSM-21 missing check in xfs_file_ioctl led to an-other interesting discussion among kernel developers [9].With this interface, a userspace program may perform low-level file system operations, but security_inode_read_linkLSM hook was missing. An adversary could exploit thisinterface and gain access to the whole file system that isnot allowed by LSM policy. Interestingly, however, the privi-leged function performed CAP_SYS_ADMIN Capability permis-

sion check. This created disagreement between kernel devel-opers: one group argues that the LSM hook is necessary, whileanother thinks that CAP_SYS_ADMIN is sufficient. We agree withthe former because LSM is designed to limit the damage ofa compromised process to the system, even the ones of rootuser [40]. We believe that LSM permission checks shouldstill be enforced as always for better security even when thecurrent user is root.

Kernel developers decided not to fix 9 of our reports be-cause they believe these bugs are not exploitable. As discussedearlier, PeX in the current form neither validates if a suspi-cious static path is dynamically reachable, nor generates aconcrete exploit to demonstrate the security issue; we be-lieve both are good future works. Nonetheless, we have onecomplaint to share.

For the LSM-19 and LSM-20 cases, PeX foundthat the LSM hooks security_kernel_read_file andsecurity_kernel_post_read_file were used to pro-tect the privileged functions kernel_read_file andkernel_post_read_file in some program paths. Wereported missing LSM hooks in load_elf_binary andload_elf_library for these privileged functions. However,the kernel developers responded that those hooks are usedto monitor loading firmware/kernel modules only (not otherfiles), and thus no patch is required. Here, the implication wefound is three-fold. First, the permission check names areambiguous and misleading. Second, we were not able to findany documentation of such LSM specification regarding theprotection of firmware/kernel modules. Last, PeX actuallyfound a counter-example in IMA where the same checks areindeed used for loading other files (neither firmware norkernel modules). Consequently, we suggest changing thefunction name and documenting the clear intention to avoidany confusion and to prevent system administrators fromcreating an LSM policy that does not work.

7.5.2 Inconsistent Check

The CAP-13 inconsistent check has been discussed in Fig-ure 1. One program path in Figures 1a and 1c has twoCAP_SYS_RAWIO checks and one CAP_SYS_ADMIN check, whileanother path in Figures 1b and 1c has only one CAP_SYS_ADMIN

check. PeX detects this bug as an inconsistent check, butfrom the viewpoint of correction, which requires addingCAP_SYS_RAWIO, this may also be viewed as a missing check.There is a separate redundant check error in CAP_SYS_RAWIO.

Upon further investigation, we were interested in learn-ing the practices in using multiple capabilities together.scsi_ioctl in Figure 1a checks both CAP_SYS_ADMIN andCAP_SYS_RAWIO. However, in a different network subsys-tem (not shown), we found that too_many_unix_fds per-forms a weaker permission check with the CAP_SYS_ADMIN orCAP_SYS_RAWIO condition. We believe this OR-based weakercheck is not a good practice because this in effect makesCAP_SYS_ADMIN too powerful (like root), diminishing the ben-

USENIX Association 28th USENIX Security Symposium 1215

Page 13: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

Table 6: Bugs Reported By PeX. Confirmed or Ignored.

Type-# File Function Description StatusDAC-1 fs/btrfs/send.c btrfs_send missing DAC check when traversing a snapshot CDAC-2 fs/ecryptfs/inode.c ecryptfs_removexattr(),_setxattr() missing xattr_permission() CDAC-3 fs/ecryptfs/inode.c ecryptfs_listxattr() missing xattr_permission() CCAP-4 drivers/char/random.c write_pool(), credit_entropy_bits() missing CAP_SYS_ADMIN CCAP-5 drivers/scsi/sg.c sg_scsi_ioctl() missing CAP_SYS_ADMIN or CAP_RAW_IO ICAP-6 drivers/block/pktcdvd.c add_store(), remove_store() missing CAP_SYS_ADMIN ICAP-7 drivers/char/nvram.c nvram_write() missing CAP_SYS_ADMIN ICAP-8 drivers/firmware/efi/efivars.c efivar_entry_set() missing CAP_SYS_ADMIN CCAP-9 net/rfkill/core.c rfkill_set_block(), rfkill_fop_write() missing CAP_NET_ADMIN CCAP-10 block/scsi_ioctl.c mmc_rpmb_ioctl() missing verify_command or CAP_SYS_ADMIN ICAP-11 drivers/platform/x86/thinkpad_acpi.c acpi_evalf() missing CAP_SYS_ADMIN ICAP-12 drivers/md/dm.c dm_blk_ioctl() missing CAP_RAW_IO ICAP-13 block/bsg.c bsg_ioctl inconsistent/missing CAP_SYS_ADMIN CCAP-14 kernel/sys.c prctl_set_mm_exe_file inconsistent capability check ICAP-15 kernel/sys.c prctl_set_mm_exe_file inconsistent capability and namespace check ICAP-16 block/scsi_ioctl.c blk_verify_command redundant check CAP_SYS_RAWIO ILSM-17 fs/ecryptfs/inode.c ecryptfs_removexattr(), _setxattr() missing security_inode_removexattr() CLSM-18 mm/mmap.c remap_file_pages missing security_mmap_file() ILSM-19 fs/binfmt_elf.c load_elf_binary() missing security_kernel_read_file ILSM-20 fs/binfmt_elf.c load_elf_library() missing security_kernel_read_file ILSM-21 fs/xfs/xfs_ioctl.c xfs_file_ioctl() missing security_inode_readlink() CLSM-22 kernel/workqueue.c wq_nice_store() missing security_task_setnice() CLSM-23 fs/ecryptfs/inode.c ecryptfs_listxattr() missing security_inode_listxattr CLSM-24 include/linux/sched.h comm_write() missing security_task_prctl() CLSM-25 fs/binfmt_misc.c load_elf_binary() missing security_bprm_set_creds() ILSM-26 drivers/android/binder.c binder_set_nice missing security_task_setnice() ILSM-27 fs/ocfs2/cluster/tcp.c o2net_start_listening() missing security_socket_bind ILSM-28 fs/ocfs2/cluster/tcp.c o2net_start_listening() missing security_socket_listen ILSM-29 fs/dlm/lowcomms.c tcp_create_listen_sock missing security_socket_bind ILSM-30 fs/dlm/lowcomms.c tcp_create_listen_sock missing security_socket_listen ILSM-31 fs/dlm/lowcomms.c sctp_listen_for_all missing security_socket_listen ILSM-32 net/socket.c kernel_bind missing security_socket_bind ILSM-33 net/socket.c kernel_listen missing security_socket_listen ILSM-34 net/socket.c kernel_connect missing security_socket_connect ILSM-35 fs/ocfs2/cluster/tcp.c o2net_start_listening() redundant security_socket_create CLSM-36 fs/ocfs2/cluster/tcp.c o2net_open_listening_sock() redundant security_socket_create C

efit of fine-grained capability-based access control.The CAP-14 and CAP-15 inconsistent error reports were

acknowledged but ignored by the kernel developers forthe following reason. For the same privileged functionprctl_set_mm_exe_file, which is used to set an executablefile, PeX discovered one case requiring CAP_SYS_RESOURCE inuser namespace, and another case checking CAP_SYS_ADMIN ininit namespace. Kernel developers responded that each caseis fine by design for that specific context. PeX does not con-sider the precise context in which prctl_set_mm_exe_file isused (similar to aforementioned security_kernel_read_file

used for loading kernel modules), leading to an imprecisereport, but we believe that both CAP-14 and CAP-15 areworthwhile for further investigation.

7.5.3 Redundant Check

A redundant check occurs in two forms. First, for user-reachable functions, it happens when a privileged function iscovered by the same permission checks multiple times. Wereported three cases. The CAP-16 case was discussed in Fig-ures 1a and 1c with two CAP_SYS_RAWIO checks, which wasignored by kernel developers. On the other hand, for the LSM-35 and LSM-36 cases found in the ocfs2 file system, the otherkernel developer group confirmed and promised to fix thebugs. Second, any permission check in kernel-initialization

functions is marked as redundant because the boot thread isexecuted under root. PeX detected tens of such cases, but wedid not report them as they are less critical.

8 Related Work

8.1 Hook Verification and Placement

There is a series of studies on checking kernel LSM hooks.Automated LSM hook verification work [56] verifies the com-plete mediation of LSM hooks relying manually specifiedsecurity rules. While [20] automates LSM hook placementsutilizing manually written specification of security sensitiveoperations. However, required manual processes are error-prone when applied to huge Linux code base. Edwards etal. [18] proposed to use dynamic analysis to detect LSMhook inconsistencies. While PeX is using static analysis, canachieve better code coverage.

AutoISES [44] is the most closely related work to PeX.AutoISES regards data structures, such as the structure fieldsand global variables, as privileged, applies static analysis toextract security check usage patterns, and validates the pro-tections to these data structures. The difference between Au-toISES and PeX is three-fold. First, PeX is privileged functionoriented while AutoISES is more like data structure oriented.

1216 28th USENIX Security Symposium USENIX Association

Page 14: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

Second, AutoISES is designed for LSM only, whose permis-sion checks (hooks) are clearly defined, and therefore it isnot applicable to DAC and Capabilities due to their variouspermission check wrappers. In contrast, PeX works for allthree types of permission checks. Third, AutoISES uses type-based pointer analysis to resolve indirect calls, while PeXuses KIRIN to resolve indirect calls in a more precise manner.

There are also works [21, 32, 33] that extend authorizationhook analysis to user space programs, including X server andpostgresql. However, their approaches canot be applied to thehuge kernel scale. Moreover, all of above works either do notanalyze indirect calls at all, or apply over approximate indirectcall analysis techniques, such as type-based approach or fieldinsensitive approach. To the contrary, PeX uses KIRIN, aprecise and scalable indirect call analysis technique, whichcan also benefit these works by finding more accurate indirectcall targets.

8.2 Kernel Static Analysis Tools

Coccinelle [34] is a tool that detects a bug of pre-definedpattern based on text pattern matching on the symbolic rep-resentation of bug cases. This is basically intra-proceduralanalysis. Building upon Coccinelle, Wang et al. proposedanother pattern matching based static tool which detects po-tential double-fetch vulnerabilities in the Linux kernel [48].

Sparse [11] is designed to detect the problematic use ofpointers belonging to different address space (kernel spaceor userspace). Later, Sparse was used to build a static anal-ysis framework called Smatch [10] for detecting differentsorts of kernel bugs. However, Smatch is also based on intra-procedural analysis, thus it can only find shallow bugs.

Double-Fetch [52], Check-it-again [49] focus on detect-ing time of check to time of use (TOCTTOU) bugs. Dr.Checker [29] is designed for analyzing Linux kernel drivers.It adopts the modular design, allowing new bug detectors tobe plug-in easily. KINT [50] applies taint analysis to detectinteger errors in Linux kernel while UniSan [28] leveragesthe same analysis to detect uninitialized kernel memory leak-ages to the userspace. Chucky [53] also uses a taint analysisto analyze missing checks in different sources in userspaceprograms and Linux kernel. However, Chucky can handleonly kernel file system code due to the lack of pointer analy-sis. Note that to resolve indirect call targets, all these worksleverage a type-based approach, which is not as accurate asKIRIN, thus suffering from false positives.

MECA [54] is an annotation based static analysis frame-work, and it can detect security rule violations in Linux.APISan [55] aims at finding API misuse. It figures out theright API usage through the analysis of existing code base andperforms intra-procedural analysis to find bugs. To achieve theformer, APISan relies on relaxed symbolic execution whichis complementary to the techniques used in PeX.

8.3 Permission Check Analysis ToolsEngler et al. propose to use programmer beliefs to automati-cally extract checking information from the source code. Theyapply the checking information to detect missing checks [19].RoleCast [42] leverages software engineering patterns to de-tect missing security checks in web applications. TESLA [14]implements temporal assertions based on LLVM instrument,in which the FreeBSD hooks are checked by inserted asser-tions dynamically. Different from TESLA, PeX uses KIRINto analyze jump targets of all kernel function pointers stat-ically, achieving better resolution rate and code coverage.JIGSAW [47] is a system that can automatically derive pro-grammer expectations on resources access and enforce it onthe deployment. It is designed for analyzing userspace pro-grams, cannot be applied to kernel directly.

JUXTA [30] is a tool designed for detecting semanticbugs in filesystem while PScout [15] is a static analysistool for validating Android permission checking mechanisms.Kratos [39] is a static security check framework designed forthe Android framework. It builds a call graph using LLVMand tries to discover inconsistent check paths in the frame-work. However, Android has well-documented permissioncheck specifications [2], i.e., privileged functions and the per-mission required for them are both clearly defined. In contrast,the Linux kernel has no such documentation, which makesit impossible to apply PScout and Kratos to Linux kernelpermission checks.

9 ConclusionThis paper presents PeX, a static permission check analysisframework for Linux kernel, which can automatically infermappings between permission checks and privileged func-tions as well as detect missing, inconsistent, and redundantpermission checks for any privileged functions. PeX relies onKIRIN, our novel call graph analysis based on kernel inter-faces, to resolve indirect calls precisely and efficiently.

We evaluated both KIRIN and PeX for the latest stableLinux kernel v4.18.5. The experiments show that KIRIN canresolve 86%-92% of all indirect callsites in the kernel within7 minutes. In particular, PeX reported 36 permission checkbugs of DAC, Capabilities, and LSM, 14 of which have al-ready been confirmed by the kernel developers. PeX sourcecode is available at https://github.com/lzto/pex, along withthe identified mapping between permission checks and privi-leged functions. We believe that such a mapping allows kerneldevelopers to validate their code with PeX and encouragesthem to contribute to PeX by refining the mapping with theirdomain knowledge.

AcknowledgmentsThe authors would like to thank anonymous reviewers for theirinsightful comments. This research is partially supported bythe NSF under Grant No. CSR-1814430 and CSR-1750503.

USENIX Association 28th USENIX Security Symposium 1217

Page 15: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

References[1] alse boundaries and arbitrary code execution.

https://forums.grsecurity.net/viewtopic.php?f=7&t=2522.

[2] Android Permission Overview. https://developer.android.com/guide/topics/permissions/overview.

[3] Apparmor. https://gitlab.com/apparmor/apparmor/wikis/home/.

[4] capabilities - overview of linux capabilities.http://man7.org/linux/man-pages/man7/capabilities.7.html.

[5] CAP_SYS_ADMIN: the new root. https://lwn.net/Articles/486306/.

[6] K-miner: Data-flow analysis for the linux kernel. https://github.com/ssl-tud/k-miner.

[7] Locationmanager. https://developer.android.com/reference/android/location/LocationManager#getLastKnownLocation(java.lang.String).

[8] Mandatory access control. https://en.wikipedia.org/wiki/Mandatory_access_control.

[9] Re: Leaking path in xfs’s ioctl interface(missing lsmcheck) by stephen smalley. https://lkml.org/lkml/2018/9/26/668.

[10] Smatch: pluggable static analysis for c. https://lwn.net/Articles/691882/.

[11] Sparse. https://www.kernel.org/doc/html/v4.14/dev-tools/sparse.html.

[12] Virtual file system. https://en.wikipedia.org/wiki/Virtual_file_system.

[13] Whole Program LLVM: a wrapper script to build whole-program llvm bitcode files. https://github.com/travitch/whole-program-llvm.

[14] Jonathan Anderson, Robert NM Watson, David Chis-nall, Khilan Gudka, Ilias Marinos, and Brooks Davis.Tesla: temporally enhanced system logic assertions. InProceedings of the Ninth European Conference on Com-puter Systems, page 19. ACM, 2014.

[15] Kathy Wain Yee Au, Yi Fan Zhou, Zhen Huang, andDavid Lie. Pscout: analyzing the android permissionspecification. In Proceedings of the 2012 ACM confer-ence on Computer and communications security, pages217–228. ACM, 2012.

[16] Cristian Cadar, Daniel Dunbar, Dawson R Engler, et al.Klee: Unassisted and automatic generation of high-coverage tests for complex systems programs. In OSDI,volume 8, pages 209–224, 2008.

[17] Evelyn Duesterwald, Rajiv Gupta, and Mary Lou Soffa.Demand-driven computation of interprocedural dataflow. In Proceedings of the 22nd ACM SIGPLAN-SIGACT symposium on Principles of programming lan-guages, pages 37–48. ACM, 1995.

[18] Antony Edwards, Trent Jaeger, and Xiaolan Zhang. Run-time verification of authorization hook placement forthe linux security modules framework. In Proceedingsof the 9th ACM Conference on Computer and Commu-nications Security, pages 225–234. ACM, 2002.

[19] Dawson Engler, David Yu Chen, Seth Hallem, AndyChou, and Benjamin Chelf. Bugs as deviant behavior:A general approach to inferring errors in systems code.In ACM SIGOPS Operating Systems Review, volume 35,pages 57–72. ACM, 2001.

[20] Vinod Ganapathy, Trent Jaeger, and Somesh Jha. Au-tomatic placement of authorization hooks in the linuxsecurity modules framework. In Proceedings of the 12thACM conference on Computer and communications se-curity, pages 330–339. ACM, 2005.

[21] Vinod Ganapathy, Trent Jaeger, and Somesh Jha. To-wards automated authorization policy enforcement. InProceedings of Second Annual Security Enhanced LinuxSymposium. Citeseer, 2006.

[22] David Gens, Simon Schmitt, Lucas Davi, and Ahmad-Reza Sadeghi. K-miner: Uncovering memory corruptionin linux. In Proceedings of the 2018 Annual Networkand Distributed System Security Symposium (NDSS),San Diego, CA, 2018.

[23] Ben Hardekopf and Calvin Lin. The ant and thegrasshopper: fast and accurate pointer analysis for mil-lions of lines of code. In ACM SIGPLAN Notices, vol-ume 42, pages 290–299. ACM, 2007.

[24] Ben Hardekopf and Calvin Lin. Exploiting pointer andlocation equivalence to optimize pointer analysis. pages265–280, 2007.

[25] Ben Hardekopf and Calvin Lin. Flow-sensitive pointeranalysis for millions of lines of code. In Proceedingsof the 9th Annual IEEE/ACM International Symposiumon Code Generation and Optimization, pages 289–298.IEEE Computer Society, 2011.

[26] Bogdan Korel and Juergen Rilling. Program slicingin understanding of large programs. In Program Com-prehension, 1998. IWPC’98. Proceedings., 6th Interna-tional Workshop on, pages 145–152. IEEE, 1998.

1218 28th USENIX Security Symposium USENIX Association

Page 16: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

[27] Chris Lattner and Vikram Adve. Llvm: A compilationframework for lifelong program analysis & transforma-tion. In Proceedings of the International Symposium onCode Generation and Optimization: Feedback-directedand Runtime Optimization, CGO ’04, pages 75–, 2004.

[28] Kangjie Lu, Chengyu Song, Taesoo Kim, and WenkeLee. Unisan: Proactive kernel memory initialization toeliminate data leakages. In Proceedings of the 2016ACM SIGSAC Conference on Computer and Communi-cations Security, pages 920–932. ACM, 2016.

[29] Aravind Machiry, Chad Spensky, Jake Corina, NickStephens, Christopher Kruegel, and Giovanni Vigna. Dr.checker: A soundy analysis for linux kernel drivers. In26th {USENIX} Security Symposium ({USENIX} Secu-rity 17), pages 1007–1024. USENIX Association, 2017.

[30] Changwoo Min, Sanidhya Kashyap, Byoungyoung Lee,Chengyu Song, and Taesoo Kim. Cross-checking se-mantic correctness: The case of finding file system bugs.In Proceedings of the 25th Symposium on OperatingSystems Principles, pages 361–377. ACM, 2015.

[31] S.S. Muchnick. Advanced Compiler Design Implemen-tation. Morgan Kaufmann Publishers, 1997.

[32] Divya Muthukumaran, Trent Jaeger, and Vinod Ganapa-thy. Leveraging choice to automate authorization hookplacement. In Proceedings of the 2012 ACM confer-ence on Computer and communications security, pages145–156. ACM, 2012.

[33] Divya Muthukumaran, Nirupama Talele, Trent Jaeger,and Gang Tan. Producing hook placements to enforceexpected access control policies. In International Sym-posium on Engineering Secure Software and Systems,pages 178–195. Springer, 2015.

[34] Yoann Padioleau, Julia Lawall, René Rydhof Hansen,and Gilles Muller. Documenting and automating collat-eral evolutions in linux device drivers. In Acm sigopsoperating systems review, volume 42, pages 247–260.ACM, 2008.

[35] Fernando Magno Quintao Pereira and Daniel Berlin.Wave propagation and deep propagation for pointeranalysis. In Code Generation and Optimization, 2009.CGO 2009. International Symposium on, pages 126–135. IEEE, 2009.

[36] Lili Qiu, Yin Zhang, Feng Wang, Mi Kyung, andHan Ratul Mahajan. Trusted computer system eval-uation criteria. In National Computer Security Center.Citeseer, 1985.

[37] Sanjay Rawat, Laurent Mounier, and Marie-Laure Potet.Listt: An investigation into unsound-incomplete yetpractical result yielding static taintflow analysis. InAvailability, Reliability and Security (ARES), 2014 NinthInternational Conference on, pages 498–505. IEEE,2014.

[38] Ravi S Sandhu and Pierangela Samarati. Access control:principle and practice. IEEE communications magazine,32(9):40–48, 1994.

[39] Yuru Shao, Qi Alfred Chen, Zhuoqing Morley Mao,Jason Ott, and Zhiyun Qian. Kratos: Discovering in-consistent security policy enforcement in the androidframework. In NDSS, 2016.

[40] Stephen Smalley and Robert Craig. Security enhanced(se) android: Bringing flexible mac to android. In NDSS,volume 310, pages 20–38, 2013.

[41] Stephen Smalley, Chris Vance, and Wayne Salamon. Im-plementing selinux as a linux security module. NAILabs Report, 1(43):139, 2001.

[42] Sooel Son, Kathryn S McKinley, and Vitaly Shmatikov.Rolecast: finding missing security checks when you donot know what checks are. In ACM SIGPLAN Notices,volume 46, pages 1069–1084. ACM, 2011.

[43] Yulei Sui and Jingling Xue. Svf: interprocedural staticvalue-flow analysis in llvm. In Proceedings of the 25thInternational Conference on Compiler Construction,pages 265–266. ACM, 2016.

[44] Lin Tan, Xiaolan Zhang, Xiao Ma, Weiwei Xiong, andYuanyuan Zhou. Autoises: Automatically inferring se-curity specification and detecting violations. In USENIXSecurity Symposium, pages 379–394, 2008.

[45] Frank Tip. A survey of program slicing techniques. Cen-trum voor Wiskunde en Informatica, 1994.

[46] National Computer Security Center (US). A guide tounderstanding discretionary access control in trustedsystems, volume 3. National Computer Security Center,1987.

[47] Hayawardh Vijayakumar, Xinyang Ge, Mathias Payer,and Trent Jaeger. Jigsaw: Protecting resource access byinferring programmer expectations. In USENIX SecuritySymposium, pages 973–988, 2014.

[48] Pengfei Wang, Jens Krinke, Kai Lu, Gen Li, and SteveDodier-Lazaro. How double-fetch situations turn intodouble-fetch vulnerabilities: A study of double fetchesin the linux kernel. In USENIX Security Symposium,2017.

USENIX Association 28th USENIX Security Symposium 1219

Page 17: PeX: A Permission Check Analysis Framework for Linux KernelLinux, inode_permission (as listed in Table1) is often used to check the permissions of the current process on a given inode

[49] Wenwen Wang, Kangjie Lu, and Pen-Chung Yew. Checkit again: Detecting lacking-recheck bugs in os kernels.In Proceedings of ACM conference on Computer andcommunications security. ACM, 2018.

[50] Xi Wang, Haogang Chen, Zhihao Jia, Nickolai Zel-dovich, and M Frans Kaashoek. Improving integer secu-rity for systems with kint. In OSDI, volume 12, pages163–177, 2012.

[51] Chris Wright, Crispin Cowan, James Morris, StephenSmalley, and Greg Kroah-Hartman. Linux security mod-ule framework. In Ottawa Linux Symposium, volume8032, pages 6–16, 2002.

[52] Meng Xu, Chenxiong Qian, Kangjie Lu, MichaelBackes, and Taesoo Kim. Precise and scalable detectionof double-fetch bugs in os kernels. 2018.

[53] Fabian Yamaguchi, Christian Wressnegger, Hugo Gas-con, and Konrad Rieck. Chucky: Exposing missingchecks in source code for vulnerability discovery. In

Proceedings of the 2013 ACM SIGSAC conference onComputer & communications security, pages 499–510.ACM, 2013.

[54] Junfeng Yang, Ted Kremenek, Yichen Xie, and DawsonEngler. Meca: an extensible, expressive system andlanguage for statically checking security properties. InProceedings of the 10th ACM conference on Computerand communications security, pages 321–334. ACM,2003.

[55] Insu Yun, Changwoo Min, Xujie Si, Yeongjin Jang, Tae-soo Kim, and Mayur Naik. Apisan: Sanitizing api usagesthrough semantic cross-checking. In USENIX SecuritySymposium, pages 363–378, 2016.

[56] Xiaolan Zhang, Antony Edwards, and Trent Jaeger. Us-ing cqual for static analysis of authorization hook place-ment. In USENIX Security Symposium, pages 33–48,

2002.

1220 28th USENIX Security Symposium USENIX Association