CVE-2024-35944 - Dangerous memcpy in Linux Kernel’s VMCI Subsystem Explained

CVE-2024-35944 deals with a security issue in the Linux kernel's VMware VMCI (Virtual Machine Communication Interface) datagram handler. A careless use of memcpy() triggered a run-time warning and a potential memory overflow, found by the Syzkaller fuzzer. Let’s break down the issue, code, implications, and the official fix, with simple explanations.

What’s VMCI and Why Does This Matter?

VMCI is a subsystem in the Linux kernel used to allow fast communication between virtual machines and their host via VMware virtualization. The code in question processes messages called "datagrams," which are structured as both a header and a payload.

Where did Syzkaller trip up?

Fuzzing found that a memcpy() in the function dg_dispatch_as_host() wrote data from a source struct into a destination struct — but the size parameter (dg_size) could include both the header *and* payload. The problem? The destination buffer was just the header member, which is smaller.

Let’s look at the relevant structs and code (simplified for clarity)

// Size macro for datagrams
#define VMCI_DG_SIZE(_dg) (VMCI_DG_HEADERSIZE + (size_t)(_dg)->payload_size)
// VMCI_DG_HEADERSIZE is 24 bytes

struct delayed_datagram_info {
    struct datagram_entry *entry;
    struct work_struct work;
    bool in_dg_host_queue;
    struct vmci_datagram msg;  // 24 bytes
    u8 msg_payload[];          // flexible array for the payload
};

// The problematic memcpy:
memcpy(&dg_info->msg, dg, dg_size);
// - &dg_info->msg: points to 24-byte msg
// - dg:            source struct vmci_datagram (plus possible payload)
// - dg_size:       24 (header) + payload_size

// Example: payload_size = 32
// memcpy() writes 56 bytes (24+32), but msg is only 24 bytes!

The result: The memcpy wrote 32 bytes past the msg field, into the flexible array member msg_payload. Luckily, the struct is laid out so these two are adjacent, but this violates C memory safety rules and triggers warnings under FORTIFY_SOURCE hardening.

Syzkaller found this, which the kernel logged like so

memcpy: detected field-spanning write (size 56) of single field "&dg_info->msg"
at drivers/misc/vmw_vmci/vmci_datagram.c:237 (size 24)
WARNING: CPU:  PID: 1555 at ...dg_dispatch_as_host...

The assignment was spanning two fields — the struct member, and the start of msg_payload — in a way fortify didn't like.

Why This Is Bad

- Security: Overwriting structurally adjacent areas can lead to memory corruption, privilege escalation, or denial of service — especially if the destination array isn't sized as expected.
- Reliability: Even if it seems “safe,” toolchains with hardening like FORTIFY_SOURCE will complain, or worse, in the future this struct layout might change and break code in subtle ways.

How to Fix

The right way: Don’t copy across struct boundaries. Assign fields separately.

Example Fix

// Split copying into header and payload
memcpy(&dg_info->msg, dg, sizeof(struct vmci_datagram));
if (dg->payload_size)
    memcpy(dg_info->msg_payload, dg + 1, dg->payload_size);

The first line only copies the struct's header, which is safe.

- If there's a payload, dg + 1 points to the payload right after the header, and that gets safely copied to the flexible array.

Reference — See the official commit on kernel.org for details.

Background Reading

- The struct layout in the actual kernel source.
- Syzkaller: Linux Kernel Fuzzing
- FORTIFY_SOURCE checks explained (RedHat)

While not a straightforward remote code execution, this bug could theoretically be abused

- It provides a primitive for overwriting memory close to the header struct (depending on how msg_payload is managed).
- If a malicious VM or process creates a malformed datagram with a large payload, and there is insufficient bounds checking elsewhere, this could lead to a crash or more serious kernel compromise.

Demo scenario (conceptual)

// Malicious user creates an oversized datagram on purpose
struct vmci_datagram *mal_dg = malloc(sizeof(*mal_dg) + 32);
mal_dg->payload_size = 32;
// Fill mal_dg->payload with controlled content
send_to_kernel(mal_dg, sizeof(*mal_dg) + 32);
// Older kernel would copy all 56 bytes to &dg_info->msg, overflowing

Credits

- Bug discovery: Syzkaller Fuzzer

References

- Linux Kernel Git Commit - vmci: Fix memcpy run-time warning in dg_dispatch_as_host()
- syzkaller Issue on VMCI memcpy *(example link)*
- Linux struct flexible array member best practices (LWN)


In summary:
CVE-2024-35944 was a classic but subtle bug that shows why C programmers must be careful with struct sizes and use of memcpy. The fix is straightforward but crucial for system stability and security!

Timeline

Published on: 05/19/2024 11:15:50 UTC
Last modified on: 05/04/2025 09:08:56 UTC