A vulnerability has been discovered in the Linux kernel's workqueue implementation, which has now been resolved. The vulnerability, identified as CVE-2024-44981, is associated with an undefined behavior: 'subtraction overflow' error in the shift_and_mask() function of workqueue.c. This blog post will delve into the technical details of the vulnerability, provide code snippets, and shed light on the exploit and its solution.

Vulnerability details

The issue was reported when running the Linux kernel within a virtual machine on an Android system, causing a kernel panic during boot. The kernel panic resulted from a 'subtraction overflow' error within the workqueue implementation. The cause of this error was the incorrect usage of a signed constant in the shift_and_mask() function that led to decremented INT_MIN.

The corresponding console error message is shown below

| Internal error: UBSAN: integer subtraction overflow: 00000000f2005515 [#1] PREEMPT SMP
| ... (truncated for brevity)
| Code: f900fbf 97fffa2f 39400268 37100048 (d42aa2a)
| ---[ end trace 000000000000000 ]---
| Kernel panic - not syncing: UBSAN: integer subtraction overflow: Fatal exception

The error results from the following code within the shift_and_mask() function in workqueue.c

static inline unsigned int shift_and_mask(unsigned int x, unsigned int shift)
{
    return (x >> shift) & ((1 << (32 - shift)) - 1);
}

Here, the mask value in the function is being constructed using the signed constant 1. When the shift_and_mask() function is called with a shift of 31 (defined as WORK_OFFQ_POOL_SHIFT), the signed constant 1 causes a 'subtraction overflow' error and decrement from INT_MIN.

Fix:

The fix to this vulnerability is quite simple. The signed constant 1 should be replaced with an unsigned constant 1U. The updated shift_and_mask() function is as follows:

static inline unsigned int shift_and_mask(unsigned int x, unsigned int shift)
{
    return (x >> shift) & ((1U << (32 - shift)) - 1);
}

By using the unsigned constant 1U, the mask value in the shift_and_mask() function will be correctly generated, and the error will be resolved.

Conclusion

The vulnerability CVE-2024-44981, resulting from a 'subtraction overflow' error in the shift_and_mask() function of the Linux kernel workqueue implementation, has been resolved by using an unsigned constant 1U. This fix will prevent the kernel panic associated with the error when running the Linux kernel inside a virtual machine on an Android system. It is recommended to apply the necessary patches to your Linux kernel to avoid potential exploitation of this vulnerability.

Original references

1. Linux kernel git commit for the fix
2. GCC manual for the use of suffix 'U'

Timeline

Published on: 09/04/2024 20:15:07 UTC
Last modified on: 09/05/2024 17:54:19 UTC