gitlab-foss (5, Ruby), darktable (5, C), suitecrm (6, PHP), inkscape (4, C++), calibre (4, Python), scribus (4, C++), vscode (4, TypeScript), digikam (4, C++). Note: darktable-0004 and digikam-0004 are CWE-312 (cleartext credential logging), not CWE-407.
6.5 KiB
darktable — CWE-407 + CWE-312 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Five defects in darktable across the map location system, tag management, map view clustering, module group visibility, and password storage. Three are CWE-407 algorithmic complexity (O(n²) from g_list_find() inside loops), one is CWE-407 from g_list_find_custom() inside nested loops, and one is CWE-312 (cleartext credential logging). All patched.
The Defects
darktable-0001 (PATCHED — MEDIUM, CWE-407): src/common/map_locations.c:535,558
// In dt_map_location_update_images / dt_map_location_update_locations:
for(GList *img = imgs; img; img = g_list_next(img))
{
if(!g_list_find(new_imgs, img->data)) // O(M) linear scan per image
{
dt_tag_detach(ld->id, ...);
}
}
Two functions compute symmetric differences between old and new image/tag lists using g_list_find() inside for-loops. Four total g_list_find() call sites. Each is O(N*M) where N and M are list lengths.
darktable-0002 (PATCHED — MEDIUM, CWE-407): src/common/tags.c:38,54,378
// In _tag_add_tags_to_list — fires on batch tag operations:
if(!g_list_find(*list, t->data)) // O(L) per tag on accumulator list
{
*list = g_list_prepend(*list, t->data);
}
Three functions use g_list_find() for dedup: _tag_add_tags_to_list (O(TL) where T = tags to add, L = accumulator size), _get_tb_removed_tag_string_values and _get_tb_added_tag_string_values (O(BA) for before/after tag diff in undo).
darktable-0003 (PATCHED — HIGH, CWE-407): src/views/map.c:1506
// In map view clustering — fires on every map pan/zoom:
if(g_list_find((GList *)sel_imgs, GINT_TO_POINTER(p[j].imgid)))
entry->selected_in_group = TRUE;
// Inside O(I*J) clustering loop — total O(I²×S)
Map clustering iterates all images (I) in nested loops and checks selection membership with g_list_find(sel_imgs) — O(S) per check. Total: O(I²×S). For 5000 map images with 500 selected: 12.5 billion pointer comparisons per map redraw.
darktable-0004 (PATCHED — MEDIUM, CWE-312): src/common/pwstorage/backend_kwallet.c:368,555 and backend_apple_keychain.c:65,239
// In KWallet and Apple Keychain backends:
dt_print(DT_DEBUG_PWSTORAGE, "...storing (%s, %s)", (gchar *)key, (gchar *)value);
// value contains JSON with plaintext password: {"password":"s3cr3t"}
Four dt_print() calls log the full credential value (including plaintext passwords) when the -d pwstorage or -d all debug flag is active. Piwigo credentials are real username+password pairs, so exposure means direct account compromise. Users commonly run -d all to diagnose OpenCL issues, silently exposing credentials.
darktable-0005 (PATCHED — MEDIUM, CWE-407): src/libs/modulegroups.c:277
// In _lib_modulegroups_test_visible — fires on every keystroke in module search:
for(const GList *l = d->groups; l; l = g_list_next(l))
{
dt_lib_modulegroups_group_t *gr = l->data;
if(g_list_find_custom(gr->modules, module, _iop_compare) != NULL) // O(P)
return TRUE;
}
For each of M IOP modules, test_visible() scans all G groups, each with P modules via g_list_find_custom(). Total: O(MGP). Every keystroke in the search box triggers a full recheck — 10 characters = 64,000 string comparisons at M=80, G=8, P=10.
Complexity Proof
darktable-0001: At N=1000 images, M=1000 new images:
- Defective: 4 × 1000 × 1000 = 4,000,000 pointer comparisons
- Fixed: 4 × (1000 + 1000) = 8,000 hash operations
- ~500× op reduction.
darktable-0002: At T=500 tags to add:
- Defective: 500 × 499 / 2 = ~125,000 comparisons
- Fixed: 500 hash lookups + 500 inserts
- ~125× op reduction.
darktable-0003: At I=5000 images, S=500 selected:
- Defective: O(I² × S) = billions of comparisons
- Fixed: O(I² + S) — hash build once, O(1) per check
- Orders of magnitude improvement.
darktable-0005: At M=80 modules, G=8 groups, P=10:
- Defective: 80 × 8 × 10 = 6,400 string comparisons per keystroke
- Fixed: 80 × 1 = 80 hash lookups per keystroke
- ~80× speedup.
Impact
darktable serves hundreds of thousands of photographers managing large RAW photo libraries. The map view defect (0003) fires on every pan/zoom, making geographic photo browsing of large geotagged collections unusably slow. The tag defect (0002) fires during batch tag operations — a common workflow when importing and organizing photos. The module search defect (0005) fires on every keystroke in the darkroom module search. The credential defect (0004) exposes Piwigo passwords in debug logs that users routinely enable for troubleshooting.
The Fix
darktable-0001/0002/0003: Replace g_list_find() with GHashTable for O(1) lookup:
// Before
if(!g_list_find(new_imgs, img->data))
// After — O(1) hash lookup
GHashTable *new_set = g_hash_table_new(g_direct_hash, g_direct_equal);
for(GList *img = new_imgs; img; img = g_list_next(img))
g_hash_table_add(new_set, img->data);
if(!g_hash_table_contains(new_set, img->data))
darktable-0004: Redact credential values in debug logs:
// Before
dt_print(DT_DEBUG_PWSTORAGE, "...storing (%s, %s)", key, value);
// After
dt_print(DT_DEBUG_PWSTORAGE, "...storing (%s, [REDACTED])", key);
darktable-0005: Build a flat GHashTable of all visible module op-names, rebuilt when groups change:
// Before — O(M*G*P) per visibility check
for(groups) { g_list_find_custom(gr->modules, module, _iop_compare); }
// After — O(1) per check
g_hash_table_contains(d->visible_modules_set, module);
Patch
Fixes available: defects/darktable/patch/darktable-0001-0005-*.patch
Five patches across map_locations.c, tags.c, map.c, backend_kwallet.c, backend_apple_keychain.c, and modulegroups.c.
darktable-0001: 500× op reduction at 1000 images. darktable-0002: 125× at 500 tags. darktable-0003: orders-of-magnitude at 5000 geotagged images. darktable-0004: credential leak eliminated. darktable-0005: 80× per keystroke.
What We Ask
Patches are ready for review.
- Confirm receipt and assign a GitHub issue reference (darktable-org/darktable).
- Assess severity — darktable-0003 fires on every map view redraw; darktable-0004 leaks credentials in debug mode.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the darktable team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.