-- v2: crypt32: Don't assert in Context_Release() on invalid refcount. crypt32/tests: Add a test for deleting and adding certs during enumeration. crypt32: Only remove cert from mem store list when deleting it. crypt32: Don't try to release zero-refcount context in MemStore_addContext(). crypt32: Factor out memstore_free_context() function.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/collectionstore.c | 2 +- dlls/crypt32/crypt32_private.h | 2 ++ dlls/crypt32/rootstore.c | 1 - dlls/crypt32/store.c | 14 ++++++++++++-- dlls/crypt32/tests/cert.c | 6 ++---- dlls/crypt32/tests/store.c | 20 +++++++++----------- dlls/cryptui/main.c | 4 +++- 7 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/dlls/crypt32/collectionstore.c b/dlls/crypt32/collectionstore.c index 0193be86222..d11ec89ecdf 100644 --- a/dlls/crypt32/collectionstore.c +++ b/dlls/crypt32/collectionstore.c @@ -265,7 +265,7 @@ static BOOL Collection_deleteCert(WINECRYPT_CERTSTORE *store, context_t *context TRACE("(%p, %p)\n", store, cert);
linked = (cert_t*)context->linked; - return CertDeleteCertificateFromStore(&linked->ctx); + return CRYPT_DeleteCertificateFromStore(&linked->ctx); }
static BOOL Collection_addCRL(WINECRYPT_CERTSTORE *store, context_t *crl, diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index a5aeae63168..9ac5b4a3067 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -417,6 +417,8 @@ context_t *Context_CreateDataContext(size_t contextSize, const context_vtbl_t *v */ context_t *Context_CreateLinkContext(unsigned contextSize, context_t *linked, struct WINE_CRYPTCERTSTORE*);
+BOOL CRYPT_DeleteCertificateFromStore(PCCERT_CONTEXT pCertContext); + /* Copies properties from fromContext to toContext. */ void Context_CopyProperties(const void *to, const void *from);
diff --git a/dlls/crypt32/rootstore.c b/dlls/crypt32/rootstore.c index 4c215d44943..3aca97aaab9 100644 --- a/dlls/crypt32/rootstore.c +++ b/dlls/crypt32/rootstore.c @@ -745,7 +745,6 @@ static void sync_trusted_roots_from_known_locations( HKEY key, HCERTSTORE cached /* Delete from cached so deleted certs do not participate in chain verification. */ CertDeleteCertificateFromStore( cert ); /* Restart enumeration as it is broken by deleting cert from store. */ - CertFreeCertificateContext( cert ); cert = NULL; }
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c index de2d760d2b0..f41aad2c3ff 100644 --- a/dlls/crypt32/store.c +++ b/dlls/crypt32/store.c @@ -934,10 +934,9 @@ PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(HCERTSTORE hCertStore, PCCERT_ return ret ? &ret->ctx : NULL; }
-BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) +BOOL CRYPT_DeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) { WINECRYPT_CERTSTORE *hcs; - TRACE("(%p)\n", pCertContext);
if (!pCertContext) @@ -951,6 +950,17 @@ BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) return hcs->vtbl->certs.delete(hcs, &cert_from_ptr(pCertContext)->base); }
+BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) +{ + BOOL ret; + + TRACE("(%p)\n", pCertContext); + + ret = CRYPT_DeleteCertificateFromStore(pCertContext); + CertFreeCertificateContext(pCertContext); + return ret; +} + BOOL WINAPI CertAddCRLContextToStore(HCERTSTORE hCertStore, PCCRL_CONTEXT pCrlContext, DWORD dwAddDisposition, PCCRL_CONTEXT* ppStoreContext) diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index c56578f1e21..396f6a2e390 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -4416,9 +4416,8 @@ static void testKeyProvInfo(void)
ret = CertDeleteCertificateFromStore(cert); ok(ret, "CertDeleteCertificateFromStore error %#lx\n", GetLastError()); - - CertFreeCertificateContext(cert); - CertCloseStore(store, 0); + ret = CertCloseStore(store, CERT_CLOSE_STORE_CHECK_FLAG); + ok(ret, "got error %#lx.\n", GetLastError()); }
static void test_VerifySignature(void) @@ -4518,7 +4517,6 @@ START_TEST(cert) testGetIssuerCert(); testLinkCert(); testKeyProvInfo(); - testCryptHashCert(); testCryptHashCert2(); testCertSigs(); diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 8809c5048b4..48f06c54f7c 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -269,10 +269,10 @@ static void testMemStore(void) /* close an empty store */ ret = CertCloseStore(NULL, 0); ok(ret, "CertCloseStore failed: %ld\n", GetLastError()); - ret = CertCloseStore(store1, 0); - ok(ret, "CertCloseStore failed: %ld\n", GetLastError()); - ret = CertCloseStore(store2, 0); - ok(ret, "CertCloseStore failed: %ld\n", GetLastError()); + ret = CertCloseStore(store1, CERT_CLOSE_STORE_CHECK_FLAG); + ok(ret, "got error %#lx.\n", GetLastError()); + ret = CertCloseStore(store2, CERT_CLOSE_STORE_CHECK_FLAG); + ok(ret, "got error %#lx.\n", GetLastError());
/* This seems nonsensical, but you can open a read-only mem store, only * it isn't read-only @@ -447,8 +447,8 @@ static void testRegStoreSavedCerts(void) ok (ret, "Failed to delete certificate from store at %ld, %lx\n", i, GetLastError());
CertFreeCertificateContext(cert1); - CertFreeCertificateContext(cert2); - CertCloseStore(store, 0); + ret = CertCloseStore(store, CERT_CLOSE_STORE_CHECK_FLAG); + ok(ret, "got error %#lx.\n", GetLastError());
res = RegOpenKeyExW(reg_store_saved_certs[i].key, key_name, 0, KEY_ALL_ACCESS, &key); ok (res, "The cert's registry entry should be absent at %li, %lx\n", i, GetLastError()); @@ -2754,7 +2754,7 @@ static void testEmptyStore(void)
res = CertAddCertificateContextToStore(cert->hCertStore, cert2, CERT_STORE_ADD_NEW, &cert3); ok(res, "CertAddCertificateContextToStore failed\n"); - todo_wine + todo_wine_if(cert3) ok(cert3 && cert3 != cert2, "Unexpected cert3\n"); ok(cert3->hCertStore == cert->hCertStore, "Unexpected hCertStore\n");
@@ -2764,8 +2764,6 @@ static void testEmptyStore(void) ok(res, "CertDeleteCertificateContextFromStore failed\n"); ok(cert3->hCertStore == cert->hCertStore, "Unexpected hCertStore\n");
- CertFreeCertificateContext(cert3); - store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); ok(store != NULL, "CertOpenStore failed\n");
@@ -2778,8 +2776,8 @@ static void testEmptyStore(void) ok(res, "CertDeleteCertificateContextFromStore failed\n"); ok(cert3->hCertStore == store, "Unexpected hCertStore\n");
- CertCloseStore(store, 0); - CertFreeCertificateContext(cert3); + res = CertCloseStore(store, CERT_CLOSE_STORE_CHECK_FLAG); + ok(res, "got error %#lx.\n", GetLastError());
res = CertCloseStore(cert->hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); ok(!res && GetLastError() == E_UNEXPECTED, "CertCloseStore returned: %x(%lx)\n", res, GetLastError()); diff --git a/dlls/cryptui/main.c b/dlls/cryptui/main.c index 2bd6b0b9f6a..2edab44ffd6 100644 --- a/dlls/cryptui/main.c +++ b/dlls/cryptui/main.c @@ -987,7 +987,9 @@ static void cert_mgr_do_remove(HWND hwnd) PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, selection);
- CertDeleteCertificateFromStore(cert); + /* CertDeleteCertificateFromStore() releases certificate, duplicate it to keep context stored + * in the list valid. */ + CertDeleteCertificateFromStore(CertDuplicateCertificateContext(cert)); } } while (selection >= 0); cert_mgr_clear_cert_selection(hwnd);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/store.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c index f41aad2c3ff..2ea9a898c6a 100644 --- a/dlls/crypt32/store.c +++ b/dlls/crypt32/store.c @@ -140,6 +140,11 @@ BOOL WINAPI I_CertUpdateStore(HCERTSTORE store1, HCERTSTORE store2, DWORD unk0, return TRUE; }
+static void memstore_free_context(context_t *context) +{ + Context_Free(context); +} + static BOOL MemStore_addContext(WINE_MEMSTORE *store, struct list *list, context_t *orig_context, context_t *existing, context_t **ret_context, BOOL use_link) { @@ -208,7 +213,7 @@ static BOOL MemStore_deleteContext(WINE_MEMSTORE *store, context_t *context) LeaveCriticalSection(&store->cs);
if(in_list && !context->ref) - Context_Free(context); + memstore_free_context(context); return TRUE; }
@@ -220,7 +225,7 @@ static void free_contexts(struct list *list) { TRACE("freeing %p\n", context); list_remove(&context->u.entry); - Context_Free(context); + memstore_free_context(context); } }
@@ -228,7 +233,7 @@ static void MemStore_releaseContext(WINECRYPT_CERTSTORE *store, context_t *conte { /* Free the context only if it's not in a list. Otherwise it may be reused later. */ if(list_empty(&context->u.entry)) - Context_Free(context); + memstore_free_context(context); }
static BOOL MemStore_addCert(WINECRYPT_CERTSTORE *store, context_t *cert,
From: Paul Gofman pgofman@codeweavers.com
That is essentially a no-op code, or that would be asserting in Context_Release(). --- dlls/crypt32/store.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c index 2ea9a898c6a..97b745e661e 100644 --- a/dlls/crypt32/store.c +++ b/dlls/crypt32/store.c @@ -162,8 +162,6 @@ static BOOL MemStore_addContext(WINE_MEMSTORE *store, struct list *list, context context->u.entry.prev->next = &context->u.entry; context->u.entry.next->prev = &context->u.entry; list_init(&existing->u.entry); - if(!existing->ref) - Context_Release(existing); }else { list_add_head(list, &context->u.entry); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/context.c | 2 ++ dlls/crypt32/crypt32_private.h | 1 + dlls/crypt32/store.c | 55 ++++++++++++++++++++-------------- 3 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/dlls/crypt32/context.c b/dlls/crypt32/context.c index 4a16d42296b..1a982f7bd0a 100644 --- a/dlls/crypt32/context.c +++ b/dlls/crypt32/context.c @@ -42,6 +42,7 @@ context_t *Context_CreateDataContext(size_t contextSize, const context_vtbl_t *v
context->vtbl = vtbl; context->ref = 1; + context->deleted_from_store = FALSE; context->linked = NULL;
store->vtbl->addref(store); @@ -64,6 +65,7 @@ context_t *Context_CreateLinkContext(unsigned int contextSize, context_t *linked memcpy(context_ptr(context), context_ptr(linked), contextSize); context->vtbl = linked->vtbl; context->ref = 1; + context->deleted_from_store = FALSE; context->linked = linked; context->properties = linked->properties; Context_AddRef(linked); diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index 9ac5b4a3067..6548dd36ab0 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -185,6 +185,7 @@ typedef struct { struct _context_t { const context_vtbl_t *vtbl; LONG ref; + BOOL deleted_from_store; struct WINE_CRYPTCERTSTORE *store; struct _context_t *linked; CONTEXT_PROPERTY_LIST *properties; diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c index 97b745e661e..3291d0522b6 100644 --- a/dlls/crypt32/store.c +++ b/dlls/crypt32/store.c @@ -80,6 +80,8 @@ typedef struct _WINE_MEMSTORE { WINECRYPT_CERTSTORE hdr; CRITICAL_SECTION cs; + /* Objects are deleteted from the main list upon deleting from store and from _enum lists + * upon deleting objects themselves. */ struct list certs; struct list crls; struct list ctls; @@ -142,6 +144,14 @@ BOOL WINAPI I_CertUpdateStore(HCERTSTORE store1, HCERTSTORE store2, DWORD unk0,
static void memstore_free_context(context_t *context) { + WINE_MEMSTORE *store = (WINE_MEMSTORE *)context->store; + + TRACE(".\n"); + + EnterCriticalSection(&store->cs); + list_remove(&context->u.entry); + LeaveCriticalSection(&store->cs); + Context_Free(context); }
@@ -157,11 +167,8 @@ static BOOL MemStore_addContext(WINE_MEMSTORE *store, struct list *list, context TRACE("adding %p\n", context); EnterCriticalSection(&store->cs); if (existing) { - context->u.entry.prev = existing->u.entry.prev; - context->u.entry.next = existing->u.entry.next; - context->u.entry.prev->next = &context->u.entry; - context->u.entry.next->prev = &context->u.entry; - list_init(&existing->u.entry); + list_add_before(&existing->u.entry, &context->u.entry); + existing->deleted_from_store = TRUE; }else { list_add_head(list, &context->u.entry); } @@ -184,34 +191,37 @@ static context_t *MemStore_enumContext(WINE_MEMSTORE *store, struct list *list, next = list_next(list, &prev->u.entry); Context_Release(prev); }else { - next = list_next(list, list); + next = list_head(list); + } + + while (next) + { + ret = LIST_ENTRY(next, context_t, u.entry); + if (!ret->deleted_from_store) + break; + next = list_next(list, next); } + if (!next) + ret = NULL; LeaveCriticalSection(&store->cs);
- if (!next) { + if (!ret) { SetLastError(CRYPT_E_NOT_FOUND); return NULL; }
- ret = LIST_ENTRY(next, context_t, u.entry); Context_AddRef(ret); return ret; }
static BOOL MemStore_deleteContext(WINE_MEMSTORE *store, context_t *context) { - BOOL in_list = FALSE; - - EnterCriticalSection(&store->cs); - if (!list_empty(&context->u.entry)) { - list_remove(&context->u.entry); - list_init(&context->u.entry); - in_list = TRUE; + if (!context->deleted_from_store) + { + context->deleted_from_store = TRUE; + if (!context->ref) + memstore_free_context(context); } - LeaveCriticalSection(&store->cs); - - if(in_list && !context->ref) - memstore_free_context(context); return TRUE; }
@@ -223,14 +233,15 @@ static void free_contexts(struct list *list) { TRACE("freeing %p\n", context); list_remove(&context->u.entry); - memstore_free_context(context); + if (!context->deleted_from_store) + memstore_free_context(context); } }
static void MemStore_releaseContext(WINECRYPT_CERTSTORE *store, context_t *context) { - /* Free the context only if it's not in a list. Otherwise it may be reused later. */ - if(list_empty(&context->u.entry)) + /* Free the context only if it is deleted from store. Otherwise it may be reused later. */ + if (context->deleted_from_store) memstore_free_context(context); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/tests/store.c | 81 +++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-)
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 48f06c54f7c..0e814ed6429 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -117,6 +117,19 @@ static const BYTE signedCTLWithCTLInnerContent[] = { 0x8e,0xe7,0x5f,0x76,0x2b,0xd1,0x6a,0x82,0xb3,0x30,0x25,0x61,0xf6,0x25,0x23, 0x57,0x6c,0x0b,0x47,0xb8 };
+static const BYTE certWithUsage[] = { 0x30, 0x81, 0x93, 0x02, 0x01, 0x01, 0x30, + 0x02, 0x06, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, + 0x30, 0x22, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x15, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, + 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x07, 0x30, 0x02, 0x06, 0x00, + 0x03, 0x01, 0x00, 0xa3, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x1d, + 0x25, 0x01, 0x01, 0xff, 0x04, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; + #define test_store_is_empty(store) _test_store_is_empty(__LINE__,store) static void _test_store_is_empty(unsigned line, HCERTSTORE store) { @@ -128,10 +141,16 @@ static void _test_store_is_empty(unsigned line, HCERTSTORE store)
static void testMemStore(void) { + static const BYTE cert1_hash[] = + { 0x4a, 0x7f, 0x32, 0x1f, 0xcf, 0x3b, 0xc0, 0x87, 0x48, 0x2b, 0xa1, 0x86, 0x54, 0x18, 0xe4, 0x3a, 0x0e, 0x53, 0x7e, 0x2b, }; + static const BYTE cert2_hash[] = + { 0xef, 0x7c, 0x6f, 0x27, 0x13, 0xff, 0x84, 0x14, 0x7c, 0xb5, 0xf0, 0x0c, 0x5a, 0x82, 0x91, 0x78, 0x96, 0x82, 0x19, 0x5c, }; HCERTSTORE store1, store2; - PCCERT_CONTEXT context; + PCCERT_CONTEXT context, stored[2], copy; + unsigned int count; + BYTE hash[20]; BOOL ret; - DWORD GLE; + DWORD GLE, size;
/* NULL provider */ store1 = CertOpenStore(0, 0, 0, 0, NULL); @@ -266,6 +285,64 @@ static void testMemStore(void) ok(!context, "Expected an empty store\n"); }
+ /* Test deleting and adding cert during enumeration. */ + ret = CertAddEncodedCertificateToStore(store1, X509_ASN_ENCODING, bigCert, + sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL); + ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n", GetLastError()); + ret = CertAddEncodedCertificateToStore(store1, X509_ASN_ENCODING, bigCert2, + sizeof(bigCert2), CERT_STORE_ADD_ALWAYS, NULL); + ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n", GetLastError()); + + context = NULL; + count = 0; + while ((context = CertEnumCertificatesInStore(store1, context))) + { + ok(count < 2, "got %u.\n", count); + stored[count] = CertDuplicateCertificateContext(context); + ++count; + } + ok(count == 2, "got %u.\n", count); + + count = 0; + context = NULL; + while ((context = CertEnumCertificatesInStore(store1, context))) + { + size = sizeof(hash); + ret = CertGetCertificateContextProperty(context, CERT_HASH_PROP_ID, hash, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); + ok(!memcmp(hash, cert1_hash, sizeof(hash)), "wrong cert.\n"); + copy = CertDuplicateCertificateContext(context); + ok(copy == context, "got %p, %p.\n", copy, context); + ret = CertDeleteCertificateFromStore(copy); + ok(ret, "CertDeleteCertificateFromStore failed: %08lx\n", GetLastError()); + if (!count) + { + ret = CertDeleteCertificateFromStore(stored[1]); + ok(ret, "CertDeleteCertificateFromStore failed: %08lx\n", GetLastError()); + ret = CertAddEncodedCertificateToStore(store1, X509_ASN_ENCODING, certWithUsage, + sizeof(certWithUsage), CERT_STORE_ADD_ALWAYS, NULL); + ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n", GetLastError()); + } + ++count; + } + ok(count == 1, "got %u.\n", count); + + count = 0; + context = NULL; + while ((context = CertEnumCertificatesInStore(store1, context))) + { + size = sizeof(hash); + ret = CertGetCertificateContextProperty(context, CERT_HASH_PROP_ID, hash, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); + ok(!memcmp(hash, cert2_hash, sizeof(hash)), "wrong cert.\n"); + copy = CertDuplicateCertificateContext(context); + ret = CertDeleteCertificateFromStore(copy); + ok(ret, "CertDeleteCertificateFromStore failed: %08lx\n", GetLastError()); + ++count; + } + ok(count == 1, "got %u.\n", count); + CertFreeCertificateContext(stored[0]); + /* close an empty store */ ret = CertCloseStore(NULL, 0); ok(ret, "CertCloseStore failed: %ld\n", GetLastError());
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/context.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/crypt32/context.c b/dlls/crypt32/context.c index 1a982f7bd0a..812fb51e36b 100644 --- a/dlls/crypt32/context.c +++ b/dlls/crypt32/context.c @@ -110,7 +110,12 @@ void Context_Release(context_t *context) LONG ref = InterlockedDecrement(&context->ref);
TRACE("(%p) ref=%ld\n", context, ref); - assert(ref >= 0); + + if (ref < 0) + { + ERR( "ref %ld.\n", ref ); + return; + }
if (!ref) { WINECRYPT_CERTSTORE *store = context->store;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=151044
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4306: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 00000000020500F6, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
v2: - add 'deleted_from_store' variable to cert context instead of adding an extra list; - remove a pre-existing dead code (in a newly added patch 3) which was trying to release zero reference context (which would only cause an assertion if was ever called).