CVE-2022-32981: Linux kernel buffer overflow

Linux kernels through 5.18.3 on PowerPC 32-bit platforms are affected. See the details.

In a nutshell

There is ptrace framework that allows remote reading of a process' data, including CPU registers. The vulnerability is within the PTRACE_PEEKUSER and PTRACE_POKEUSER request types which allow a process to read/write registers of another process.

An excerpt from ptrace(2) manual page:

PTRACE_PEEKUSER

Read a word at offset addr in the tracee's USER area, which holds the registers and other information about the process (see ). The word is returned as the result of the ptrace() call. Typically, the offset must be word-aligned, though this might vary by architecture.

PTRACE_POKEUSER

Copy the word data to offset addr in the tracee's USER area. As for PTRACE_PEEKUSER, the offset must typically be word-aligned. In order to maintain the integrity of the kernel, some modifications to the USER area are disallowed.

Nature of the vulnerability

To get/set a register, the API takes an index into an imaginary address space called the "USER area", where the registers of the process are laid out in some fashion. The kernel then maps that index to a particular register in its own data structures and gets/sets the value. The API only allows a single machine-word to be read/written at a time. So 4 bytes on 32-bit kernels and 8 bytes on 64-bit kernels.

The way floating point registers (FPRs) are addressed is somewhat complicated, because double precision float values are 64-bit even on 32-bit CPUs. That means on 32-bit kernels each FPR occupies two word-sized locations in the USER area. On 64-bit kernels each FPR occupies one word-sized location in the USER area.

Internally the kernel stores the FPRs in an array of u64s, or if VSX is enabled, an array of pairs of u64s where one half of each pair stores the FPR. Which half of the pair stores the FPR depends on the kernel's endianness.

To handle the different layouts of the FPRs depending on VSX/no-VSX and big/little endian, the TS_FPR() macro was introduced. Unfortunately the TS_FPR() macro does not take into account the fact that the addressing of each FPR differs between 32-bit and 64-bit kernels. It just takes the index into the "USER area" passed from userspace and indexes into the fp_state.fpr array.

On 32-bit there are 64 indexes that address FPRs, but only 32 entries in the fp_state.fpr array, meaning the user can read/write 256 bytes past the end of the array. Because the fp_state sits in the middle of the thread_struct there are various fields than can be overwritten, including some pointers. As such it may be exploitable.

The fix

The vulnerability was recently fixed. The fix is about explicit addressing of the array for 32-bit and 64-bit platforms, and also about alerting about VSX being enabled on a 32-bit platform during kernel build time.

Useful Links