darktable-org / darktable-org/darktable

`atrous`: the sample count and the array it bounds are published to the GUI as an unlocked pair

Open
#22,057 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

scope: threading
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

atrous hands two related values from a pixelpipe worker thread to a GTK draw callback:
the number of wavelet scales it used, and the array describing them. Neither is written
nor read under a critical section, and — the point of this report — they are two
fields that must agree with each other
, so an unlocked handoff can pair a fresh count
with stale samples, or the reverse.

Of the three reverse-direction cases found in this audit (the others are hotpixels and
denoiseprofile) this is the one worth looking at first, because the other two publish a
single independent scalar each.

Evidence

All code is in src/iop/atrous.c.

  • Pipe side: get_samples() fills g->sample[] and its return value is then assigned to
    g->num_samples (lines 295, 382, 501). The two stores are separate and unordered with
    respect to any reader.
  • GTK side: area_draw() uses g->num_samples to bound a loop over g->sample[]
    (lines 1167-1182).
  • The module never calls dt_iop_gui_enter_critical_section().

Impact

No out-of-bounds risk: the count is clamped by the array size — num_samples cannot
exceed the MAX_NUM_SCALES extent of g->sample. The consequence is a mismatched pair,
so the drawing code renders the wrong scale markers for a frame.

Display only, low severity. Listed because it is the one case in the group where the
correctness condition is a relationship between two fields, which a per-field fix or a
per-field atomic would not establish.

Suggested fix

Write the count and the array under one critical section on the pipe side, and read
them under one critical section on the GUI side. A per-field lock, or making
num_samples atomic, does not fix this — the pair has to be published and consumed
atomically.

If holding the lock across the whole array copy is unwelcome on the draw path, copy both
into locals inside the section and draw from the locals afterwards.

Related

hotpixels and denoiseprofile are the other two pipe-to-GUI readouts from the same
audit, filed separately in this directory. The toggle-direction cases are in
#22064.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/iop/atrous.c by reading get_samples() and area_draw(), focusing on the assignments and loop described around lines 295, 382, 501, and 1167-1182. Review how dt_iop_gui_enter_critical_section() is used elsewhere, then ensure the count and sample array are published and consumed as one consistent pair. Done means the GUI cannot combine a fresh count with stale samples and the display remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
desktop
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.