Talos Vulnerability Report

TALOS-2026-2412

vtk vtkPNGReader::ExecuteInformation invalid pointer dereference vulnerability

October 29, 2026

CVE Number

CVE-2026-41216

Summary

A invalid pointer dereference vulnerability exists in the vtkPNGReader::ExecuteInformation functionality of vtk (version(s): 9.6). A specially crafted .png file can lead to a denial of service. An attacker can provide a malicious file to trigger this vulnerability.

Confirmed Vulnerable Versions

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)

Product URLs

vtk - https://vtk.org/

CVSSv3 Score

7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-825 - Expired Pointer Dereference

Details

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.

The interesting part of the crash details is the following :
#3 0x00007f00855f1dae in vtkPNGReader::vtkInternals::HandleLibPngError (this=<optimized out>, pngPtr=<error reading variable: Cannot access memory at address 0x7c2008000000207c>, infoPtr=<error reading variable: Cannot access memory at address 0x49484452>, fp=0x515000003000) at /opt/vtk-src/IO/Image/vtkPNGReader.cxx:265

So when looking into the call stack we can observe that vtkPNGReader::vtkInternals::HandleLibPngError is called from vtkPNGReader::ExecuteInformation

Following source extract of vtkPNGReader::ExecuteInformation calling HandleLibPngError LINE70 then continue flow of execution.

LINE1	void vtkPNGReader::ExecuteInformation()
LINE2	{
LINE3	  vtkInternals* impl = this->Internals;
          [...]
LINE58	  png_structp png_ptr = nullptr;
LINE59	  png_infop info_ptr = nullptr;
LINE60	  png_infop end_info = nullptr;
LINE61	  if (!impl->CreateLibPngStructs(png_ptr, info_ptr, end_info))
LINE62	  {
LINE63	    if (fp)
LINE64	    {
LINE65	      fclose(fp);
LINE66	    }
LINE67	    return;
LINE68	  }
LINE69	
LINE70	  impl->HandleLibPngError(png_ptr, info_ptr, fp);
LINE71	  impl->InitLibPngInput(this, png_ptr, &stream, fp);
LINE72	
LINE73	  png_read_info(png_ptr, info_ptr);
LINE74	
            [...]
LINE143	}

Looking into HandleLibPngError source code :

LINE146	  void HandleLibPngError(png_structp pngPtr, png_infop infoPtr, FILE* fp)
LINE147	  {
LINE148	    if (setjmp(png_jmpbuf(pngPtr)))
LINE149	    {
LINE150	      png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp) nullptr);
LINE151	      if (fp)
LINE152	      {
LINE153	        fclose(fp);
LINE154	      }
LINE155	    }
LINE156	  }

We notice some setjmp instruction in order to call png_destroy_read_struct which will be use in case of error and cause the stack and some registers to be saved for later reuse. Then HandleLibPngError ends and flow continue execution coming back to vtkPNGReader::ExecuteInformation LINE71 to init and read png info through call to png_read_info LINE73. The issue is happening as vtkPNGReader::ExecuteInformation() crashes with SIGSEGV or SIGABRT when processing a malformed PNG file, as longjmp is triggered by png_read_info. This is happening because the longjmp returns into HandleLibPngError. At the time longjmp fires, HandleLibPngError() has already returned and its stack frame has been overwritten by subsequent calls (InitLibPngInput, png_read_info). The fp parameter slot in that dead frame now contains garbage from whatever the last callee placed there. The issue is the fclose(fp) LINE153 as fp is not pointing anymore to the file pointer. The correct approach should have been to implement the setjmp directly intovtkPNGReader::ExecuteInformation.

fclose() dereferences fp as a FILE structure: 1. Reads fp->_IO_file_jumps — the FILE vtable pointer 2. Calls a function pointer from that vtable (_IO_FINISH)

If the attacker controls the memory region fp points to (e.g., via the PNG file content loaded in heap), they can supply a crafted vtable and redirect execution.

