darktable-org / darktable-org/darktable
`retouch`: four display fields sit unlocked beside ten critical sections that guard others
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 13.1k
- Forks
- 1.4k
- Avg merge
- 22h 14m
- Merged PRs (30d)
- 198
Description
Found by code analysis (Claude + Codex, cross-checked) during the gui_data threading
audit, 2026-08-19. No known crash.
Description
retouch has ten critical sections (lines 1196, 1689, 1715, 1727, 1759, 2131, 3988,
4002, 4820, 4836). They guard the preview_auto_levels handshake and
displayed_wavelet_scale; the one at 2131 just wraps rt_shape_selection_changed().
Four other gui_data fields that both the GTK thread and the pipe touch sit right beside
all of that, unprotected — including display_wavelet_scale, whose near-identical
neighbour displayed_wavelet_scale is locked.
This is the clearest illustration in the audit of why "does this module lock?" is the
wrong question to ask of a module.
The four fields
| field | direction | sites |
|---|---|---|
display_wavelet_scale |
GTK writes, pipe reads | GTK gesture callbacks write (1682, 2060, 2082) and change_image() resets (2403-2405); process() reads (3916, 3934, 3951, 3958, 4019), process_cl() reads (4735, 4757, 4777, 4854) |
mask_display |
same | same sites |
suppress_mask |
same | same sites |
first_scale_visible |
reverse: pipe writes, GTK reads | process() / process_cl() write it (3975, 4806); the rt_wdbar_draw() GTK callback reads it (1396) |
first_scale_visible running in the opposite direction is worth noting on its own: it is
the direction dev-doc/GUI.md already documents as requiring a critical section.
Impact
Wrong pixels on screen for a frame (the three display fields), or a wavelet-bar decoration
drawn from a stale flag (first_scale_visible). None of the four is a pointer and none is
used as an index, so nothing here is memory-unsafe — but they are formally data races, and
process() reads the display fields at several points within one run, so a change landing
mid-call can make one part of the frame disagree with another.
Suggested fix
Wrap the writes and the reads in dt_iop_gui_enter_critical_section() /
dt_iop_gui_leave_critical_section(), snapshotting into locals at the top of process()
and process_cl() rather than re-reading the fields at each of their four or five use
sites. The mutex is not recursive, so the new sections must not nest inside the ten that
already exist.
change_image()'s reset of the three display fields needs the lock too.
Note for whoever audits this module next
The earlier drafts of the bulk report described retouch as "appearing to lock its
pipe-side access" on the strength of a dt_iop_gui_enter_critical_section grep. That was
wrong, here and in colorreconstruction, and it is recorded rather than quietly deleted
because it is the mistake this codebase invites.
Related
#22064 covers the same defect shape in eight
modules that never lock at all; #22058 covers
the other module that locks in the wrong places.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read dev-doc/GUI.md first, then locate retouch's process(), process_cl(), change_image(), and rt_wdbar_draw() entry points. Trace the four listed gui_data fields alongside the existing critical sections, ensuring the new sections do not nest. Done means all GTK and pipe accesses are protected and process() and process_cl() use consistent snapshots.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100