In this blog post, we will explore integer overflows in Windows kernel drivers and cover how arithmetic operations can lead to security vulnerabilities. We will analyze real-world cases, build a custom vulnerable driver, and demonstrate how these flaws can impact memory allocations and system stability.
What is Integer Overflow in the Kernel?
Integer overflow occurs when an arithmetic operation exceeds the maximum value a data type can hold, causing it to wrap around. In the Windows kernel, integer overflows can lead to memory corruption, buffer overflows, or incorrect size calculations in kernel allocations, often resulting in heap corruption, out-of-bounds writes, and bug checks (AKA “blue screen of death” or BSOD).
These vulnerabilities can arise in multiple ways:
- Addition Overflows occur when two large numbers add up beyond the representable range, leading to incorrect memory allocations or wraparounds that cause buffer overflows.
- Multiplication Overflows can cause incorrectly sized memory allocations, where an excessively large size results in a small allocation due to integer wraparound, leading to heap corruption and memory leaks.
Before we dive into integer overflow vulnerabilities in the Windows kernel, let’s first understand data types and how they work in memory.
Understanding Data Types
When working with low-level programming in C and C++, especially in Windows kernel and user mode applications, choosing the right data type is critical. A wrong choice can lead to integer overflows, memory corruption, privilege escalation, and serious security vulnerabilities.
To make things easier, I’ve put together a cheat sheet that you can refer back to whenever you’re analyzing a kernel driver or a user-mode application for potential bugs. This table gives you a quick overview of how different data types store values and where things can go wrong.
Use this as your go-to reference when hunting for integer overflows, wraparounds, and other dangerous bugs in kernel and user-mode applications.
Data Type | Size (x64/x86) | Signed Range | Unsigned Range | Used In |
char | 1 byte | -128 to 127 | 0 to 255 | User & Kernel |
unsigned char | 1 byte | N/A | 0 to 255 | User & Kernel |
signed char | 1 byte | -128 to 127 | N/A | User & Kernel |
short | 2 bytes | -32,768 to 32,767 | 0 to 65,535 | User & Kernel |
unsigned short | 2 bytes | N/A | 0 to 65,535 | User & Kernel |
signed short | 2 bytes | -32,768 to 32,767 | N/A | User & Kernel |
int | 4 bytes | -2,147,483,648 to 2,147,483,647 | 0 to 4,294,967,295 | User & Kernel |
unsigned int | 4 bytes | N/A | 0 to 4,294,967,295 | User & Kernel |
signed int | 4 bytes | -2,147,483,648 to 2,147,483,647 | N/A | User & Kernel |
long (Windows) | 4 bytes (x86/x64) | -2,147,483,648 to 2,147,483,647 | 0 to 4,294,967,295 | User & Kernel |
unsigned long | 4 bytes | N/A | 0 to 4,294,967,295 | User & Kernel |
signed long | 4 bytes | -2,147,483,648 to 2,147,483,647 | N/A | User & Kernel |
long long | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 0 to 18,446,744,073,709,551,615 | User & Kernel |
unsigned long long | 8 bytes | N/A | 0 to 18,446,744,073,709,551,615 | User & Kernel |
signed long long | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | N/A | User & Kernel |
SIZE_T | 8 bytes (x64) / 4 bytes (x86) | N/A | 0 to 18,446,744,073,709,551,615 (x64) / 4,294,967,295 (x86) | User & Kernel |
SSIZE_T | 8 bytes (x64) / 4 bytes (x86) | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (x64) / -2,147,483,648 to 2,147,483,647 (x86) | N/A | User & Kernel |
ULONG | 4 bytes | N/A | 0 to 4,294,967,295 | Kernel Only |
ULONGLONG | 8 bytes | N/A | 0 to 18,446,744,073,709,551,615 | Kernel Only |
DWORD | 4 bytes | 0 to 4,294,967,295 | Same as unsigned int | User & Kernel |
NTSTATUS | 4 bytes | Varies (signed) | N/A | Kernel Only |
HANDLE | 8 bytes (pointer) | System pointer | System pointer | User & Kernel |
The above data sheet provides a comprehensive reference for both user mode and kernel mode data types, covering their sizes, ranges, and potential overflow scenarios. This information is based on official Microsoft documentation and kernel data types and serves as a valuable resource for identifying vulnerabilities related to integer overflows in kernel drivers.
Common Data Types That Can Cause Integer Overflow in Kernel
Data Type | Size | Signed/Unsigned | Range | Overflow Type |
ULONG | 4 bytes | Unsigned | 0 to 4,294,967,295 (0xFFFFFFFF) | Unsigned wraparound |
LONG | 4 bytes | Signed | -2,147,483,648 to 2,147,483,647 | Signed overflow |
ULONG64 | 8 bytes | Unsigned | 0 to 18,446,744,073,709,551,615 | Large value overflow |
LONG64 | 8 bytes | Signed | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | Signed overflow |
SIZE_T | 4 bytes (x86) / 8 bytes (x64) | Unsigned | Platform-dependent | Unsigned wraparound |
SSIZE_T | 4 bytes (x86) / 8 bytes (x64) | Signed | Platform-dependent | Signed overflow |
LONG_PTR | 4 bytes (x86) / 8 bytes (x64) | Signed | Platform-dependent | Pointer arithmetic overflow |
INT64 | 8 bytes | Signed | Same as LONG64 | Multiplication overflow |
- Unsigned Types (ULONG, ULONG64, SIZE_T): These types can only hold positive values. When they overflow, they wrap around to 0 due to modulo behavior, potentially leading to buffer overflows or incorrect allocations.
- Signed Types (LONG, LONG64, SSIZE_T, LONG_PTR, INT64): These types allow both positive and negative values. When they overflow, they can flip signs, meaning a large positive value may become negative. This can lead to unexpected behavior, such as negative memory allocations or incorrect pointer arithmetic, causing crashes or security vulnerabilities.
Network Packet Overflow in Custom Windows Kernel Drivers (Addition ULONG Overflow)
I am demonstrating a custom Windows kernel driver that simulates the processing of network packets. To understand the vulnerability, let’s first discuss ULONG and its range. In Windows, ULONG is a 32-bit unsigned integer, meaning it can hold values from 0x00000000 (0 in decimal) to 0xFFFFFFFF (4,294,967,295 in decimal). Since it cannot store negative values, any arithmetic operation that exceeds 0xFFFFFFFF causes an integer overflow, wrapping the value back to a much smaller number instead of continuing to increase. This behavior is the root cause of the vulnerability in my custom driver.

