Skip to content

raw denoise: with several threads, X-Trans edge pixels depend on uninitialized memory #22457

Description

@kofa73

Is there an existing issue for this?

  • I checked and did not find my issue in the already reported ones

Describe the bug

With raw denoise enabled on an X-Trans image and more than one thread, some pixels near the image edge depend on memory that darktable never wrote. On the image of integration test 0068 with -t 4, making glibc fill new allocations with a pattern (MALLOC_PERTURB_=129) changes 8231 pixels of the export, by up to 141/255. The changes form three areas of about 40×100 pixels at the right edge, around rows 1044, 2088 and 3132. The first one is a blue and black block (attached crop). With -t 1 the same test changes nothing.

Steps to reproduce

  1. In a darktable checkout with the src/tests/integration submodule, run cd src/tests/integration.
  2. Run OMP_NUM_THREADS=4 darktable-cli images/mire1-xtrans.raf 0068-rawdenoise-xtrans/rawdenoise-xtrans.xmp /tmp/a.png --core --disable-opencl -t 4 --configdir /tmp/dt-a.
  3. Run OMP_NUM_THREADS=4 MALLOC_PERTURB_=129 darktable-cli images/mire1-xtrans.raf 0068-rawdenoise-xtrans/rawdenoise-xtrans.xmp /tmp/b.png --core --disable-opencl -t 4 --configdir /tmp/dt-b.
  4. Run compare -metric AE /tmp/a.png /tmp/b.png null:. It prints 8231.
  5. Open /tmp/b.png at the right edge, around rows 990-1100. It shows a blue and black block that /tmp/a.png does not have.

Expected behavior

The export does not depend on the contents of newly allocated memory.

Logfile | Screenshot | Screencast

$ compare -metric AE /tmp/a.png /tmp/b.png null:
8231 (0.000315263)

200×200 crop at x=6052, y=944, 3× enlarged: step 2 left, step 3 right:

Image

Commit

Not bisected. The skipped copy that causes it came with c61568e (#7202, 2020).

Where did you obtain darktable from?

self compiled

darktable version

5.7.0+1090~gd060937f63-dirty

What OS are you using?

Linux

What is the version of your OS?

Ubuntu 26.04.1 LTS

Describe your system

AMD Ryzen 5 5600X (6 cores, 12 threads), 62 GiB RAM, glibc 2.43, GTK 3.24.52, GraphicsMagick 1.3.46. Only darktable-cli was used.

Are you using OpenCL GPU in darktable?

No

If yes, what is the GPU card and driver?

No response

Please provide additional context if applicable. You can attach files too, but might need to rename to .txt or .zip

  • Other versions: not tried. The runs used master fa3fc72294. The printed version belongs to the build tree, which is one commit later and differs from master only in src/common/darktable.c. The runs linked that file from master.
  • RAW or JPEG: X-Trans raw only.
  • Fresh edit: the history is the XMP of integration test 0068.
  • Empty config dir: yes, a new --configdir for every run.
  • Lua: none.
  • OMP_NUM_THREADS matches -t because on master, darktable-cli crashes on X-Trans images with -t below the CPU count (darktable-cli: -t N below the CPU count corrupts the heap #22436, fix in common: restore OpenMP thread limit after GraphicsMagick init #22438).
  • Without MALLOC_PERTURB_, repeated exports are identical, and the damage is small: -t 4 differs from -t 1 in 326 pixels, by up to 10/255, at the same places. MALLOC_PERTURB_ is a glibc feature. I did not check what other platforms' allocators return here.

Cause (confirmed). wavelet_denoise_xtrans() builds each color plane in row chunks, and the last row of a chunk skips its red and blue copies into the row below (src/iop/rawdenoise.c:380). For this image, position (first row of the next chunk, width-2) then gets no value in the red plane, because the last-column code (:398-403) and the repair loop (:422) do not reach it. The plane keeps what the buffer held there: memory from dt_alloc_align_float() (:316) in the red pass, or the previous channel's plane in the blue pass. Confirmed with a build that fills the plane with NaN before each pass: with -t 4, red-plane positions (1044, 6250), (2088, 6250) and (3132, 6250) stay NaN, and with -t 1 no red or blue position does. Building the plane in a single chunk (nthreads = 1 at :338) removes the effect. A replay of the loop shows the same gap at column 1 for other CFA alignments, which this image does not have.

Related: a separate report on the same boundaries, where concurrent chunks overwrite green values and the export changes with the thread count.

Found and reproduced by an AI agent (Claude). Not yet reproduced by a human.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions