Stack-based buffer overflow in JM_set_annot_callout_line
Description of the bug
In src/extra.i (JM_set_annot_callout_line), a fixed stack buffer of 3 points (fz_point points[3]) is indexed up to
caller-controlled count. In src/__init__.py:9816–9817, Page.add_freetext_annot(..., callout=...) passes point_count = len(callout) without length bounds validation.MuPDF's pdf_set_annot_callout_line, a callout line (/CL) has either 2 or 3 points.
Passing >= 4 points writes past the stack buffer onto the stack frame, causing a SIGBUS crash.
How to reproduce the bug
import pymupdf
doc = pymupdf.open()
page = doc.new_page()
rect = pymupdf.Rect(50, 50, 200, 150)
callout = [(10, 10), (20, 20), (30, 30), (40, 40), (50, 50), (60, 60)]
page.add_freetext_annot(rect, "test", callout=callout)Security Impact
Because each point coordinate corresponds to two IEEE-754 32-bit floats, this out-of-bounds stack write overwrites callee-saved registers (RBX, RBP, R12–R14) and the saved return address (RIP). I have attached a video below demonstrating an end-to-end RCE proof-of-concept.
PoC Demonstration
https://github.com/user-attachments/assets/c68bd669-b10f-412d-b5f5-8878c6c523f4
PyMuPDF version
1.28.2
Operating system
Linux
Python version
3.13
Source: pymupdf/PyMuPDF