Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Cached EC curves leak when the Linux module is unloaded after composite key allocation

Offen
#63 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
55/100
Issue-Typ
Bug
Klarheit
Größtenteils klar
Aktivitätsstatus
Aktiv
Tech-Stack
c, linux

Rechercherichtung

Start with SymCryptCompositeMlDsakeyAllocate in lib/composite_mldsa.c, then trace SymCryptGetCachedEcurve and the static cache in lib/ec_internal_curves.c. Compare this with SymCryptModuleDestructor in modules/posix/common/module.c and run the ASan allocate and repeat modes from the reproducer. Done means repeated unloads no longer report the cached-curve allocations, or the supported module lifetime is explicitly documented.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Summary

On Linux, allocating and freeing composite ML-DSA keys initializes cached EC curves that are not released when libsymcrypt.so is subsequently unloaded with dlclose.

A standalone reproducer using only SymCrypt's public APIs leaks 10,240 bytes in two allocations per load/unload cycle after using the P-256 and P-384 variants. Three cycles leak 30,720 bytes in six allocations. No OpenSSL or SCOSSL dependency is required, and key generation/signing is not necessary to reproduce it.

All caller-owned composite keys are freed before unloading the module. This report concerns module-lifetime resource cleanup, not a cryptographic correctness finding.

Environment

  • Public main commit: 286762b7730e2b780678f5ab11fef2b1bad639e0 (SymCrypt 103.13.0).
  • Ubuntu 24.04.4 LTS, x86-64, running under WSL.
  • GCC 13.3.0.
  • Normal RelWithDebInfo Linux generic shared-module build.
  • SYMCRYPT_FIPS_BUILD=ON and SYMCRYPT_FIPS_POSTPROCESS=ON; no self-tests or integrity checks disabled.
  • Only the reproducer is ASan-instrumented. LeakSanitizer intercepts the shared library's allocations; the SymCrypt library itself does not need sanitizer instrumentation.

Reproduction

Build the pinned revision using the normal repository prerequisites, including scripts/requirements.txt:

git checkout 286762b7730e2b780678f5ab11fef2b1bad639e0
cmake -S . -B build-unload -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSYMCRYPT_FIPS_BUILD=ON -DSYMCRYPT_FIPS_POSTPROCESS=ON
cmake --build build-unload -j4

Save the following as symcrypt-unload-repro.c in the repository root:

#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include "symcrypt.h"

typedef VOID (SYMCRYPT_CALL *init_fn)(UINT32, UINT32);
typedef PSYMCRYPT_COMPOSITE_MLDSAKEY (SYMCRYPT_CALL *allocate_fn)(
    SYMCRYPT_COMPOSITE_MLDSA_PARAMS);
typedef VOID (SYMCRYPT_CALL *free_fn)(PSYMCRYPT_COMPOSITE_MLDSAKEY);

int main(int argc, char **argv)
{
    if (argc != 3 || (strcmp(argv[2], "allocate") &&
        strcmp(argv[2], "repeat") && strcmp(argv[2], "load-only") &&
        strcmp(argv[2], "keep-loaded")))
    {
        fprintf(stderr, "Usage: %s LIBRARY allocate|repeat|load-only|keep-loaded\n", argv[0]);
        return 2;
    }
    int cycles = strcmp(argv[2], "repeat") == 0 ? 3 : 1;
    for (int i = 0; i < cycles; ++i)
    {
        void *module = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
        if (module == NULL)
        {
            fprintf(stderr, "dlopen: %s\n", dlerror());
            return 2;
        }
        init_fn init = (init_fn)dlsym(module, "SymCryptModuleInit");
        allocate_fn allocate = (allocate_fn)dlsym(module, "SymCryptCompositeMlDsakeyAllocate");
        free_fn release = (free_fn)dlsym(module, "SymCryptCompositeMlDsakeyFree");
        if (init == NULL || allocate == NULL || release == NULL)
        {
            fprintf(stderr, "Required exported function missing\n");
            dlclose(module);
            return 2;
        }
        init(103, 12);
        if (strcmp(argv[2], "load-only") != 0)
        {
            PSYMCRYPT_COMPOSITE_MLDSAKEY a = allocate(
                SYMCRYPT_COMPOSITE_MLDSA_PARAMS_MLDSA44_ECDSA_P256_SHA256);
            PSYMCRYPT_COMPOSITE_MLDSAKEY b = allocate(
                SYMCRYPT_COMPOSITE_MLDSA_PARAMS_MLDSA65_ECDSA_P384_SHA512);
            int ok = a != NULL && b != NULL;
            if (a != NULL)
                release(a);
            if (b != NULL)
                release(b);
            if (!ok)
            {
                fprintf(stderr, "Key allocation failed\n");
                dlclose(module);
                return 2;
            }
        }
        if (strcmp(argv[2], "keep-loaded") != 0 && dlclose(module) != 0)
        {
            fprintf(stderr, "dlclose: %s\n", dlerror());
            return 2;
        }
        printf("Completed cycle %d (%s)\n", i + 1, argv[2]);
    }
    return 0;
}

Compile without linking SymCrypt directly, so that dlclose can unload it:

gcc -std=c11 -Wall -Wextra -Werror -Wno-unknown-pragmas -g -O0 -fsanitize=address -fno-omit-frame-pointer -Iinc symcrypt-unload-repro.c -ldl -o symcrypt-unload-repro

Run each mode in a separate process, without preloading SymCrypt:

ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 ./symcrypt-unload-repro ./build-unload/module/generic/libsymcrypt.so load-only
ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 ./symcrypt-unload-repro ./build-unload/module/generic/libsymcrypt.so allocate
ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 ./symcrypt-unload-repro ./build-unload/module/generic/libsymcrypt.so repeat
ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 ./symcrypt-unload-repro ./build-unload/module/generic/libsymcrypt.so keep-loaded

Observed results

Mode Behavior LeakSanitizer result Exit status
load-only Load/init/unload, no composite allocation No leak reported 0
allocate Allocate P-256/P-384 composite keys, free both, unload 10,240 bytes in 2 allocations 1
repeat Repeat the allocate/free/unload sequence three times 30,720 bytes in 6 allocations 1
keep-loaded Allocate/free both keys, retain module until process exit No leak reported 0

The allocate report contains two direct leaks of 5,120 bytes each, with allocation stacks through aligned_alloc and the two calls to SymCryptCompositeMlDsakeyAllocate:

SUMMARY: AddressSanitizer: 10240 byte(s) leaked in 2 allocation(s).

The repeated-unload case reports:

SUMMARY: AddressSanitizer: 30720 byte(s) leaked in 6 allocation(s).

SymCrypt stack frames appear as <unknown module> after dlclose. Keeping the module resident leaves the caches reachable and avoids the report; that is a lifetime workaround, not an unload-cleanup fix.

Relevant source

Expected behavior

Once all caller-owned keys have been freed, unloading the module should release module-owned curve caches, rather than accumulating allocations across repeated loads.

If unloading after composite use is intentionally unsupported, please clarify/document that lifetime requirement. Otherwise, a module-owned teardown path appears appropriate; callers should not free internal shared curve pointers.

Vorherrschende Sprache
C
Sterne
890
Forks
91
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Entwicklungsumgebung

Die Einrichtungsdateien dieses Projekts haben wir noch nicht geprüft. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus microsoft/SymCrypt

Alle Issues in microsoft/SymCrypt

Ähnliche Issues

Weitere Issues zu C

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.