Add support for generating SHA-256 hashes via CryptCATAdminCalcHashFromFileHandle2, which in turn requires CryptCATAdminAcquireContext2 in order to be able to specify the hashing algorithm.
-- v5: wintrust/tests: Add CryptCATAdminCalcHashFromFileHandle2() tests wintrust: Implement CryptCATAdminCalcHashFromFileHandle2() wintrust/tests: Add CryptCATAdminAcquireContext2() tests wintrust: Implement CryptCATAdminAcquireContext2()
From: Elias Norberg elias@aisle.se
--- dlls/wintrust/crypt.c | 63 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-)
diff --git a/dlls/wintrust/crypt.c b/dlls/wintrust/crypt.c index b2c5e31e87f..54bc558feeb 100644 --- a/dlls/wintrust/crypt.c +++ b/dlls/wintrust/crypt.c @@ -58,6 +58,9 @@ struct catadmin DWORD magic; WCHAR path[MAX_PATH]; HANDLE find; + ALG_ID alg; + const WCHAR *providerName; + DWORD providerType; };
struct catinfo @@ -97,6 +100,29 @@ static HCATINFO create_catinfo(const WCHAR *filename) */ BOOL WINAPI CryptCATAdminAcquireContext(HCATADMIN *catAdmin, const GUID *sys, DWORD dwFlags) +{ + TRACE("%p %s %lx\n", catAdmin, debugstr_guid(sys), dwFlags); + return CryptCATAdminAcquireContext2(catAdmin, sys, NULL, NULL, dwFlags); +} + +/*********************************************************************** + * CryptCATAdminAcquireContext2 (WINTRUST.@) + * Get a catalog administrator context handle. + * + * PARAMS + * catAdmin [O] Pointer to the context handle. + * sys [I] Pointer to a GUID for the needed subsystem. + * algorithm [I] String of hashing algorithm to use for catalog (SHA1/SHA256). + * policy [I] Pointer to policy structure for checking strong signatures. + * dwFlags [I] Reserved. + * + * RETURNS + * Success: TRUE. catAdmin contains the context handle. + * Failure: FALSE. + * + */ +BOOL WINAPI CryptCATAdminAcquireContext2(HCATADMIN *catAdmin, const GUID *sys, const WCHAR *algorithm, + const CERT_STRONG_SIGN_PARA *policy, DWORD dwFlags) { static const WCHAR catroot[] = {'\','c','a','t','r','o','o','t',0}; @@ -110,20 +136,44 @@ BOOL WINAPI CryptCATAdminAcquireContext(HCATADMIN *catAdmin,
WCHAR catroot_dir[MAX_PATH]; struct catadmin *ca; + ALG_ID alg; + const WCHAR *providerName; + DWORD providerType;
- TRACE("%p %s %lx\n", catAdmin, debugstr_guid(sys), dwFlags); + TRACE("%p %s %s %p %lx\n", catAdmin, debugstr_guid(sys), debugstr_w(algorithm), policy, dwFlags);
if (!catAdmin || dwFlags) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + + if (policy != NULL) + FIXME("strong policy parameter is unimplemented\n"); + + if (algorithm == NULL || wcscmp(algorithm, BCRYPT_SHA1_ALGORITHM) == 0) { + alg = CALG_SHA1; + providerName = MS_DEF_PROV_W; + providerType = PROV_RSA_FULL; + } else if (wcscmp(algorithm, BCRYPT_SHA256_ALGORITHM) == 0) { + alg = CALG_SHA_256; + providerName = MS_ENH_RSA_AES_PROV_W; + providerType = PROV_RSA_AES; + } else { + SetLastError(NTE_BAD_ALGID); + return FALSE; + } + if (!(ca = malloc(sizeof(*ca)))) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
+ ca->alg = alg; + ca->providerName = providerName; + ca->providerType = providerType; + GetSystemDirectoryW(catroot_dir, MAX_PATH); lstrcatW(catroot_dir, catroot);
@@ -146,17 +196,6 @@ BOOL WINAPI CryptCATAdminAcquireContext(HCATADMIN *catAdmin, return TRUE; }
-/*********************************************************************** - * CryptCATAdminAcquireContext2 (WINTRUST.@) - */ -BOOL WINAPI CryptCATAdminAcquireContext2(HCATADMIN *catAdmin, const GUID *sys, const WCHAR *algorithm, - const CERT_STRONG_SIGN_PARA *policy, DWORD flags) -{ - FIXME("%p %s %s %p %lx stub\n", catAdmin, debugstr_guid(sys), debugstr_w(algorithm), policy, flags); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - /*********************************************************************** * CryptCATAdminAddCatalog (WINTRUST.@) */
From: Elias Norberg elias@aisle.se
--- dlls/wintrust/tests/crypt.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/dlls/wintrust/tests/crypt.c b/dlls/wintrust/tests/crypt.c index ca246776046..50b62fdc952 100644 --- a/dlls/wintrust/tests/crypt.c +++ b/dlls/wintrust/tests/crypt.c @@ -99,6 +99,7 @@ static const BYTE test_catalog[] = { };
static BOOL (WINAPI * pCryptCATAdminAcquireContext)(HCATADMIN*, const GUID*, DWORD); +static BOOL (WINAPI * pCryptCATAdminAcquireContext2)(HCATADMIN*, const GUID*, const WCHAR *, const CERT_STRONG_SIGN_PARA *, DWORD); static BOOL (WINAPI * pCryptCATAdminReleaseContext)(HCATADMIN, DWORD); static BOOL (WINAPI * pCryptCATAdminCalcHashFromFileHandle)(HANDLE hFile, DWORD*, BYTE*, DWORD); static HCATINFO (WINAPI * pCryptCATAdminAddCatalog)(HCATADMIN, PWSTR, PWSTR, DWORD); @@ -130,6 +131,7 @@ static void InitFunctionPtrs(void) }
WINTRUST_GET_PROC(CryptCATAdminAcquireContext) + WINTRUST_GET_PROC(CryptCATAdminAcquireContext2) WINTRUST_GET_PROC(CryptCATAdminReleaseContext) WINTRUST_GET_PROC(CryptCATAdminCalcHashFromFileHandle) WINTRUST_GET_PROC(CryptCATAdminAddCatalog) @@ -164,6 +166,7 @@ static void test_context(void) BOOL ret; HCATADMIN hca; static GUID unknown = { 0xC689AABA, 0x8E78, 0x11D0, { 0x8C,0x47,0x00,0xC0,0x4F,0xC2,0x95,0xEE }}; /* WINTRUST.DLL */ + static const WCHAR unknown_alg[] = {'A', 'L', 'G', '-', 'U', 'N', 'K', 'N', 'O', 'W', 'N', '\0'}; CHAR dummydir[MAX_PATH]; DWORD attrs;
@@ -301,6 +304,28 @@ static void test_context(void) ret = pCryptCATAdminReleaseContext(hca, 0); ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); } + + /* Specify SHA-1 algorithm */ + ret = pCryptCATAdminAcquireContext2(&hca, &unknown, BCRYPT_SHA1_ALGORITHM, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hca != NULL, "Expected a context handle, got NULL\n"); + + ret = pCryptCATAdminReleaseContext(hca, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + + /* Specify SHA-256 algorithm */ + ret = pCryptCATAdminAcquireContext2(&hca, &unknown, BCRYPT_SHA256_ALGORITHM, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hca != NULL, "Expected a context handle, got NULL\n"); + + ret = pCryptCATAdminReleaseContext(hca, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + + /* Set unknown algorithm - should return failure */ + ret = pCryptCATAdminAcquireContext2(&hca, &unknown, unknown_alg, NULL, 0); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == NTE_BAD_ALGID, "Expected NTE_BAD_ALGID, got %ld\n", GetLastError()); + }
/* TODO: Check whether SHA-1 is the algorithm that's always used */
From: Elias Norberg elias@aisle.se
--- dlls/wintrust/crypt.c | 84 +++++++++++++++++++++++++++++++------ dlls/wintrust/wintrust.spec | 2 +- 2 files changed, 73 insertions(+), 13 deletions(-)
diff --git a/dlls/wintrust/crypt.c b/dlls/wintrust/crypt.c index 54bc558feeb..21248d7fcd1 100644 --- a/dlls/wintrust/crypt.c +++ b/dlls/wintrust/crypt.c @@ -327,28 +327,47 @@ done: return ret; }
-/*********************************************************************** - * CryptCATAdminCalcHashFromFileHandle (WINTRUST.@) - */ -BOOL WINAPI CryptCATAdminCalcHashFromFileHandle(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags) +static BOOL catadmin_calc_hash_from_filehandle(HCATADMIN catAdmin, HANDLE hFile, DWORD *pcbHash, + BYTE *pbHash, DWORD dwFlags) { BOOL ret = FALSE; - - TRACE("%p %p %p %lx\n", hFile, pcbHash, pbHash, dwFlags); + struct catadmin *ca = catAdmin; + ALG_ID alg = CALG_SHA1; + const WCHAR *providerName = MS_DEF_PROV_W; + DWORD providerType = PROV_RSA_FULL; + DWORD hashLength;
if (!hFile || !pcbHash || dwFlags) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - if (*pcbHash < 20) - { - *pcbHash = 20; + + if (ca) { + alg = ca->alg; + providerName = ca->providerName; + providerType = ca->providerType; + } + + switch (alg) { + case CALG_SHA1: + hashLength = 20; + break; + case CALG_SHA_256: + hashLength = 32; + break; + default: + FIXME("unsupported algorithm %x\n", alg); + return FALSE; + } + + if (*pcbHash < hashLength) { + *pcbHash = hashLength; SetLastError(ERROR_INSUFFICIENT_BUFFER); return TRUE; }
- *pcbHash = 20; + *pcbHash = hashLength; if (pbHash) { HCRYPTPROV prov; @@ -361,13 +380,13 @@ BOOL WINAPI CryptCATAdminCalcHashFromFileHandle(HANDLE hFile, DWORD *pcbHash, BY SetLastError(ERROR_OUTOFMEMORY); return FALSE; } - ret = CryptAcquireContextW(&prov, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + ret = CryptAcquireContextW(&prov, NULL, providerName, providerType, CRYPT_VERIFYCONTEXT); if (!ret) { free(buffer); return FALSE; } - ret = CryptCreateHash(prov, CALG_SHA1, 0, 0, &hash); + ret = CryptCreateHash(prov, alg, 0, 0, &hash); if (!ret) { free(buffer); @@ -391,6 +410,47 @@ BOOL WINAPI CryptCATAdminCalcHashFromFileHandle(HANDLE hFile, DWORD *pcbHash, BY return ret; }
+/*********************************************************************** + * CryptCATAdminCalcHashFromFileHandle (WINTRUST.@) + */ +BOOL WINAPI CryptCATAdminCalcHashFromFileHandle(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags) +{ + TRACE("%p %p %p %lx\n", hFile, pcbHash, pbHash, dwFlags); + return catadmin_calc_hash_from_filehandle(NULL, hFile, pcbHash, pbHash, dwFlags); +} + +/*********************************************************************** + * CryptCATAdminCalcHashFromFileHandle2 (WINTRUST.@) + * + * Calculate hash for a specific file using a catalog administrator context. + * + * PARAMS + * catAdmin [I] Catalog administrator context handle. + * hFile [I] Handle for the file to hash. + * pcbHash [I] Pointer to the length of the hash. + * pbHash [O] Pointer to the buffer that will store that hash + * dwFlags [I] Reserved. + * + * RETURNS + * Success: TRUE. If pcbHash is too small, LastError will be set to ERROR_INSUFFICIENT_BUFFER. + * pbHash contains the computed hash, if supplied. + * Failure: FALSE. + * + */ +BOOL WINAPI CryptCATAdminCalcHashFromFileHandle2(HCATADMIN catAdmin, HANDLE hFile, DWORD *pcbHash, + BYTE *pbHash, DWORD dwFlags) +{ + TRACE("%p %p %p %p %lx\n", catAdmin, hFile, pcbHash, pbHash, dwFlags); + + if (!catAdmin || ((struct catadmin *)catAdmin)->magic != CATADMIN_MAGIC) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return catadmin_calc_hash_from_filehandle(catAdmin, hFile, pcbHash, pbHash, dwFlags); +} + + /*********************************************************************** * CryptCATAdminEnumCatalogFromHash (WINTRUST.@) */ diff --git a/dlls/wintrust/wintrust.spec b/dlls/wintrust/wintrust.spec index 06db4a1159e..79e9bd32a75 100644 --- a/dlls/wintrust/wintrust.spec +++ b/dlls/wintrust/wintrust.spec @@ -7,7 +7,7 @@ @ stdcall CryptCATAdminAcquireContext2(ptr ptr wstr ptr long) @ stdcall CryptCATAdminAddCatalog(long wstr wstr long) @ stdcall CryptCATAdminCalcHashFromFileHandle(long ptr ptr long) -#@ stub CryptCATAdminCalcHashFromFileHandle2 +@ stdcall CryptCATAdminCalcHashFromFileHandle2(ptr ptr ptr ptr long) @ stdcall CryptCATAdminEnumCatalogFromHash(long ptr long long ptr) @ stub CryptCATAdminPauseServiceForBackup @ stdcall CryptCATAdminReleaseCatalogContext(long long long)
From: Elias Norberg elias@aisle.se
--- dlls/wintrust/tests/crypt.c | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+)
diff --git a/dlls/wintrust/tests/crypt.c b/dlls/wintrust/tests/crypt.c index 50b62fdc952..84675dae8b7 100644 --- a/dlls/wintrust/tests/crypt.c +++ b/dlls/wintrust/tests/crypt.c @@ -102,6 +102,7 @@ static BOOL (WINAPI * pCryptCATAdminAcquireContext)(HCATADMIN*, const GUID*, DWO static BOOL (WINAPI * pCryptCATAdminAcquireContext2)(HCATADMIN*, const GUID*, const WCHAR *, const CERT_STRONG_SIGN_PARA *, DWORD); static BOOL (WINAPI * pCryptCATAdminReleaseContext)(HCATADMIN, DWORD); static BOOL (WINAPI * pCryptCATAdminCalcHashFromFileHandle)(HANDLE hFile, DWORD*, BYTE*, DWORD); +static BOOL (WINAPI * pCryptCATAdminCalcHashFromFileHandle2)(HCATADMIN, HANDLE hFile, DWORD*, BYTE*, DWORD); static HCATINFO (WINAPI * pCryptCATAdminAddCatalog)(HCATADMIN, PWSTR, PWSTR, DWORD); static BOOL (WINAPI * pCryptCATAdminRemoveCatalog)(HCATADMIN, LPCWSTR, DWORD); static BOOL (WINAPI * pCryptCATAdminReleaseCatalogContext)(HCATADMIN, HCATINFO, DWORD); @@ -134,6 +135,7 @@ static void InitFunctionPtrs(void) WINTRUST_GET_PROC(CryptCATAdminAcquireContext2) WINTRUST_GET_PROC(CryptCATAdminReleaseContext) WINTRUST_GET_PROC(CryptCATAdminCalcHashFromFileHandle) + WINTRUST_GET_PROC(CryptCATAdminCalcHashFromFileHandle2) WINTRUST_GET_PROC(CryptCATAdminAddCatalog) WINTRUST_GET_PROC(CryptCATAdminRemoveCatalog) WINTRUST_GET_PROC(CryptCATAdminReleaseCatalogContext) @@ -332,10 +334,12 @@ static void test_context(void) static void test_calchash(void) { BOOL ret; + HCATADMIN hca_sha1, hca_sha256; HANDLE file; DWORD hashsize = 0; BYTE* hash; BYTE expectedhash[20] = {0x3a,0xa1,0x19,0x08,0xec,0xa6,0x0d,0x2e,0x7e,0xcc,0x7a,0xca,0xf5,0xb8,0x2e,0x62,0x6a,0xda,0xf0,0x19}; + BYTE expectedhash_sha256[32] = { 0x8a, 0xfd, 0x8c, 0xec, 0xdd, 0xe3, 0x0b, 0xaa, 0x2f, 0x1c, 0x3f, 0x61, 0xaf, 0xdf, 0x24, 0x84, 0x99, 0x8b, 0xe3, 0xcf, 0xda, 0xff, 0x0c, 0x5e, 0xa8, 0x68, 0xe8, 0xea, 0x94, 0x1e, 0x90, 0xe2}; CHAR temp[MAX_PATH]; DWORD written;
@@ -362,6 +366,15 @@ static void test_calchash(void) "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); CloseHandle(file);
+ /* Correct catadmin, rest is NULL */ + ret = pCryptCATAdminAcquireContext2(&hca_sha1, NULL, BCRYPT_SHA1_ALGORITHM, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha1, NULL, NULL, NULL, 0); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); + /* All OK, but dwFlags set to 1 */ file = CreateFileA(selfname, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); SetLastError(0xdeadbeef); @@ -419,8 +432,82 @@ static void test_calchash(void) !memcmp(hash, expectedhash, sizeof(expectedhash)), "Hashes didn't match\n"); CloseHandle(file); + HeapFree(GetProcessHeap(), 0, hash); + + /* Calculate hash with SHA-1 specified as algorithm */ + file = CreateFileA(selfname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + SetLastError(0xdeadbeef); + hashsize = 0; + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha1, file, &hashsize, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hashsize == 20," Expected a hash size of 20, got %ld\n", hashsize); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError()); + hash = HeapAlloc(GetProcessHeap(), 0, hashsize); + SetLastError(0xdeadbeef); + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha1, file, &hashsize, hash, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hashsize == 20," Expected a hash size of 20, got %ld\n", hashsize); + ok(GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %ld\n", GetLastError()); + CloseHandle(file); + HeapFree(GetProcessHeap(), 0, hash); + + /* Check SHA1-hash for file with known hash */ + file = CreateFileA(temp, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + hashsize = 0; + pCryptCATAdminCalcHashFromFileHandle2(hca_sha1, file, &hashsize, NULL, 0); + hash = HeapAlloc(GetProcessHeap(), 0, hashsize); + SetLastError(0xdeadbeef); + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha1, file, &hashsize, hash, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %ld\n", GetLastError()); + ok(hashsize == sizeof(expectedhash) && + !memcmp(hash, expectedhash, sizeof(expectedhash)), + "Hashes didn't match\n"); + CloseHandle(file); + HeapFree(GetProcessHeap(), 0, hash); + pCryptCATAdminReleaseContext(hca_sha1, 0); + + /* Calculate hash with SHA-256 specified as algorithm */ + ret = pCryptCATAdminAcquireContext2(&hca_sha256, NULL, BCRYPT_SHA256_ALGORITHM, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + file = CreateFileA(selfname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + hashsize = 0; + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha256, file, &hashsize, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hashsize == 32," Expected a hash size of 32, got %ld\n", hashsize); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError()); + hash = HeapAlloc(GetProcessHeap(), 0, hashsize); + SetLastError(0xdeadbeef); + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha256, file, &hashsize, hash, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(hashsize == 32," Expected a hash size of 32, got %ld\n", hashsize); + ok(GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %ld\n", GetLastError()); + CloseHandle(file); + HeapFree(GetProcessHeap(), 0, hash);
+ /* All OK, first request the size and then retrieve the SHA256 hash */ + /* Check SHA256-hash for file with known hash */ + file = CreateFileA(temp, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + hashsize = 0; + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha256, file, &hashsize, NULL, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + hash = HeapAlloc(GetProcessHeap(), 0, hashsize); + SetLastError(0xdeadbeef); + ret = pCryptCATAdminCalcHashFromFileHandle2(hca_sha256, file, &hashsize, hash, 0); + ok(ret, "Expected success, got FALSE with %ld\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %ld\n", GetLastError()); + ok(hashsize == sizeof(expectedhash_sha256) && + !memcmp(hash, expectedhash_sha256, sizeof(expectedhash_sha256)), + "Hashes didn't match\n"); + CloseHandle(file); HeapFree(GetProcessHeap(), 0, hash); + pCryptCATAdminReleaseContext(hca_sha256, 0); DeleteFileA(temp); }
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=149827
Your paranoid android.
=== debian11b (64 bit WoW report) ===
netcfgx=load error 731
user32: win.c:4070: Test failed: Expected active window 0000000001ED0176, got 0000000000000000. win.c:4071: Test failed: Expected focus window 0000000001ED0176, got 0000000000000000.
Report validation errors: d3d11:d3d11 has no test summary line (early exit of the main process?) d3d11:d3d11 has unaccounted for todo messages d3d11:d3d11 returned a non-zero exit code despite reporting no failures
Please put { on a new line everywhere. That way coding style stays consistent in this file.