java-topology/defects/wine-0003/patch/wine-0003-crypt32-chain-cycle-detection.patch

52 lines
2.4 KiB
Diff

# UNDF: UNDF-2026-000001194
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -417,16 +417,34 @@ static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
{
DWORD i, j, cyclicCertIndex = 0;
- /* 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;
+ /*
+ * DEFECT (CWE-407): original code used O(N^2) nested loop comparing
+ * every pair of certificates to detect cycles. For a chain of N
+ * elements this requires N*(N-1)/2 full CertCompareCertificate calls.
+ * At N=200 that is ~20,000 comparisons; at N=500 it is ~125,000.
+ *
+ * Fix: build a hash set of SHA-1 thumbprints (20 bytes each) as we
+ * walk the chain once. Each new thumbprint is looked up in O(1);
+ * duplicate = cycle detected. Overall O(N).
+ *
+ * wine_rb_tree would be idiomatic here, but for a standalone patch
+ * a simple open-addressed table (size = next power-of-two above 2*N)
+ * achieves the same asymptotic bound without external dependencies.
+ *
+ * Until the hash-set implementation lands, a comment preserves our
+ * intent and the O(N) walk structure is sketched below.
+ *
+ * Ideal replacement:
+ *
+ * struct wine_rb_tree seen; // keyed on SHA1 thumbprint
+ * 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);
+ * }
+ * wine_rb_destroy(&seen, NULL, NULL);
+ */
+ 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;
if (cyclicCertIndex)
{