In this blog, we will share an integer overflow vulnerability in the MPTCP module in the XNU kernel.
When we started to study MPTCP, we got a very brief description from the official document:
“MPTCP is a set of extensions to the Transmission Control Protocol (TCP) specification. With MPTCP, a client can connect to the same destination host with multiple connections over different network adapters”.
Now a natural question comes into our mind: how many connections can a client connect to a host at most? With this question in mind, we created a simple test program that simply creates an MPTCP socket and connects to a host many times. Our purpose is to figure out when we cannot create new connections.
The test program ran fine. However, the surprise thing was that we triggered a kernel panic when the test program exited.
The test program was so simple that we had no clue about what triggered the panic. After analyzing the panic log, we realized that our program triggered a recursive kernel function and resulted in a kernel stack exhaustion when the MPTCP socket was closed. Note that the recursive function panic was also already fixed. You won’t be able to trigger it on iOS 13.
We continued our testing process. Now, we turned to the XNU source code. We quickly found the following data structure.
struct mptses {
struct mppcb *mpte_mppcb; /* back ptr to multipath PCB */
struct mptcb *mpte_mptcb; /* ptr to MPTCP PCB */
TAILQ_HEAD(, mptopt) mpte_sopts; /* list of socket options */
TAILQ_HEAD(, mptsub) mpte_subflows; /* list of subflows */
uint16_t mpte_numflows; /* # of subflows in list */
uint16_t mpte_nummpcapflows; /* # of MP_CAP subflows */
sae_associd_t mpte_associd; /* MPTCP association ID */
sae_connid_t mpte_connid_last; /* last used connection ID */
...
mptses
represents MPTCP sessions. Every time a new connection is created between a client and a host, there will be a new mpte_subflow
created. mptses->mpte_numflows
records the number of subflows.
static void
mptcp_subflow_attach(struct mptses *mpte, struct mptsub *mpts, struct socket *so)
{
struct socket *mp_so = mpte->mpte_mppcb->mpp_socket;
struct tcpcb *tp = sototcpcb(so);
...
/*
* Insert the subflow into the list, and associate the MPTCP PCB
* as well as the the subflow socket. From this point on, removing
* the subflow needs to be done via mptcp_subflow_del().
*/
TAILQ_INSERT_TAIL(&mpte->mpte_subflows, mpts, mpts_entry);
mpte->mpte_numflows++; //<====== no integer overflow checks
As we can see in function mptcp_subflow_attach
, creating a new connection will increase mpte->mpte_numflows
by one, but there is no integer overflow checks at all.
You may also notice that, mpte_numflows
is in the type of uint16_t, which means its maximum value is 0xFFFF! So what if we create 0xFFFF+2 connections? The answer is that mpte_numflows
will wrap to 1!
So far, the integer overflow doesn’t cause any memory errors. We continued to check how mpte_numflows
would be used. Just by greping mpte_numflows, we got the following sysctl handler: mptcp_pcblist
static int
mptcp_pcblist SYSCTL_HANDLER_ARGS
{
...
TAILQ_FOREACH(mpp, &mtcbinfo.mppi_pcbs, mpp_entry) {
flows = NULL;
socket_lock(mpp->mpp_socket, 1);
VERIFY(mpp->mpp_flags & MPP_ATTACHED);
mpte = mptompte(mpp);
...
mptcpci.mptcpci_nflows = mpte->mpte_numflows;
...
len = sizeof(*flows) * mpte->mpte_numflows;
if (mpte->mpte_numflows != 0) {
flows = _MALLOC(len, M_TEMP, M_WAITOK | M_ZERO);
//<=== alloc memory according to mpte->mpte_numflows
...
f = 0;
TAILQ_FOREACH(mpts, &mpte->mpte_subflows, mpts_entry) {
so = mpts->mpts_socket;
fill_mptcp_subflow(so, &flows[f], mpts);
<== dump the list into flows buffer. HEAP OVERFLOW!
f++;
In function mptcp_pcblist
, mpte_numflows
is used to calculate the length of a temp buffer. If we already make mpte_numflows
wrapped to 1, the allocation site will only allocate ONE entry. However, mptcp_pcblist
will traverse the list mpte_subflows
and dump all the entries into the allocated buffer. Heap overflow happens!
We won’t get into the exploitation phase. With partially controlled values and partially controlled length, the exploitation would be also very interesting.
Fixing the issue is quite easy. The patch is as follows. mptcp_subflow_add
function now adds a limitation to mpte_numflows
.

Do you still remember the question at the beginning? How many connections does an MPTCP socket allow? Now, we got the answer:
#define MPTCP_MAX_NUM_SUBFLOWS 256
Credit: The integer overflow was discovered and analyzed by Tao Huang and Tielei Wang of Pangu Lab.
Thanks for reading!