Skip to content

Fix dangling active_job pointer in ippeveprinter delete_job - #207

Closed
tanjiroK-coder wants to merge 1 commit into
OpenPrinting:masterfrom
tanjiroK-coder:ippeveprinter-active-job-uaf
Closed

tanjiroK-coder wants to merge 1 commit into
OpenPrinting:masterfrom
tanjiroK-coder:ippeveprinter-active-job-uaf

Conversation

@tanjiroK-coder

Copy link
Copy Markdown
Contributor

create_job() points printer->active_job at each new job and only process_job() resets it, but a job can finish without ever being processed: Cancel-Job or Cancel-Jobs on a held job that has no document data yet, or the abort paths in finish_document_data()/finish_document_uri() (for example Print-URI with a document-uri that cannot be read). About 60 seconds later clean_jobs() frees that job through delete_job() while active_job still points at it, so the next Create-Job or Print-Job (create_job()), Get-Printer-Attributes (queued-job-count) or Cancel-Jobs dereferences freed memory, and Cancel-Jobs can also write state, cancel and completed through the stale pointer. None of these operations need authentication by default, so any client that can reach the printer can trigger it. I reproduced it on the ASan build with ipptool: Create-Job, Cancel-Job, a 63 second DELAY, then Get-Printer-Attributes gives a heap-use-after-free READ at ippeveprinter.c:3763 (freed by delete_job from clean_jobs, allocated in create_job), and Print-URI with file:///nonexistent/doc.pdf followed by Create-Job after the delay hits it at ippeveprinter.c:1241. Clearing the pointer in delete_job() covers every way a job can complete, and clean_jobs() already holds the printer write lock at that point. With the change both sequences run clean under ASan and make test in tools passes; I left out a regression test because the fixed 60 second cleanup would add over a minute to run-tests.sh, but the ipptool file is small if you want it.

Changelog: - Fixed a use-after-free in ippeveprinter after a canceled or aborted job was removed from the job history.

A job that is canceled or aborted before it is processed never goes
through process_job(), so printer->active_job keeps pointing at it.
clean_jobs() then frees the job 60 seconds later, and the next
Create-Job, Print-Job, Get-Printer-Attributes, or Cancel-Jobs request
dereferences freed memory.  Clear the pointer in delete_job().
@michaelrsweet michaelrsweet self-assigned this Nov 4, 2026

@michaelrsweet michaelrsweet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to take a writer lock on the printer before changing active_job, but will confirm...

@michaelrsweet michaelrsweet added the investigating Investigating the issue label Nov 4, 2026
@michaelrsweet michaelrsweet added this to the Stable milestone Nov 4, 2026
@michaelrsweet

Copy link
Copy Markdown
Member

OK, looks like the only caller is clean_jobs which holds the printer lock. But that said, I think I prefer having the active_job pointer get updated when the job is canceled/aborted/completed and not when we clean the job history.

@michaelrsweet

Copy link
Copy Markdown
Member

Here we go:

[master ff3a96a] Update active_job when canceling a job that hasn't started processing yet (Issue #207)

@michaelrsweet michaelrsweet added bug Something isn't working and removed investigating Investigating the issue labels Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants