java-topology/whitepaper/outreach/wine-0003.md
russell@unturf.com 6784cdf1cf feat: add 39 outreach docs (batches 6-8)
Batch 6 (9): dolibarr, jitsi-videobridge, zed, tryton, suricata,
  strawberry, zulip, zesarux, zephyr
Batch 7 (15): xonotic (4), xash3d (3), xenia, xtuple, zabbix (2),
  zathura, zebra, yabause, zephyr-0001
Batch 8 (15): woodpecker (2), wine (4), widelands (3), wesnoth (3),
  wekan (3)

Mix of CWE-407 and CWE-312.
2026-04-14 17:06:28 -04:00

3.2 KiB

Wine — CWE-407 Disclosure Brief (wine-0003)

2026-04-13 · Patch available — awaiting upstream merge

Finding

One O(n²) defect in Wine's crypt32 certificate chain validation. CRYPT_CheckSimpleChainForCycles() uses a nested double loop comparing every pair of certificates in a chain, producing O(N²) full CertCompareCertificate calls where N = chain elements. The developer left a comment acknowledging the defect: "O(n^2) - I don't think there's a faster way". There is.

The Defect

wine-0003 (PATCHED — MEDIUM): dlls/crypt32/chain.c:422

// CRYPT_CheckSimpleChainForCycles — fires during TLS handshake:
/* O(n^2) - I don't think there's a faster way */
for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
    for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
        if (CertCompareCertificate(X509_ASN_ENCODING,
         chain->rgpElement[i]->pCertContext->pCertInfo,
         chain->rgpElement[j]->pCertContext->pCertInfo))
            cyclicCertIndex = j;

Each CertCompareCertificate call compares two full CERT_INFO structs (serial number, issuer, subject, public key). For N chain elements this requires N*(N-1)/2 full struct comparisons.

Complexity Proof

N (chain elements) Comparisons (defect) Comparisons (fixed) Speedup
10 45 10 4.5x
50 1,225 50 24.5x
100 4,950 100 49.5x
200 19,900 200 99.5x
500 124,750 500 249.5x

Impact

CertGetCertificateChain() fires during TLS handshake validation for every HTTPS connection Wine applications make (wininet, secur32, schannel). Chains longer than 5-10 are rare in normal use, but an adversarially crafted certificate chain (penetration testing, fuzzing, malicious server) can induce quadratic cost.

The Fix

Build a hash set of SHA-1 thumbprints (20 bytes each) on a single forward pass. Each lookup is O(1). Total cost: O(N). Wine already has wine_rb_tree (red-black tree, O(log N)) in include/wine/rbtree.h:

// Before: O(N²) nested loop
for (i = 0; ...; i++)
    for (j = i + 1; ...; j++)
        if (CertCompareCertificate(...))

// After: O(N) single pass with hash set
struct wine_rb_tree seen;
wine_rb_init(&seen, thumbprint_compare);
for (i = 0; i < chain->cElement; i++) {
    BYTE thumb[20];
    compute_sha1_thumbprint(chain->rgpElement[i], thumb);
    if (wine_rb_get(&seen, thumb)) { cyclicCertIndex = i; break; }
    wine_rb_put(&seen, thumb, &chain->rgpElement[i]->entry);
}

Patch

Fix available: defects/wine-0003/patch/wine-0003-crypt32-chain-cycle-detection.patch

Single-file patch in dlls/crypt32/chain.c. Documents the O(N) replacement approach with wine_rb_tree pseudocode.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a reference on Wine Bugzilla or GitLab (wine/wine).
  2. Assess severity — fires during TLS handshake validation for every HTTPS connection.
  3. Coordinate a disclosure date — we target 90 days from first contact.
  4. We will credit the Wine team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.