glibc ≥ 2.24 includes _IO_vtable_check() which validates that the vtable is within the expected range. This converts some exploitation attempts into SIGABRT instead of arbitrary code execution. However, this mitigation is absent on glibc <= 2.24 and on other C libraries.

Crash Information

Program received signal SIGSEGV, Segmentation fault.
0x00007f006dc9402d in png_free_data () from /lib/x86_64-linux-gnu/libpng16.so.16
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────────────────────────────────[ LAST SIGNAL ]─────────────────────────────────────────────────────────────────────────────────────────────────
current pc: 0x7f006dc9402d
─────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────────────
 RAX  0
*RBX  0x49484452
*RCX  0xffffffff ◂— 0
*RDX  0xffff
*RDI  0x7c2008000000207c
*RSI  0x49484452
*R8   0x7ffee970c920 —▸ 0x513000000740 ◂— 0
*R9   0x7ffee970c9d0 —▸ 0x7ffee970cae0 —▸ 0x7ffee970cb10 —▸ 0x7ffee970cbd0 —▸ 0x7ffee970cc90 ◂— ...
*R10  0x7fffffffff01
*R11  0x201
*R12  0x49484452
*R13  0xffff
*R14  0xfffdd2e1924 ◂— 0
*R15  0x7f005bfe7800 ◂— 0xbebebebebebebebe
*RBP  0x7ffee970c8c0 —▸ 0x7ffee970c8e0 —▸ 0x7ffee970c910 —▸ 0x7ffee970c9d0 —▸ 0x7ffee970cae0 ◂— ...
*RSP  0x7ffee970c880 ◂— 0
*RIP  0x7f006dc9402d (png_free_data+45) ◂— mov rsi, qword ptr [rsi + 0xa0]
──────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────────────
 ► 0x7f006dc9402d <png_free_data+45>    mov    rsi, qword ptr [rsi + 0xa0]      <Cannot dereference [0x494844f2]>
   0x7f006dc94034 <png_free_data+52>    mov    r12, rdi                         R12 => 0x7c2008000000207c
   0x7f006dc94037 <png_free_data+55>    mov    r14d, ecx                        R14D => 0xffffffff ◂— 0
   0x7f006dc9403a <png_free_data+58>    mov    edx, dword ptr [rbx + 0x12c]     <Cannot dereference [0x4948457e]>
   0x7f006dc94040 <png_free_data+64>    mov    eax, r13d                        EAX => 0xffff
   0x7f006dc94043 <png_free_data+67>    and    eax, edx
   0x7f006dc94045 <png_free_data+69>    test   rsi, rsi
   0x7f006dc94048 <png_free_data+72>    je     png_free_data+198           <png_free_data+198>

   0x7f006dc9404a <png_free_data+74>    test   ah, 0x40
   0x7f006dc9404d <png_free_data+77>    je     png_free_data+198           <png_free_data+198>

   0x7f006dc9404f <png_free_data+79>    cmp    ecx, -1
────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffee970c880 ◂— 0
... ↓        2 skipped
03:0018│-028 0x7ffee970c898 ◂— 0x7c2008000000207c /* '| ' */
04:0020│-020 0x7ffee970c8a0 ◂— 0x49484452 /* 'RDHI' */
05:0028│-018 0x7ffee970c8a8 —▸ 0x7ffee970c960 ◂— 0
06:0030│-010 0x7ffee970c8b0 —▸ 0xfffdd2e1924 ◂— 0
07:0038│-008 0x7ffee970c8b8 —▸ 0x7f005bfe7800 ◂— 0xbebebebebebebebe
──────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0   0x7f006dc9402d png_free_data+45
   1   0x7f006dc94629 png_destroy_info_struct+57
   2   0x7f006dc97412 png_destroy_read_struct+66
   3   0x7f00855f1dae vtkPNGReader::vtkInternals::HandleLibPngError(png_struct_def*, png_info_def*, _IO_FILE*)+206
   4   0x7f00855ec742 vtkPNGReader::ExecuteInformation()+4482
   5   0x7f0085405fa2 vtkImageReader2::RequestInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*)+162
   6   0x7f0083f7ced8 vtkExecutive::CallAlgorithm(vtkInformation*, int, vtkInformationVector**, vtkInformationVector*)+504
   7   0x7f00840c2870 vtkStreamingDemandDrivenPipeline::ExecuteInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*)+224
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> k
#0  0x00007f006dc9402d in png_free_data () from /lib/x86_64-linux-gnu/libpng16.so.16
#1  0x00007f006dc94629 in png_destroy_info_struct () from /lib/x86_64-linux-gnu/libpng16.so.16
#2  0x00007f006dc97412 in png_destroy_read_struct () from /lib/x86_64-linux-gnu/libpng16.so.16
#3  0x00007f00855f1dae in vtkPNGReader::vtkInternals::HandleLibPngError (this=<optimized out>, pngPtr=<error reading variable: Cannot access memory at address 0x7c2008000000207c>,
    infoPtr=<error reading variable: Cannot access memory at address 0x49484452>, fp=0x515000003000) at /opt/vtk-src/IO/Image/vtkPNGReader.cxx:265