The vulnerable function in this custom driver takes a user-supplied packet size and adds 0x1000 to determine how much memory to allocate for storing the packet. However, if an attacker provides a large value like 0xFFFFFFFF, adding 0x1000 causes an integer wraparound, meaning instead of a large allocation, the kernel ends up allocating a much smaller buffer than expected. For example, 0xFFFFFFFF + 0x1000 wraps around to 0x00000FFF, allocating only 4,095 bytes instead of the intended large buffer.
Triggering the Bug: Integer Wraparound in Packet Allocation
I created a simple PoC (proof of concept) to trigger the vulnerable line in the driver. The function takes a user-supplied packet size and adds 0x1000 for memory allocation. However, providing a large value like 0xFFFFFFFF causes an integer wraparound, resulting in a much smaller allocation (0x00000FFF instead of the intended large buffer), leading to a crash.

The crash occurred at: movntps xmmword ptr [rcx-10h], xmm0
, attempting to write beyond the allocated buffer at rcx = ffff860d714f1010
, which is already out of bounds from the 0x1000-byte allocation at RAX. This confirms an out-of-bounds memory write due to the integer overflow in the allocation size calculation.

Packet Size Overflow in Custom Windows Kernel Drivers (Signed Integer Overflow Long)
I am demonstrating a custom Windows kernel driver that simulates the processing of network packets. To understand the vulnerability, let’s first discuss LONG and its range. In a vulnerable kernel driver, the input size is stored in a signed LONG (32-bit integer), which ranges from -2,147,483,648 (0x80000000) to 2,147,483,647 (0x7FFFFFFF). If an attacker provides 0x7FFFFFFFF (which is a 9-digit hex value), it exceeds the LONG range, causing truncation to 0xFFFFFFFF.
In two’s complement, signed integers represent negative values by flipping bits. For example, 0xFFFFFFFF
is interpreted as -1
, and 0x80000000
is the lowest possible LONG
value (-2,147,483,648
). This behavior is key to understanding how large unsigned input can become a negative number, triggering overflow bugs.

Since 0xFFFFFFFF is interpreted as -1 in signed representation, the calculation allocates a small buffer, but later memmove(PackedData->Data, userBuffer, headerSize)
attempts to copy 0xFFFFFFFF bytes, leading to out-of-bounds memory corruption.

This results in a kernel crash (BSOD) due to an invalid memory access, demonstrating how signed integer overflows can break memory safety and cause system instability.

EDR Log Processing (SIZE_T Unsigned Integer Overflow)
In modern security solutions like endpoint detection and response (EDR), logs and events are often stored in dynamically allocated buffers. If an EDR driver incorrectly calculates the buffer size using SIZE_T (which is 4 bytes on x86 and 8 bytes on x64), an integer overflow can occur, leading to buffer overflows or memory corruption.
To demonstrate this, here’s a custom dummy kernel driver that mimics a real-world EDR scenario, where logs are dynamically allocated using SIZE_T, and an integer overflow vulnerability occurs due to improper size calculations.

If an attacker provides a value like 0xFFFFFFFFFFFFFFFF on a 64-bit system, the addition logSize + 0x40
wraps around to a small value due to modulo behavior. This results in a tiny buffer allocation, but the memmove still tries to copy 0xFFFFFFFFFFFFFFFF bytes, leading to heap corruption or a kernel crash—a serious security risk in EDR logging mechanisms.

