52 lines
2.4 KiB
Diff
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)
|
|
{
|