#4  0x00007f00855ec742 in vtkPNGReader::ExecuteInformation (this=0x521000001500) at /opt/vtk-src/IO/Image/vtkPNGReader.cxx:360
#5  0x00007f0085405fa2 in vtkImageReader2::RequestInformation (this=0x516000000080, outputVector=0x50e000000820) at /opt/vtk-src/IO/Image/vtkImageReader2.cxx:394
#6  0x00007f0083f7ced8 in vtkExecutive::CallAlgorithm (this=0x512000000640, request=<optimized out>, direction=<optimized out>, inInfo=<optimized out>, outInfo=<optimized out>)
    at /opt/vtk-src/Common/ExecutionModel/vtkExecutive.cxx:723
#7  0x00007f00840c2870 in vtkStreamingDemandDrivenPipeline::ExecuteInformation (this=0x512000000640, request=<optimized out>, inInfoVec=<optimized out>, outInfoVec=0x50e000000820)
    at /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:465
#8  0x00007f0083f4be49 in vtkDemandDrivenPipeline::ProcessRequest (this=0x512000000640, request=0x50e0000011c0, inInfoVec=<optimized out>, outInfoVec=<optimized out>)
    at /opt/vtk-src/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx:209
#9  0x00007f00840bdd33 in vtkStreamingDemandDrivenPipeline::ProcessRequest (this=0x512000000640, request=0x50e0000011c0, inInfoVec=<optimized out>, outInfoVec=<optimized out>)
    at /opt/vtk-src/Common/ExecutionModel/vtkStreamingDemandDrivenPipeline.cxx:372
#10 0x00007f0083f0b610 in vtkAlgorithm::UpdateInformation (this=<optimized out>) at /opt/vtk-src/Common/ExecutionModel/vtkAlgorithm.cxx:1752
#11 0x000055c25989ef70 in ExercisePNGReaderAPIs (reader=0x516000000080, input="/manuwork/advisories/tests_202f92dbec9f1eaa6f45b34d3bd6f0035b4ac1a3.png") at /manuwork/medical_harness_vtk/harness_png.cpp:49
#12 main (argc=0x5, argv=<optimized out>) at /manuwork/medical_harness_vtk/harness_png.cpp:98
#13 0x00007f00758b91ca in __libc_start_call_main (main=main@entry=0x55c25989d1c8 <main(int, char**)>, argc=argc@entry=0x5, argv=argv@entry=0x7ffee970d178) at ../sysdeps/nptl/libc_start_call_main.h:58
#14 0x00007f00758b928b in __libc_start_main_impl (main=0x55c25989d1c8 <main(int, char**)>, argc=0x5, argv=0x7ffee970d178, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7ffee970d168) at ../csu/libc-start.c:360
#15 0x000055c2597b9895 in _start ()

Vendor Response (CVE-2026-41216)

Vendor Link: https://vtk.org/download/

Timeline

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

Credit

Emmanuel Tacheau of Cisco Talos