Skip to content

Commit fdd17ab

Browse files
authored
bpo-35941: Fix performance regression in SSL certificate code (GH-12610)
Accumulate certificates in a set instead of doing a costly list contain operation. A Windows cert store can easily contain over hundred certificates. The old code would result in way over 5,000 comparison operations Signed-off-by: Christian Heimes <[email protected]>
1 parent 74b7413 commit fdd17ab

File tree

2 files changed

+29
-31
lines changed

2 files changed

+29
-31
lines changed

Lib/test/test_ssl.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -832,8 +832,8 @@ def test_enum_certificates(self):
832832
cert, enc, trust = element
833833
self.assertIsInstance(cert, bytes)
834834
self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
835-
self.assertIsInstance(trust, (set, bool))
836-
if isinstance(trust, set):
835+
self.assertIsInstance(trust, (frozenset, set, bool))
836+
if isinstance(trust, (frozenset, set)):
837837
trust_oids.update(trust)
838838

839839
serverAuth = "1.3.6.1.5.5.7.3.1"

Modules/_ssl.c

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5517,7 +5517,7 @@ parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags)
55175517
}
55185518
return PyErr_SetFromWindowsErr(error);
55195519
}
5520-
retval = PySet_New(NULL);
5520+
retval = PyFrozenSet_New(NULL);
55215521
if (retval == NULL) {
55225522
goto error;
55235523
}
@@ -5592,20 +5592,6 @@ ssl_collect_certificates(const char *store_name)
55925592
return hCollectionStore;
55935593
}
55945594

5595-
/* code from Objects/listobject.c */
5596-
5597-
static int
5598-
list_contains(PyListObject *a, PyObject *el)
5599-
{
5600-
Py_ssize_t i;
5601-
int cmp;
5602-
5603-
for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
5604-
cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
5605-
Py_EQ);
5606-
return cmp;
5607-
}
5608-
56095595
/*[clinic input]
56105596
_ssl.enum_certificates
56115597
store_name: str
@@ -5628,7 +5614,7 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
56285614
PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL;
56295615
PyObject *result = NULL;
56305616

5631-
result = PyList_New(0);
5617+
result = PySet_New(NULL);
56325618
if (result == NULL) {
56335619
return NULL;
56345620
}
@@ -5668,11 +5654,10 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
56685654
enc = NULL;
56695655
PyTuple_SET_ITEM(tup, 2, keyusage);
56705656
keyusage = NULL;
5671-
if (!list_contains((PyListObject*)result, tup)) {
5672-
if (PyList_Append(result, tup) < 0) {
5673-
Py_CLEAR(result);
5674-
break;
5675-
}
5657+
if (PySet_Add(result, tup) == -1) {
5658+
Py_CLEAR(result);
5659+
Py_CLEAR(tup);
5660+
break;
56765661
}
56775662
Py_CLEAR(tup);
56785663
}
@@ -5696,7 +5681,14 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
56965681
return PyErr_SetFromWindowsErr(GetLastError());
56975682
}
56985683

5699-
return result;
5684+
/* convert set to list */
5685+
if (result == NULL) {
5686+
return NULL;
5687+
} else {
5688+
PyObject *lst = PySequence_List(result);
5689+
Py_DECREF(result);
5690+
return lst;
5691+
}
57005692
}
57015693

57025694
/*[clinic input]
@@ -5720,7 +5712,7 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
57205712
PyObject *crl = NULL, *enc = NULL, *tup = NULL;
57215713
PyObject *result = NULL;
57225714

5723-
result = PyList_New(0);
5715+
result = PySet_New(NULL);
57245716
if (result == NULL) {
57255717
return NULL;
57265718
}
@@ -5750,11 +5742,10 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
57505742
PyTuple_SET_ITEM(tup, 1, enc);
57515743
enc = NULL;
57525744

5753-
if (!list_contains((PyListObject*)result, tup)) {
5754-
if (PyList_Append(result, tup) < 0) {
5755-
Py_CLEAR(result);
5756-
break;
5757-
}
5745+
if (PySet_Add(result, tup) == -1) {
5746+
Py_CLEAR(result);
5747+
Py_CLEAR(tup);
5748+
break;
57585749
}
57595750
Py_CLEAR(tup);
57605751
}
@@ -5776,7 +5767,14 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
57765767
Py_XDECREF(result);
57775768
return PyErr_SetFromWindowsErr(GetLastError());
57785769
}
5779-
return result;
5770+
/* convert set to list */
5771+
if (result == NULL) {
5772+
return NULL;
5773+
} else {
5774+
PyObject *lst = PySequence_List(result);
5775+
Py_DECREF(result);
5776+
return lst;
5777+
}
57805778
}
57815779

57825780
#endif /* _MSC_VER */

0 commit comments

Comments
 (0)