There exists an integer overflow in kqueue syscall which can lead to kernel deadlock(Or crash on some devices) on latest macOS and iOS. kqueue in BSD is just like epoll in Linux which powers IO multiplexing. It means users can monitor a set of actions of file descriptors, like read or write available. At the same time, an object of kqueue in kernel is also a file descriptor in user space, so users can monitor a file descriptor of kqueue object by kqueue. The kqueues which monitor other objects are called parent kqueue, so we can name the kqueue to be monitored as child kqueue, it is fine until now because they are both file descriptors. The problem is that the user can make a circular kqueue which means the two kqueues can monitor each other.
Lets look at the source. The function associated with this issue lies in bsd/kern/kern_event.c named kqueue_kqfilter. This function is called when the child kqueue is attached to the list of parent kqueue. This is a piece of comment in this function.
1 |
/* |
It seems fine because the level of child will always be lower
than the parent. But the level field of kqueue is just a 16
bit unsigned integer which means we can overflow it easily.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* kqueue - common core definition of a kqueue
*
* No real structures are allocated of this type. They are
* either kqfile objects or kqworkq objects - each of which is
* derived from this definition.
*/
struct kqueue {
struct waitq_set kq_wqs; /* private waitq set */
lck_spin_t kq_lock; /* kqueue lock */
uint16_t kq_state; /* state of the kq */
uint16_t kq_level; /* nesting level of the kq */
uint32_t kq_count; /* number of queued events */
struct proc *kq_p; /* process containing kqueue */
struct kqtailq kq_queue[1]; /* variable array of kqtailq structs */
};
1 |
|
Line 16 is the root cause of this integer overflow. When the kq->kq_level is 65535 and parentkq->kq_level is 2(Changed to 2 when it is 0), the parentkq->kq_level will be set to 0. When we do this in the opposite way, it can bypass the condition of the if statement.
Download Proof of concept.How to reproduce:
- clang circular_queue.c -o circular_queue
- ./circular_queue
After that you can see a kernel crash or kernel deadlock.
Information about my device(sw_vers):
ProductName: Mac OS X
ProductVersion: 10.13.3
BuildVersion: 17D47
This poc is also worked on my iOS 11.0.2 of iPhone 8 Plus, I believe it can also work on latest iOS 11.2.5.
Update on July 5, 2018: This issue still exists on latest macOS 10.13.5 and iOS 11.4.