In this category, the variable that is checked is not the variable that is used subsequently. There is, however, some sort of mapping between the checked variable and the used variable (e.g. the used variable is a field of the checked variable). Therefore, it is easy to obtain the checked variable from the passed variable and vice versa.
These type errors are subject to TOCTTOU [2] attacks, because the mapping between the checked variable and the used variable might change during the course of execution. Whether the vulnerability is exploitable depends on whether the user can manipulate the mapping without special privilege. At least one of the type errors that we found is exploitable, as we demonstrate below.
Figure 8 shows the code path that contains the type error. The code sequence shows Linux implementation of file locking via the fcntl system call. In function sys_fcntl() , the variable filp , which is a pointer to the file structure and is retrieved via the file descriptor fd , is checked by the security_ops->file_ops->fcntl(filp, ...) hook. However, after the check, the file descriptor fd , instead of the checked variable filp , is passed to the intermediate function do_fcntl(fd, ...) and eventually to the worker function fcntl_getlk(fd, ...) , where the filp is retrieved again with the given fd .
This double retrieval of the file pointer creates a race condition and can be exploited as follows. A user can have the security_ops->file_ops->fcntl(filp) authorization performed on a different file to the one that is eventually locked. Figure 9 shows the exploit.
Note that although step (7) is written as a whole system call, there
is actually only one line of C (an assignment) in step (7) that needs
to come between (6) and (8). Since step (6) does a
get_user
, the attacker can cause their own program to page fault which enables
step (7) to be performed before (8).
Also note that non-LSM Linux is not vulnerable since the validation in fcntl_setlk is done after the second lookup. LSM is vulnerable because the only authorization that protects the operation is performed before the second lookup.
As an example of how dangerous this can be, login and su (PAM'd versions) both try to lock the file /var/run/utmp (world readable). insmod locks any modules it loads.
A patch that fixes this problem was posted to the LSM mailing list [5].
The remaining type errors in this category involve kernel data structures that cannot be easily modified by users via system calls. As a result, it is unclear whether these type errors can lead to exploits. However, it certainly complicates the code unnecessarily and increases the chance of race conditions when the data structures are not properly synchronized, which may result in potential exploits.
Here we present a type error of this kind. Many security checks that intend to protect the inode structure are performed on the dentry data structure. For example, the following code does the permission check on the dentry structure, but does the ``set attribute'' operation on the inode structure.
/* from fs/attr.c */ ... security_ops->inode_ops ->setattr(dentry, attr); ... inode = dentry->d_inode; inode_setattr(inode, attr); ...
It is also quite common in Linux to check on the file data structure and operate on the inode data structure.