The lab focuses on CVE-2026–31431 and walks through the bug class, vulnerable path, trigger shape, patch, and review takeaway.

From platform called CVE Playground for people who want to learn real vulnerabilities by actually working through them, not just reading summaries or running random PoCs.

None

Lab link: CVE Playground

The lab is divided into five parts:

01 Brief      -> What broke and why it matters
02 Locate     -> Find the vulnerable function
03 Reproduce  -> Understand the trigger path
04 Patch      -> Study how the fix landed
05 Harden     -> Take away the review lesson

Below is my step-by-step solution.

Question 1: The pre-fix code in algif_aead._aead_recvmsg() was structured as if the AEAD operation might run in place: same buffer for source and destination, with offsets and SGL chaining to make that work. In reality, algif_aead's source comes from the TX SGL (populated via sendmsg) and the destination comes from the RX SGL (populated via recvmsg). What is the bug class here?

The first clue is the false in-place assumption.

The vulnerable algif_aead._aead_recvmsg() path was written as if source and destination could be treated as the same buffer. But in reality, they come from two different scatter-gather lists:

TX SGL  -> source data from sendmsg()
RX SGL  -> destination data for recvmsg()

That means the bug is not about memory lifetime, integer math, or a race. The problem is that data from one mapping was handled as if it belonged to another mapping.

So the correct answer is:

B. Incorrect resource transfer between spheres (CWE-669)

The "spheres" are the TX and RX mappings. The vulnerable code blurred that boundary by pretending both sides were one shared in-place buffer.

Question 2: Walk into _aead_recvmsg() cold and read it. The first signal that something is structurally off is in the helper signatures the function calls. Which signature change is the giveaway?

The giveaway is the helper signature, not the main crypto call.

While reading _aead_recvmsg(), the suspicious part is that af_alg_count_tsgl() was changed to accept an offset. For the real algif_aead flow, that offset is unnecessary because source and destination are not truly the same buffer.

So the correct answer is:

B. af_alg_count_tsgl() taking an offset parameter that the real algif_aead use case never needs

That offset exists only to support the fake in-place model. Once we know TX and RX are separate mappings, this becomes the first structural sign that the code is solving the wrong problem.

Question 3: Suppose you have a local, unprivileged shell on a vulnerable kernel. Which sequence is the realistic shape of an exploit attempt, i.e., the driver that gets the bug to fire, even if you don't have the precise byte-layout that wins?

The realistic exploit shape is to drive the vulnerable algif_aead path through the normal AF_ALG userspace crypto interface.

So the correct answer is:

B. socket(AF_ALG) → bind(... gcm(aes)) → setsockopt key + authsize → accept → craft AAD/payload via sendmsg → drive the bug via recvmsg

This matches how algif_aead is reached: userspace opens an AF_ALG socket, configures an AEAD cipher, sends AAD/payload through sendmsg(), then triggers the receive path with recvmsg().

The other options describe unrelated paths: hugepage racing, module-init UAF, or /dev/crypto ioctl usage.

Question 4: Read the upstream fix commit message: "This mostly reverts commit 72548b0 except for the copying of the associated data. There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings." Which property of the fix is the load-bearing one, i.e., the one that actually closes the bug class, vs. just incidentally cleaning up?

The important part of the fix is that it removes the false in-place model completely.

So the correct answer is:

C. Operating out-of-place, TX SGL stays the source, RX SGL stays the destination, no chaining or offsets between them

This closes the actual bug class because TX and RX are treated as separate mappings again.

The removed offset parameters and explicit memcpy_sglist() are part of the cleanup, but the real fix is the design change: no more pretending that source and destination are one shared buffer.

Question 5: You're code-reviewing a kernel patch that adds an "in-place" fast path to a different subsystem. The patch description says: "When src and dst point to the same userspace mapping, we can skip the copy and operate directly on the buffer." What is the single most important thing to verify in review, drawing on the lesson from CVE-2026–31431?

he main lesson is not "never use in-place optimization." The lesson is to prove when it is actually valid.

So the correct answer is:

D. Verify which concrete callers ever pass src == dst, AND that the src != dst path is independently correct

CVE-2026–31431 happened because the code added in-place plumbing for a case that did not really exist in algif_aead.

During review, the key question is: Do real callers actually pass the same buffer for src and dst?

If not, the fast path is just unnecessary complexity. And even if some callers do pass src == dst, the separate-buffer path must still be correct.