TALOS-2026-2424
CVE-2026-9536
A heap-based buffer overflow vulnerability exists in the vtkWindBladeReader FindVariableOffsets functionality of vtk (version(s): 9.6.1). A specially crafted .wind can lead to a buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
vtk (version(s): 9.6.1)
vtk - https://vtk.org/
7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-122 - Heap-based Buffer Overflow
VTK, or the Visualization Toolkit, is an open-source software system for 3D computer graphics, image processing, and scientific visualization. It provides a large collection of algorithms and data structures for rendering, volume visualization, geometric processing, and interaction.
vtkWindBladeReader parses the WindBlade simulation format — an ASCII .wind metadata file describing grid dimensions and variable layout, paired with binary data files containing Fortran-style record-marked float arrays.
The reader parses a malformed WindBlade data set without error. ASan reports:
==4281==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000003330
READ of size 4 at 0x502000003330 thread T0
#0 vtkWindBladeReader::LoadVariableData(int) vtkWindBladeReader.cxx:564
#1 vtkWindBladeReader::SetUpFieldVars(...) vtkWindBladeReader.cxx:1262
#2 vtkWindBladeReader::RequestData(...) vtkWindBladeReader.cxx:386
[...]
0x502000003331 is located 0 bytes after 1-byte region [0x502000003330,0x502000003331)
allocated at vtkWindBladeReader::LoadVariableData(int) line 540
The crash-site code in LoadVariableData():
LINE_528 void vtkWindBladeReader::LoadVariableData(int var)
LINE_529 {
[...]
LINE_540 float* block = new float[this->BlockSize];
LINE_541 this->InitVariableData(var, numberOfComponents, varData, planeSize, rowSize);
LINE_542 for (int comp = 0; comp < numberOfComponents; comp++)
LINE_543 {
LINE_544 size_t cnt;
LINE_545 if ((cnt = fread(block, sizeof(float), this->BlockSize,
LINE_546 this->Internal->FilePtr)) != this->BlockSize)
LINE_547 {
[...]
LINE_553 }
LINE_555 int pos = comp;
LINE_556 for (int k = this->SubExtent[4]; k <= this->SubExtent[5]; k++)
LINE_558 for (int j = this->SubExtent[2]; j <= this->SubExtent[3]; j++)
LINE_560 for (int i = this->SubExtent[0]; i <= this->SubExtent[1]; i++)
LINE_561 {
LINE_562 int index = (k * planeSize) + (j * rowSize) + i;
LINE_563 varData[pos] = block[index];
LINE_564 pos += numberOfComponents;
LINE_565 }
[...]
}
At LINE_540, block is allocated with this->BlockSize elements. BlockSize is a size_t member variable — its value determines the buffer capacity. At LINE_563, the triple-nested loop reads block[index] where index is computed from grid-derived extents (SubExtent). SubExtent encodes the spatial dimensions of the data slice being loaded.
The ASan trace shows that the allocated region is 1 byte — a new float[N] call with N == 0 produces a minimal allocation. this->BlockSize was therefore 0 at allocation time, yet the loop at LINE_556–LINE_563 still iterates (because SubExtent ranges are non-empty), reading past the end of the buffer.
BlockSize is computed during the information phase. RequestInformation() calls ReadGlobalData() at LINE_255, which parses the .wind metadata. When the DATA_VARIABLES keyword is encountered, ReadGlobalData() calls FindVariableOffsets() at LINE_1615:
LINE_706 bool vtkWindBladeReader::FindVariableOffsets()
LINE_707 {
[...]
LINE_726 int byteCount;
LINE_727 if (fread(&byteCount, sizeof(int), 1, this->Internal->FilePtr) != 1)
LINE_728 {
LINE_729 vtkWarningMacro("... Premature EOF while reading byteCount.");
LINE_730 }
[...]
LINE_733 this->BlockSize = static_cast<size_t>(byteCount / BYTES_PER_DATA);
[...]
}
At LINE_726, byteCount is a signed int read directly from the binary data file — its value is attacker-controlled. BYTES_PER_DATA equals 4. At LINE_733, signed integer division truncates toward zero: any byteCount in the range [-3, 3] produces BlockSize == 0. Negative values produce either 0 or a negative quotient that, cast to size_t, wraps to a very large value (a separate but related issue). No validation between LINE_727 and LINE_733 checks that byteCount is positive, a multiple of 4, or consistent with the grid dimensions declared in the .wind file.
During RequestData(), the call chain consumes BlockSize without re-checking:
LINE_369 int vtkWindBladeReader::RequestData(...)
LINE_370 {
[...]
LINE_386 this->SetUpFieldVars(field);
[...]
}
SetUpFieldVars() iterates over selected variables and calls LoadVariableData() at LINE_1262, which uses this->BlockSize at LINE_540. No validation of BlockSize occurs between the information phase (where it is computed) and the data phase (where it is consumed).
The grid dimensions (GRID_SIZE_X/Y/Z from the .wind file) determine the SubExtent loop bounds at LINE_556–LINE_560. Larger declared grids cause more iterations and deeper out-of-bounds reads from the zero-sized buffer. The attacker therefore controls both the buffer size (via byteCount) and the read depth (via grid dimensions).
The vulnerability allows an attacker to cause a deterministic denial of service by providing a crafted WindBlade data set. On non-instrumented builds, the out-of-bounds reads silently succeed and may expose heap-resident data through the output arrays, creating a potential information disclosure vector.
[PoC] Update(./poc/zero_bytecount/zero_bytecount.wind)
=================================================================
==4285==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000003330 at pc 0x7f164e6b979e bp 0x7ffd1ae8ee30 sp 0x7ffd1ae8ee28
READ of size 4 at 0x502000003330 thread T0
#0 0x7f164e6b979d in vtkWindBladeReader::LoadVariableData(int) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:564:26
#1 0x7f164e6afc2a in vtkWindBladeReader::SetUpFieldVars(vtkStructuredGrid*) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:1262:13
#2 0x7f164e6ada6a in vtkWindBladeReader::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:386:11
#3 0x7f164cfd1ed7 in vtkExecutive::CallAlgorithm(vtkInformation*, int, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkExecutive.cxx:723:33
#4 0x7f164cfa400b in vtkDemandDrivenPipeline::ExecuteData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:447:22
#5 0x7f164cf7ffd8 in vtkCompositeDataPipeline::ExecuteData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkCompositeDataPipeline.cxx:151:32
#6 0x7f164cfa0fa9 in vtkDemandDrivenPipeline::ProcessRequest(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:246:22
#7 0x7f164d11259e in vtkStreamingDemandDrivenPipeline::ProcessRequest(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:331:27
#8 0x7f164cfa30b9 in vtkDemandDrivenPipeline::UpdateData(int) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:404:16
#9 0x7f164d1150ce in vtkStreamingDemandDrivenPipeline::Update(int, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:421:34
#10 0x7f164cf5e387 in vtkAlgorithm::Update(int) /opt/vtk-src/Common/ExecutionModel/vtkAlgorithm.cxx:1660:32
#11 0x564b747d438e in main /manuwork/advisories/windblade-geometry-1/poc_vtkwindbladereader_update.cpp:16:11
#12 0x7f163e9091c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#13 0x7f163e90928a in __libc_start_main csu/../csu/libc-start.c:360:3
#14 0x564b746f05a4 in _start (/manuwork/advisories/windblade-geometry-1/build-afl/poc_vtkwindbladereader_update+0x2d5a4) (BuildId: 4b8d1a5755c7044c4bda645de445fe1123c97576)
0x502000003331 is located 0 bytes after 1-byte region [0x502000003330,0x502000003331)
allocated by thread T0 here:
#0 0x564b747d1b71 in operator new[](unsigned long) (/manuwork/advisories/windblade-geometry-1/build-afl/poc_vtkwindbladereader_update+0x10eb71) (BuildId: 4b8d1a5755c7044c4bda645de445fe1123c97576)
#1 0x7f164e6b8931 in vtkWindBladeReader::LoadVariableData(int) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:540:18
#2 0x7f164e6afc2a in vtkWindBladeReader::SetUpFieldVars(vtkStructuredGrid*) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:1262:13
#3 0x7f164e6ada6a in vtkWindBladeReader::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:386:11
#4 0x7f164cfd1ed7 in vtkExecutive::CallAlgorithm(vtkInformation*, int, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkExecutive.cxx:723:33
#5 0x7f164cfa400b in vtkDemandDrivenPipeline::ExecuteData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:447:22
#6 0x7f164cf7ffd8 in vtkCompositeDataPipeline::ExecuteData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkCompositeDataPipeline.cxx:151:32
#7 0x7f164cfa0fa9 in vtkDemandDrivenPipeline::ProcessRequest(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:246:22
#8 0x7f164d11259e in vtkStreamingDemandDrivenPipeline::ProcessRequest(vtkInformation*, vtkInformationVector**, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:331:27
#9 0x7f164cfa30b9 in vtkDemandDrivenPipeline::UpdateData(int) /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:404:16
#10 0x7f164d1150ce in vtkStreamingDemandDrivenPipeline::Update(int, vtkInformationVector*) /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:421:34
#11 0x7f164cf5e387 in vtkAlgorithm::Update(int) /opt/vtk-src/Common/ExecutionModel/vtkAlgorithm.cxx:1660:32
#12 0x564b747d438e in main /manuwork/advisories/windblade-geometry-1/poc_vtkwindbladereader_update.cpp:16:11
#13 0x7f163e9091c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#14 0x7f163e90928a in __libc_start_main csu/../csu/libc-start.c:360:3
#15 0x564b746f05a4 in _start (/manuwork/advisories/windblade-geometry-1/build-afl/poc_vtkwindbladereader_update+0x2d5a4) (BuildId: 4b8d1a5755c7044c4bda645de445fe1123c97576)
SUMMARY: AddressSanitizer: heap-buffer-overflow /opt/vtk-src/IO/Geometry/vtkWindBladeReader.cxx:564:26 in vtkWindBladeReader::LoadVariableData(int)
Shadow bytes around the buggy address:
0x502000003080: fa fa fd fa fa fa fd fa fa fa fd fa fa fa 00 fa
0x502000003100: fa fa 00 fa fa fa fd fa fa fa fd fa fa fa fd fa
0x502000003180: fa fa fd fa fa fa 00 fa fa fa fd fa fa fa 00 fa
0x502000003200: fa fa 00 fa fa fa fd fa fa fa 00 fa fa fa fd fa
0x502000003280: fa fa 07 fa fa fa fd fd fa fa 00 fa fa fa 00 fa
=>0x502000003300: fa fa 00 fa fa fa[01]fa fa fa fa fa fa fa fa fa
0x502000003380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000003400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000003480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000003500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000003580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==4285==ABORTING
Aborted
Vendor Link: https://vtk.org/download/
2026-06-23 - Vendor Notification
2026-06-25 - Vendor Acknowledged
2026-06-30 - 7-Day
2026-07-02 - Follow-Up Sent
2026-07-07 - Vendor Replied, Dev Team Working
2026-08-07 - 45-Day
2026-08-22 - 60-Day
2026-09-15 - Follow-Up Sent
2026-09-21 - 90-Day
2026-09-23 - Vendor Replied, Dev Team Working
2026-10-12 - Advised Vendor Cisco Will Publish And Close Out
Emmanuel Tacheau of Cisco Talos