This results in a kernel crash (BSOD) due to an invalid memory access, demonstrating how unsigned integer overflows can break memory safety and cause system instability.

Simple Integer Overflows to Real-World Heap Corruption
So far, we’ve explored some basic integer overflow scenarios, but let’s take things up a notch and look at how a seemingly small miscalculation can lead to serious heap corruption in the Windows kernel.
In the given code snippet, the issue arises due to an integer overflow in the calculation of new_size
.

However, both old_items_count
and size_of_item
are user-controlled values. If an attacker provides large values, the multiplication can exceed the maximum value of an ULONG (32-bit unsigned integer, max: 4,294,967,295). When this happens, the value wraps around to a much smaller number due to integer overflow.
As a result of the integer overflow in the memory allocation step, the buffer allocated is significantly smaller than expected. However, the driver still proceeds to call RtlCopyMemory
, using the original (overflowed) size to copy data. Since RtlCopyMemory
does not perform boundary checks, this results in an out-of-bounds write, where excess data spills into adjacent heap memory.

This is a perfect example of how integer overflows aren’t just theoretical. In the right conditions, they can result in real-world exploits that compromise system security. Even a small miscalculation in memory allocation can lead to heap corruption. Once overwritten, critical kernel structures can be manipulated for privilege escalation.
BSOD Demonstration: Triggering Integer Overflow

The above PoC sends a specially crafted ITEMS_INFO
structure to the vulnerable driver, setting last_item_index
to 0xFFFFFFFF and size_of_item
to a large value (0xFFFFFFF0). This causes an integer overflow when calculating memory allocation size, leading to heap corruption in kernel space. Eventually, this results in a blue screen of death (BSOD).

Exploring Real-World Kernel Vulnerabilities
In our previous blog posts, we examined basic integer overflow scenarios within IOCTL handlers. Now, let’s dive into a real-world case: CVE-2025-21333, an integer overflow vulnerability in vkrnlintvsp.sys—the Windows Kernel Driver.
Function Overview
The function VkiRootAdjustSecurityDescriptorForVmwp
is responsible for adjusting the security descriptor of a virtual machine worker process (VMWP). It retrieves and modifies the discretionary access control list (DACL) of the object security descriptor, then allocates memory for the modified ACL using ExAllocatePool2.

Potential Vulnerability: Integer Overflow in Pool Allocation
In certain kernel functions, memory allocation can be influenced by user-controlled values, leading to potential security vulnerabilities. One such case occurs in ExAllocatePool2()
, where the allocation size is determined by the value of v8
:

Here’s why this is problematic:
v6
andv7
are return values fromRtlLengthSid()
, representing the lengths of static string SIDs.Dacl->AclSize
comes from the object’s security descriptor, meaning it could be manipulated by an attacker.
An attacker can craft a malicious security descriptor where the Dacl
field points to user-controlled memory and AclSize
contains an arbitrary large value. When the kernel calls ExAllocatePool2()
using Dacl->AclSize + v6 + v7 + 16
as the allocation size, it performs unchecked arithmetic on attacker-influenced values. This can cause an integer overflow, resulting in a smaller-than-expected buffer being allocated—opening the door to pool-based buffer overflows and potential privilege escalation.
Heap-Based Buffer Overflow via memmove()
When memmove()
copies Dacl->AclSize
bytes into Pool2
, an attacker-controlled size can lead to out-of-bounds writes, resulting in heap corruption and potential privilege escalation.

If Dacl->AclSize
is crafted to exceed the allocated buffer, memmove()
can overwrite adjacent memory structures, triggering heap corruption. I won’t dive too deep here, but I’ve included a reference link below for a detailed root cause analysis and stack tracing—definitely worth checking out!
Bonus Tip: Going Beyond Just Arithmetic
When auditing kernel code for integer overflows, it’s not just about arithmetic—it’s about what happens next. If an overflowed value influences memory allocation (ExAllocatePoolWithTag()), comes from untrusted sources (IOCTLs, user-mode apps), or lacks proper bounds checking, it can lead to heap corruption, buffer overflows, or even code execution.
For higher chances of finding vulnerabilities, inspect network packet handlers, check EDR logs for unusual memory allocations, and analyze large-scale data parsing in drivers. Many privilege escalation bugs start with a tiny integer overflow—so always follow the trail from math to memory operations.
Conclusion
This deep dive into integer overflows in kernel code highlights how arithmetic flaws can escalate into security risks when combined with memory allocations and user-controlled inputs. By building a custom vulnerable driver, we demonstrated how these bugs manifest in real-world scenarios and explored a production vulnerability to reinforce our understanding. These hands-on insights help bridge the gap between theory and exploitation, equipping us with the knowledge to identify and mitigate similar flaws in kernel drivers.