darktable-org / darktable-org/darktable

`exposure`: `deflicker_histogram` is freed by the GTK thread while a pipe thread walks it

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

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 bots via code analysis, 2026-08-24.

**Severity: a freed pointer and a freed buffer

Summary

dt_iop_exposure_gui_data_t caches the source image's raw histogram for deflicker mode:

  uint32_t *deflicker_histogram;              // src/iop/exposure.c:85
  dt_dev_histogram_stats_t deflicker_histogram_stats;   // :86

The GTK thread frees and reallocates it. A pipe thread reads the pointer, the buffer
contents and the stats tuple. Neither side takes a lock.

The two sides

GTK thread — frees and reallocates:

  • gui_update()dt_free_align + NULL (:724-725), then
    _deflicker_prepare_histogram() (:737)
  • gui_changed() on a mode switch — :998-999, then :1015
  • gui_cleanup():1362

Pipe thread — reads: _process_common_setup()_compute_correction()
(:486-493), which dereferences the buffer in its percentile scan and reads
histogram_stats->pixels and ->bins_count.

gui_changed() frees before dt_dev_add_history_item() invalidates anything, so a pipe
already inside process() with d->deflicker == 1 can be walking the buffer as it is
freed.

Why a lock around the pointer load is not enough

Taking gui_lock only to load the pointer leaves the buffer free to be released while the
reader walks it. This is the Short Is Not The Same As Correct case in
dev-doc/GUI_Threading.md. deflicker_histogram_stats is the matching tuple problem:
bins_count and pixels are read while the GTK thread rewrites them, so they can be
mutually inconsistent even if each is individually intact.

Suggested fix

Needs a design choice, not a one-liner. Options, roughly in order of preference:

  1. Move the histogram out of gui_data entirely — it is source-image data, not GUI
    state. The non-GUI branch of _process_common_setup() (:495-502) already builds and
    frees its own copy; making the GUI path do the same removes the sharing. Costs a
    rebuild per pipe run, which is why it was cached in the first place — measure before
    assuming that matters.
  2. Refcount or version the allocation, so a reader holds it alive for the duration of
    _compute_correction().
  3. Hold gui_lock across the whole of _compute_correction() — correct, but it
    blocks the GTK thread for the length of a 65536-bin scan.

Interacts with defect 2 in #21974 (the NULL
histogram producing a black frame): the transient half of that defect's reachability is
this race. Fix them in the same change if convenient; the black-frame fix is independent
and can land first.

Reachability

Deflicker mode only — raw image, buf_dsc.channels == 1, datatype == TYPE_UINT16
(commit_params(), src/iop/exposure.c:646-650). Reached through the "magic lantern
defaults" preset (:312-322) or by switching the mode combo manually.

Environment

Present in 56898c27b8. Static analysis only — nothing verified at runtime.

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

Read dev-doc/GUI_Threading.md and inspect src/iop/exposure.c at gui_update(), gui_changed(), gui_cleanup(), _process_common_setup(), and _compute_correction(). Trace deflicker_histogram and deflicker_histogram_stats across GTK and pipe threads, then verify the chosen design removes use-after-free and inconsistent stats access in deflicker mode.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
desktop
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.