Skip to content

Commit b4fb2c2

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 <christian@python.org>
1 parent 64947dc commit b4fb2c2

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
@@ -849,8 +849,8 @@ def test_enum_certificates(self):
849849
cert, enc, trust = element
850850
self.assertIsInstance(cert, bytes)
851851
self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
852-
self.assertIsInstance(trust, (set, bool))
853-
if isinstance(trust, set):
852+
self.assertIsInstance(trust, (frozenset, set, bool))
853+
if isinstance(trust, (frozenset, set)):
854854
trust_oids.update(trust)
855855

856856
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
@@ -5384,7 +5384,7 @@ parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags)
53845384
}
53855385
return PyErr_SetFromWindowsErr(error);
53865386
}
5387-
retval = PySet_New(NULL);
5387+
retval = PyFrozenSet_New(NULL);
53885388
if (retval == NULL) {
53895389
goto error;
53905390
}
@@ -5459,20 +5459,6 @@ ssl_collect_certificates(const char *store_name)
54595459
return hCollectionStore;
54605460
}
54615461

5462-
/* code from Objects/listobject.c */
5463-
5464-
static int
5465-
list_contains(PyListObject *a, PyObject *el)
5466-
{
5467-
Py_ssize_t i;
5468-
int cmp;
5469-
5470-
for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
5471-
cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
5472-
Py_EQ);
5473-
return cmp;
5474-
}
5475-
54765462
/*[clinic input]
54775463
_ssl.enum_certificates
54785464
store_name: str
@@ -5495,7 +5481,7 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
54955481
PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL;
54965482
PyObject *result = NULL;
54975483

5498-
result = PyList_New(0);
5484+
result = PySet_New(NULL);
54995485
if (result == NULL) {
55005486
return NULL;
55015487
}
@@ -5535,11 +5521,10 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
55355521
enc = NULL;
55365522
PyTuple_SET_ITEM(tup, 2, keyusage);
55375523
keyusage = NULL;
5538-
if (!list_contains((PyListObject*)result, tup)) {
5539-
if (PyList_Append(result, tup) < 0) {
5540-
Py_CLEAR(result);
5541-
break;
5542-
}
5524+
if (PySet_Add(result, tup) == -1) {
5525+
Py_CLEAR(result);
5526+
Py_CLEAR(tup);
5527+
break;
55435528
}
55445529
Py_CLEAR(tup);
55455530
}
@@ -5563,7 +5548,14 @@ _ssl_enum_certificates_impl(PyObject *module, const char *store_name)
55635548
return PyErr_SetFromWindowsErr(GetLastError());
55645549
}
55655550

5566-
return result;
5551+
/* convert set to list */
5552+
if (result == NULL) {
5553+
return NULL;
5554+
} else {
5555+
PyObject *lst = PySequence_List(result);
5556+
Py_DECREF(result);
5557+
return lst;
5558+
}
55675559
}
55685560

55695561
/*[clinic input]
@@ -5587,7 +5579,7 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
55875579
PyObject *crl = NULL, *enc = NULL, *tup = NULL;
55885580
PyObject *result = NULL;
55895581

5590-
result = PyList_New(0);
5582+
result = PySet_New(NULL);
55915583
if (result == NULL) {
55925584
return NULL;
55935585
}
@@ -5617,11 +5609,10 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
56175609
PyTuple_SET_ITEM(tup, 1, enc);
56185610
enc = NULL;
56195611

5620-
if (!list_contains((PyListObject*)result, tup)) {
5621-
if (PyList_Append(result, tup) < 0) {
5622-
Py_CLEAR(result);
5623-
break;
5624-
}
5612+
if (PySet_Add(result, tup) == -1) {
5613+
Py_CLEAR(result);
5614+
Py_CLEAR(tup);
5615+
break;
56255616
}
56265617
Py_CLEAR(tup);
56275618
}
@@ -5643,7 +5634,14 @@ _ssl_enum_crls_impl(PyObject *module, const char *store_name)
56435634
Py_XDECREF(result);
56445635
return PyErr_SetFromWindowsErr(GetLastError());
56455636
}
5646-
return result;
5637+
/* convert set to list */
5638+
if (result == NULL) {
5639+
return NULL;
5640+
} else {
5641+
PyObject *lst = PySequence_List(result);
5642+
Py_DECREF(result);
5643+
return lst;
5644+
}
56475645
}
56485646

56495647
#endif /* _MSC_VER */

0 commit comments

Comments
 (0)