Commit da5e650
committed
gfx/vulkan: fix three heap-corruption bugs in driver and common layer + CI
Three independent issues found during a focused audit of gfx/drivers/
vulkan.c and gfx/common/vulkan_common.c, all in the same memory-safety
class (writes past compile-time-sized arrays or undersized
allocations). Submitting together because they share the same audit
context and none stands alone particularly well. Adds AddressSanitizer
regression tests for each under samples/gfx/, wired into a new
.github/workflows/Linux-samples-gfx.yml CI job.
* vulkan_find_device_extensions: heap overflow
(gfx/common/vulkan_common.c)
The required-extension list is currently written twice -- once via
memcpy at the top of the body, then again in a per-element loop
that appears to be left over from an earlier refactor. The
instance-side counterpart vulkan_find_instance_extensions only
logs in its loop, confirming the divergence. The redundant write
consumes more slots in the caller's `enabled` buffer than the slot
accounting expects.
Inside vulkan_context_create_device_wrapper the buffer is sized
for (info.enabledExtensionCount + ARRAY_SIZE(required) +
ARRAY_SIZE(optional)) entries, but the function actually writes
(enabledExtensionCount + 2*required + optional) entries
worst-case. With current values (1 required, 3 optional) that is
a one-element heap overflow at the end of the malloc'd block
whenever a libretro core uses the Vulkan HW context-negotiation
interface and the GPU supports at least one optional extension.
The other call site in vulkan_context_init_device uses a stack
buffer of size 8 and stays inside it today, but only by a margin
of three.
The duplicate writes also produce duplicate entries in
ppEnabledExtensionNames passed to vkCreateDevice, which the spec
forbids (VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-01840);
strict validation layers will have been complaining.
Drop the per-element loop -- the early vulkan_find_extensions()
check at the top of the function already validates that all
required extensions are present, so the per-element re-check has
never done any work. Keep the debug log.
* vulkan_create_swapchain: OOB writes from unclamped image count
(gfx/common/vulkan_common.c)
vulkan_create_swapchain calls vkGetSwapchainImagesKHR twice --
first to query the count, then to fill swapchain_images[] -- and
never clamps the count between the two calls. swapchain_images,
swapchain_fences, the four swapchain_*_semaphores arrays,
readback.staging[] and vk->swapchain[] are all sized at compile
time to VULKAN_MAX_SWAPCHAIN_IMAGES (8). If a driver returns
more images than that the second vkGetSwapchainImagesKHR writes
past swapchain_images[], and every loop bounded by
num_swapchain_images (~12 sites across init/deinit/textures/
buffers/descriptor pools/command buffers/readback and direct
vk->swapchain[i] accesses) walks past its array.
Two clamps. First, cap desired_swapchain_images to
VULKAN_MAX_SWAPCHAIN_IMAGES before vkCreateSwapchainKHR so we
never ask a well-behaved driver for more images than we can
hold. Second, clamp num_swapchain_images between the two
vkGetSwapchainImagesKHR calls to handle drivers that return
more images than were requested -- the spec permits this.
* vulkan_create_texture: 32-bit overflow in staging-buffer sizing
(gfx/drivers/vulkan.c)
buffer_width = width * bpp and buffer_info.size = buffer_width *
height are both unsigned * unsigned in 32-bit before being
widened to VkDeviceSize on assignment. With dimensions large
enough to wrap (e.g. width=65536, height=16385, bpp=4 ->
0x1_0004_0000 wraps to 0x40000), the staging buffer is allocated
at the wrapped (small) size while the per-row upload memcpy loop
later in the same function walks the unmodified width x height.
The mapped region is smaller than what the loop writes, so the
memcpy walks past the mapping into adjacent heap memory.
Reachable from libretro cores supplying oversized
retro_framebuffer dimensions and from vulkan_load_texture /
vulkan_set_texture_frame, which take dimensions originating in
image decoders.
Compute the staging-buffer size in 64-bit, and widen the upload
loop's stride and per-row copy size to size_t. VkDeviceSize is
already 64-bit so the assignment to buffer_info.size is now
correct end-to-end. The loop's pointer math was already
implicitly widened on 64-bit hosts but is now unconditionally
correct on 32-bit hosts (3DS, Vita, PSP, Wii, Wii U, older
Android, 32-bit Windows) as well.
* samples/gfx/, .github/workflows/Linux-samples-gfx.yml
Three regression tests, one per fix above, following the pattern
established by the security regression tests under samples/tasks/
(archive_name_safety_test, http_method_match_test,
video_shader_wildcard_test, input_remap_bounds_test,
bsv_replay_bounds_test, bps_patch_bounds_test). Each test keeps
a verbatim copy of the relevant post-fix predicate, demarcated by
`=== verbatim copy ===` markers; an `IMPORTANT: ... must follow`
comment documents the convention. Plain C, asserts manually,
exits nonzero on failure -- matches the harness the existing
Linux-samples-{tasks,libretro-{common,db}}-samples.yml workflows
understand.
vulkan_extension_count_test (5 cases): create_device_wrapper-
shaped caller, init_device-shaped caller, GPU supporting no
optional extensions, GPU missing a required extension, and the
prepushed-via-negotiation-interface case. Verified pre/post-
patch discrimination: under the pre-fix double-write shape the
create_device_wrapper case ASan-aborts with heap-buffer-overflow.
vulkan_swapchain_clamp_test (5 cases): well-behaved 3-image
driver, at-capacity boundary, 9-image (MAX+1) driver,
pathological 64-image driver, and the request-side cap across
six input values. Verified pre/post-patch discrimination: under
the pre-fix no-clamp shape the 9-image case ASan-aborts on the
second vkGetSwapchainImagesKHR fill.
vulkan_texture_size_test (6 cases): typical 320x240, 4K RGBA,
the smoking-gun 65536x16385x4 wrap case, an assorted-shapes
table, the per-row upload-loop strides under an ASan-instrumented
buffer, and a 64-bit-result sanity check. Verified pre/post-
patch discrimination: under the pre-fix 32-bit arithmetic the
overflow cases produce arithmetic mismatches against the 64-bit
reference.
Linux-samples-gfx.yml is a near-verbatim copy of
Linux-samples-tasks.yml, runs each test under
-fsanitize=address with a per-step explanation of what the test
is regression-testing and the `verbatim copy must follow`
reminder. ~3 seconds per test on Ubuntu 24 with system gcc.1 parent 2d7b7ed commit da5e650
9 files changed
Lines changed: 1378 additions & 16 deletions
File tree
- .github/workflows
- gfx
- common
- drivers
- samples/gfx
- vulkan_extension_count
- vulkan_swapchain_clamp
- vulkan_texture_size
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
432 | 432 | | |
433 | 433 | | |
434 | 434 | | |
| 435 | + | |
| 436 | + | |
435 | 437 | | |
436 | | - | |
437 | | - | |
438 | 438 | | |
439 | | - | |
440 | | - | |
441 | | - | |
442 | | - | |
443 | | - | |
444 | | - | |
445 | | - | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
| 439 | + | |
| 440 | + | |
450 | 441 | | |
451 | 442 | | |
452 | 443 | | |
| |||
2300 | 2291 | | |
2301 | 2292 | | |
2302 | 2293 | | |
| 2294 | + | |
| 2295 | + | |
| 2296 | + | |
| 2297 | + | |
| 2298 | + | |
| 2299 | + | |
| 2300 | + | |
| 2301 | + | |
| 2302 | + | |
2303 | 2303 | | |
2304 | 2304 | | |
2305 | 2305 | | |
| |||
2415 | 2415 | | |
2416 | 2416 | | |
2417 | 2417 | | |
| 2418 | + | |
| 2419 | + | |
| 2420 | + | |
| 2421 | + | |
| 2422 | + | |
| 2423 | + | |
| 2424 | + | |
| 2425 | + | |
| 2426 | + | |
| 2427 | + | |
| 2428 | + | |
| 2429 | + | |
| 2430 | + | |
| 2431 | + | |
2418 | 2432 | | |
2419 | 2433 | | |
2420 | 2434 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1017 | 1017 | | |
1018 | 1018 | | |
1019 | 1019 | | |
| 1020 | + | |
1020 | 1021 | | |
1021 | 1022 | | |
1022 | 1023 | | |
| |||
1051 | 1052 | | |
1052 | 1053 | | |
1053 | 1054 | | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
1054 | 1062 | | |
1055 | 1063 | | |
1056 | 1064 | | |
1057 | 1065 | | |
1058 | | - | |
| 1066 | + | |
1059 | 1067 | | |
1060 | 1068 | | |
1061 | 1069 | | |
| |||
1365 | 1373 | | |
1366 | 1374 | | |
1367 | 1375 | | |
1368 | | - | |
| 1376 | + | |
| 1377 | + | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
1369 | 1381 | | |
1370 | 1382 | | |
1371 | 1383 | | |
1372 | 1384 | | |
1373 | 1385 | | |
1374 | 1386 | | |
1375 | | - | |
| 1387 | + | |
1376 | 1388 | | |
1377 | 1389 | | |
1378 | 1390 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
0 commit comments