#3 CODESYS V3.5.16.0 not working

1.0
closed
nobody
None
2026-10-29
2020-05-07
Ingo
No

Currently the method in install2.sh, which is in fact doing a portable installation of the system by extracting all relevant files is not working. I didn't test install.sh, yet, but I expect even more problems with this method, as the installer is always a bit instable.

The effects:

wineuser@14ff3e064512:/$ codesys ide
Could not load wine-gecko. HTML rendering will be disabled.
wine: configuration in '/home/wineuser/.wine.cds' has been updated.
winevdm: Cannot start DOS application C:\CODESYS\CODESYS\Common\CODESYS.exe
         because the DOS memory range is unavailable.
         You should install DOSBox.
winevdm: Cannot start DOS application C:\CODESYS\CODESYS\Common\CODESYS.exe
         because the DOS memory range is unavailable.
         You should install DOSBox.
wineuser@14ff3e064512:/$ codesys export-documentation

So in fact I don't get any output on the commandline, and the commands return immediately. I guess, that this needs more investigation outside of the containers.

Discussion

  • Ingo

    Ingo - 2020-05-07

    Another test showed that the IDE worked natively on buster. Seems that we only have problems with the docker images.

     
  • Ingo

    Ingo - 2020-05-12

    Works again! Was just a DotNet issue. winetricks was not able to install DotNet completely headless anymore. So we now have the workaround with xvfb in install2.sh, like we had for install.sh.

     
  • Ingo

    Ingo - 2020-05-12
    • status: open --> closed
     
  • mondinmr

    mondinmr - 2026-10-29

    Hi everyone,

    I’ve successfully got CODESYS 64 3.5.21.30 (SP21 Patch 3) running on Linux!

    I’ve compiled and gone online with a very large, highly complex project containing hundreds of thousands of lines of code, with an architecture that makes extremely heavy use of polymorphism. Compilation succeeded, and I was able to connect to the PLC and work online.

    The Wine version used is Wine 11.20, built from development commit:

    66caeb46aea4aa8fe6f87b7a209e7867651c09dd

    This is a new WoW64 build supporting both 32-bit and 64-bit Windows applications, with two local patches:

    • NCrypt: implements NCryptEnumAlgorithms and NCryptFreeBuffer, with regression tests.
    • NTDLL: addresses a thread-suspension livelock affecting the x86 .NET runtime under Linux split-lock mitigation. This resolved the freeze when opening Change in CODESYS Installer. The kernel mitigation remains enabled.

    The environment also uses native .NET Framework 4.8 and Windows Desktop Runtime 8.0.33 x86 for CODESYS Installer.

    I’m attaching both patches with the exact Wine base commit, plus a screenshot showing CODESYS running on my Linux desktop. Project and device names have been blurred.

    These are locally tested patches; the NTDLL change is still experimental, and not every CODESYS feature has been validated.

    To patch wine:

    diff --git a/dlls/ncrypt/main.c b/dlls/ncrypt/main.c
    index 62b95aa8136981271339dcbda97090261640b58f..6e427ef374cbc8ce783039e698b2762c0f77cbcb 100644
    --- a/dlls/ncrypt/main.c
    +++ b/dlls/ncrypt/main.c
    @@ -340,11 +340,81 @@ SECURITY_STATUS WINAPI NCryptEncrypt(NCRYPT_KEY_HANDLE handle, BYTE *input, DWOR
         return map_ntstatus(status);
     }
    
    -SECURITY_STATUS WINAPI NCryptEnumAlgorithms(NCRYPT_PROV_HANDLE provider, DWORD alg_ops,
    +static BOOL match_algorithm_operations(const NCryptAlgorithmName *algorithm, DWORD operations)
    +{
    
    +    /* ECDH advertises signing operations, but is not included in a signature-only enumeration. */
    +
    +    DWORD mask = 1 << (algorithm->dwClass - 1);
    +
    +    if (algorithm->dwClass == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE)
    +        mask |= NCRYPT_SIGNATURE_OPERATION;
    +    return !operations || (operations & mask);
    +}
    +
    +SECURITY_STATUS WINAPI NCryptEnumAlgorithms(NCRYPT_PROV_HANDLE handle, DWORD alg_ops,
                                                 DWORD *alg_count, NCryptAlgorithmName **alg_list, DWORD flags)
     {
    -    FIXME("(%#Ix, %#lx, %p, %p, %#lx): stub\n", provider, alg_ops, alg_count, alg_list, flags);
    -    return NTE_NOT_SUPPORTED;
    +    static const NCryptAlgorithmName algorithms[] =
    +    {
    +        {(WCHAR *)BCRYPT_RSA_ALGORITHM, BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE,
    +         NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION | NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_DH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, NCRYPT_SECRET_AGREEMENT_OPERATION},
    +        {(WCHAR *)BCRYPT_DSA_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE,
    +         NCRYPT_SECRET_AGREEMENT_OPERATION | NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDH_P256_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE,
    +         NCRYPT_SECRET_AGREEMENT_OPERATION | NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDH_P384_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE,
    +         NCRYPT_SECRET_AGREEMENT_OPERATION | NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDH_P521_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE,
    +         NCRYPT_SECRET_AGREEMENT_OPERATION | NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDSA_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, NCRYPT_SIGNATURE_OPERATION},
    +        {(WCHAR *)BCRYPT_AES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, NCRYPT_CIPHER_OPERATION},
    +        {(WCHAR *)BCRYPT_3DES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, NCRYPT_CIPHER_OPERATION},
    +        {(WCHAR *)BCRYPT_DES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, NCRYPT_CIPHER_OPERATION},
    +    };
    +    struct provider *provider = (struct provider *)handle;
    +    NCryptAlgorithmName *list;
    +    DWORD count = 0;
    +    unsigned int i, j;
    +    SIZE_T size = 0, len;
    +    WCHAR *name;
    +
    +    TRACE("(%#Ix, %#lx, %p, %p, %#lx)\n", handle, alg_ops, alg_count, alg_list, flags);
    +
    +    if (!provider || provider->obj.type != PROVIDER) return NTE_INVALID_HANDLE;
    +    if (!alg_count || !alg_list) return NTE_INVALID_PARAMETER;
    +
    +    *alg_count = 0;
    +    *alg_list = NULL;
    +    if (flags & ~NCRYPT_SILENT_FLAG) return NTE_BAD_FLAGS;
    +
    +    for (i = 0; i < ARRAY_SIZE(algorithms); ++i)
    +    {
    +        if (!match_algorithm_operations(&algorithms[i], alg_ops)) continue;
    +        size += sizeof(*list) + (wcslen(algorithms[i].pszName) + 1) * sizeof(WCHAR);
    +        ++count;
    +    }
    +    if (!count) return NTE_NOT_SUPPORTED;
    +    if (!(list = malloc(size))) return NTE_NO_MEMORY;
    +
    +    name = (WCHAR *)(list + count);
    +    for (i = 0, j = 0; i < ARRAY_SIZE(algorithms); ++i)
    +    {
    +        if (!match_algorithm_operations(&algorithms[i], alg_ops)) continue;
    +        list[j] = algorithms[i];
    +        list[j++].pszName = name;
    +        len = (wcslen(algorithms[i].pszName) + 1) * sizeof(WCHAR);
    +        memcpy(name, algorithms[i].pszName, len);
    +        name += len / sizeof(WCHAR);
    +    }
    +
    +    *alg_count = count;
    +    *alg_list = list;
    +    return ERROR_SUCCESS;
     }
    
     SECURITY_STATUS WINAPI NCryptEnumKeys(NCRYPT_PROV_HANDLE provider, const WCHAR *scope,
    @@ -386,8 +456,10 @@ SECURITY_STATUS WINAPI NCryptFinalizeKey(NCRYPT_KEY_HANDLE handle, DWORD flags)
    
     SECURITY_STATUS WINAPI NCryptFreeBuffer(PVOID buf)
     {
    
    -    FIXME("(%p): stub\n", buf);
    -    return NTE_NOT_SUPPORTED;
    +    TRACE("(%p)\n", buf);
    +
    +    free(buf);
    +    return ERROR_SUCCESS;
     }
    
     SECURITY_STATUS WINAPI NCryptFreeObject(NCRYPT_HANDLE handle)
    diff --git a/dlls/ncrypt/tests/ncrypt.c b/dlls/ncrypt/tests/ncrypt.c
    index 61c61abd07b81f0fb1e1500f72dd8a57d4ecb27d..e5d7aea6b511ec25ee6913bf822c0dc8373513e1 100644
    --- a/dlls/ncrypt/tests/ncrypt.c
    +++ b/dlls/ncrypt/tests/ncrypt.c
    @@ -898,8 +898,161 @@ static void test_NCryptImportKey(void)
         NCryptFreeObject(prov);
     }
    
    +static void test_enum_algorithms(void)
    +{
    
    +    static const DWORD operations[] =
    +    {
    +        0, NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION, NCRYPT_SIGNATURE_OPERATION,
    +        NCRYPT_SECRET_AGREEMENT_OPERATION, NCRYPT_CIPHER_OPERATION,
    +        NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION | NCRYPT_SIGNATURE_OPERATION, ~0u,
    +    };
    +    static const DWORD bad_flags[] = {1, NCRYPT_MACHINE_KEY_FLAG, NCRYPT_SILENT_FLAG | 1, 0x80000000};
    +    static const DWORD unsupported_ops[] = {NCRYPT_HASH_OPERATION, BCRYPT_RNG_OPERATION, 0x80000000};
    +    NCryptAlgorithmName *list, *second;
    +    NCRYPT_PROV_HANDLE prov;
    +    NCRYPT_KEY_HANDLE key;
    +    SECURITY_STATUS ret;
    +    DWORD count, second_count, i, j, k;
    +    BOOL rsa, ecdsa, ecdh;
    +
    +    ret = NCryptFreeBuffer(NULL);
    +    ok(!ret, "got %#lx\n", ret);
    +
    +    ret = NCryptOpenStorageProvider(&prov, NULL, 0);
    +    ok(!ret, "got %#lx\n", ret);
    +    if (ret) return;
    +
    +    count = 0xdeadbeef;
    +    list = (void *)0xdeadbeef;
    +    ret = NCryptEnumAlgorithms(0, 0, &count, &list, 0);
    +    ok(ret == NTE_INVALID_HANDLE, "got %#lx\n", ret);
    +    ok(count == 0xdeadbeef, "got count %lu\n", count);
    +    ok(list == (void *)0xdeadbeef, "got list %p\n", list);
    +
    +    ret = NCryptEnumAlgorithms(prov, 0, NULL, &list, 0);
    +    ok(ret == NTE_INVALID_PARAMETER, "got %#lx\n", ret);
    +    ok(list == (void *)0xdeadbeef, "got list %p\n", list);
    +    ret = NCryptEnumAlgorithms(prov, 0, &count, NULL, 0);
    +    ok(ret == NTE_INVALID_PARAMETER, "got %#lx\n", ret);
    +    ok(count == 0xdeadbeef, "got count %lu\n", count);
    +
    +    ret = NCryptCreatePersistedKey(prov, &key, BCRYPT_RSA_ALGORITHM, NULL, 0, 0);
    +    ok(!ret, "got %#lx\n", ret);
    +    if (!ret)
    +    {
    +        ret = NCryptEnumAlgorithms(key, 0, &count, &list, 0);
    +        ok(ret == NTE_INVALID_HANDLE, "got %#lx\n", ret);
    +        ok(count == 0xdeadbeef, "got count %lu\n", count);
    +        ok(list == (void *)0xdeadbeef, "got list %p\n", list);
    +        NCryptFreeObject(key);
    +    }
    +
    +    for (i = 0; i < ARRAY_SIZE(bad_flags); ++i)
    +    {
    +        count = 0xdeadbeef;
    +        list = (void *)0xdeadbeef;
    +        ret = NCryptEnumAlgorithms(prov, 0, &count, &list, bad_flags[i]);
    +        ok(ret == NTE_BAD_FLAGS, "flags %#lx: got %#lx\n", bad_flags[i], ret);
    +        ok(!count, "got count %lu\n", count);
    +        ok(!list, "got list %p\n", list);
    +    }
    +    for (i = 0; i < ARRAY_SIZE(unsupported_ops); ++i)
    +    {
    +        count = 0xdeadbeef;
    +        list = (void *)0xdeadbeef;
    +        ret = NCryptEnumAlgorithms(prov, unsupported_ops[i], &count, &list, 0);
    +        ok(ret == NTE_NOT_SUPPORTED, "operations %#lx: got %#lx\n", unsupported_ops[i], ret);
    +        ok(!count, "got count %lu\n", count);
    +        ok(!list, "got list %p\n", list);
    +    }
    +
    +    for (i = 0; i < ARRAY_SIZE(operations); ++i)
    +    {
    +        count = 0;
    +        list = NULL;
    +        ret = NCryptEnumAlgorithms(prov, operations[i], &count, &list, 0);
    +        if (operations[i] == NCRYPT_CIPHER_OPERATION && broken(ret == NTE_NOT_SUPPORTED))
    +            continue; /* Windows 7 */
    +        ok(!ret, "operations %#lx: got %#lx\n", operations[i], ret);
    +        if (ret) continue;
    +        ok(count && list, "got count %lu, list %p\n", count, list);
    +        rsa = ecdsa = ecdh = FALSE;
    +        for (j = 0; j < count; ++j)
    +        {
    +            ok(list[j].pszName && list[j].pszName[0], "got empty algorithm name\n");
    +            if (!list[j].pszName) continue;
    +            ok(!list[j].dwFlags, "%s: flags %#lx\n", debugstr_w(list[j].pszName), list[j].dwFlags);
    +            for (k = 0; k < j; ++k)
    +                ok(wcscmp(list[k].pszName, list[j].pszName), "duplicate %s\n", debugstr_w(list[j].pszName));
    +            ret = NCryptIsAlgSupported(prov, list[j].pszName, 0);
    +            ok(!ret, "%s: got %#lx\n", debugstr_w(list[j].pszName), ret);
    +            if (!wcscmp(list[j].pszName, BCRYPT_RSA_ALGORITHM))
    +            {
    +                rsa = TRUE;
    +                ok(list[j].dwClass == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE, "RSA class %lu\n", list[j].dwClass);
    +                ok(list[j].dwAlgOperations == (NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION | NCRYPT_SIGNATURE_OPERATION),
    +                   "RSA operations %#lx\n", list[j].dwAlgOperations);
    +            }
    +            if (!wcscmp(list[j].pszName, BCRYPT_ECDSA_P256_ALGORITHM))
    +            {
    +                ecdsa = TRUE;
    +                ok(list[j].dwClass == BCRYPT_SIGNATURE_INTERFACE, "ECDSA class %lu\n", list[j].dwClass);
    +                ok(list[j].dwAlgOperations == NCRYPT_SIGNATURE_OPERATION, "ECDSA operations %#lx\n", list[j].dwAlgOperations);
    +            }
    +            if (!wcscmp(list[j].pszName, BCRYPT_ECDH_P256_ALGORITHM))
    +            {
    +                ecdh = TRUE;
    +                ok(list[j].dwClass == BCRYPT_SECRET_AGREEMENT_INTERFACE, "ECDH class %lu\n", list[j].dwClass);
    +                ok(list[j].dwAlgOperations == (NCRYPT_SECRET_AGREEMENT_OPERATION | NCRYPT_SIGNATURE_OPERATION),
    +                   "ECDH operations %#lx\n", list[j].dwAlgOperations);
    +            }
    +            if (operations[i] == NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION)
    +                ok(list[j].dwClass == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE,
    +                   "%s: class %lu\n", debugstr_w(list[j].pszName), list[j].dwClass);
    +            if (operations[i] == NCRYPT_SIGNATURE_OPERATION)
    +                ok(list[j].dwClass == BCRYPT_SIGNATURE_INTERFACE || list[j].dwClass == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE,
    +                   "%s: class %lu\n", debugstr_w(list[j].pszName), list[j].dwClass);
    +            if (operations[i] == NCRYPT_SECRET_AGREEMENT_OPERATION)
    +                ok(list[j].dwClass == BCRYPT_SECRET_AGREEMENT_INTERFACE,
    +                   "%s: class %lu\n", debugstr_w(list[j].pszName), list[j].dwClass);
    +            if (operations[i] == NCRYPT_CIPHER_OPERATION)
    +                ok(list[j].dwClass == BCRYPT_CIPHER_INTERFACE,
    +                   "%s: class %lu\n", debugstr_w(list[j].pszName), list[j].dwClass);
    +        }
    +        if (!operations[i] || operations[i] & (NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION | NCRYPT_SIGNATURE_OPERATION))
    +            ok(rsa, "operations %#lx: RSA not enumerated\n", operations[i]);
    +        if (!operations[i] || operations[i] & NCRYPT_SIGNATURE_OPERATION)
    +            ok(ecdsa, "operations %#lx: ECDSA_P256 not enumerated\n", operations[i]);
    +        if (!operations[i] || operations[i] & NCRYPT_SECRET_AGREEMENT_OPERATION)
    +            ok(ecdh, "operations %#lx: ECDH_P256 not enumerated\n", operations[i]);
    +        ret = NCryptFreeBuffer(list);
    +        ok(!ret, "got %#lx\n", ret);
    +    }
    +
    +    ret = NCryptEnumAlgorithms(prov, 0, &count, &list, NCRYPT_SILENT_FLAG);
    +    ok(!ret, "got %#lx\n", ret);
    +    if (!ret)
    +    {
    +        ret = NCryptEnumAlgorithms(prov, 0, &second_count, &second, 0);
    +        ok(!ret, "got %#lx\n", ret);
    +        if (!ret)
    +        {
    +            ok(second != list, "same enumeration buffer\n");
    +            ok(second_count == count, "got %lu and %lu\n", second_count, count);
    +            NCryptFreeBuffer(second);
    +        }
    +        NCryptFreeObject(prov);
    +        for (i = 0; i < count; ++i)
    +            ok(list[i].pszName && list[i].pszName[0], "name lost after freeing provider\n");
    +        ret = NCryptFreeBuffer(list);
    +        ok(!ret, "got %#lx\n", ret);
    +    }
    +    else NCryptFreeObject(prov);
    +}
    +
     START_TEST(ncrypt)
     {
    +    test_enum_algorithms();
         test_key_import_rsa();
         test_free_object();
         test_get_property();
    diff --git a/include/ncrypt.h b/include/ncrypt.h
    index 4c99ac09bb722faf50083f90b9f2eb536a4dd474..46ef37264e689407eef60620e3586a1f48564235 100644
    --- a/include/ncrypt.h
    +++ b/include/ncrypt.h
    @@ -79,6 +79,12 @@ typedef ULONG_PTR NCRYPT_KEY_HANDLE;
     typedef ULONG_PTR NCRYPT_HASH_HANDLE;
     typedef ULONG_PTR NCRYPT_SECRET_HANDLE;
    
    +#define NCRYPT_CIPHER_OPERATION                 0x00000001
    +#define NCRYPT_HASH_OPERATION                   0x00000002
    +#define NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION  0x00000004
    +#define NCRYPT_SECRET_AGREEMENT_OPERATION       0x00000008
    +#define NCRYPT_SIGNATURE_OPERATION              0x00000010
    +
     #define NCRYPT_KEY_STORAGE_INTERFACE        0x00010001
     #define NCRYPT_SCHANNEL_INTERFACE           0x00010002
     #define NCRYPT_SCHANNEL_SIGNATURE_INTERFACE 0x00010003
    @@ -142,6 +148,7 @@ SECURITY_STATUS WINAPI NCryptCreatePersistedKey(NCRYPT_PROV_HANDLE, NCRYPT_KEY_H
     SECURITY_STATUS WINAPI NCryptDecrypt(NCRYPT_KEY_HANDLE, BYTE *, DWORD, void *, BYTE *, DWORD, DWORD *, DWORD);
     SECURITY_STATUS WINAPI NCryptDeleteKey(NCRYPT_KEY_HANDLE, DWORD);
     SECURITY_STATUS WINAPI NCryptEncrypt(NCRYPT_KEY_HANDLE, BYTE *, DWORD, void *, BYTE *, DWORD, DWORD *, DWORD);
    +SECURITY_STATUS WINAPI NCryptEnumAlgorithms(NCRYPT_PROV_HANDLE, DWORD, DWORD *, NCryptAlgorithmName **, DWORD);
     SECURITY_STATUS WINAPI NCryptEnumKeys(NCRYPT_PROV_HANDLE, const WCHAR *, NCryptKeyName **, void **, DWORD);
     SECURITY_STATUS WINAPI NCryptExportKey(NCRYPT_KEY_HANDLE, NCRYPT_KEY_HANDLE, const WCHAR *, NCryptBufferDesc *, BYTE *,
                                            DWORD, DWORD *, DWORD);
    diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c
    index 7416fd6d18c83d5fdd41a6f45531833220c8fffc..ebdf416144d16691f2c36d55dadfd3e8bb745911 100644
    --- a/dlls/ntdll/tests/thread.c
    +++ b/dlls/ntdll/tests/thread.c
    @@ -517,6 +517,95 @@ static void test_arm64_skip_loader_init(void)
         CloseHandle( thread );
     }
    
    +#ifdef __i386__
    +struct split_lock_thread_data
    +{
    
    +    volatile LONGLONG *value;
    +    LONG stop;
    +    HANDLE ready;
    +};
    +
    +static DWORD WINAPI split_lock_thread( void *arg )
    +{
    +    struct split_lock_thread_data *data = arg;
    +
    +    SetEvent( data->ready );
    +    while (!InterlockedCompareExchange( &data->stop, 0, 0 ))
    +    {
    +        InterlockedIncrement64( data->value );
    +        SwitchToThread();
    +    }
    +    return 0;
    +}
    +
    +static void test_split_lock_suspend(void)
    +{
    +    static const unsigned int offsets[] = {0, 60};
    +    struct split_lock_thread_data data;
    +    LONGLONG initial, value, previous;
    +    HANDLE thread;
    +    CONTEXT context;
    +    BYTE *memory;
    +    DWORD ret;
    +    unsigned int i, j;
    +
    +    memory = VirtualAlloc( NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
    +    ok( !!memory, "VirtualAlloc failed, error %lu.\n", GetLastError() );
    +    if (!memory) return;
    +    data.ready = CreateEventW( NULL, TRUE, FALSE, NULL );
    +    ok( !!data.ready, "CreateEvent failed, error %lu.\n", GetLastError() );
    +    if (!data.ready) goto done;
    +
    +    for (i = 0; i < ARRAY_SIZE(offsets); i++)
    +    {
    +        data.value = (LONGLONG *)(memory + offsets[i]);
    +        *data.value = 0;
    +        data.stop = 0;
    +        ResetEvent( data.ready );
    +        thread = CreateThread( NULL, 0, split_lock_thread, &data, 0, NULL );
    +        ok( !!thread, "CreateThread failed, error %lu.\n", GetLastError() );
    +        if (!thread) continue;
    +        ret = WaitForSingleObject( data.ready, 3000 );
    +        ok( ret == WAIT_OBJECT_0, "Thread not ready, wait returned %#lx.\n", ret );
    +        initial = *data.value;
    +        value = initial;
    +        for (j = 0; j < 32; j++)
    +        {
    +            Sleep( 1 );
    +            ret = SuspendThread( thread );
    +            ok( !ret, "SuspendThread returned %#lx.\n", ret );
    +            if (ret == ~0u) break;
    +            memset( &context, 0, sizeof(context) );
    +            context.ContextFlags = CONTEXT_FULL;
    +            ret = GetThreadContext( thread, &context );
    +            ok( ret, "GetThreadContext failed, error %lu.\n", GetLastError() );
    +            if (ret)
    +                ok( !(context.EFlags & 0x100), "Internal single-step flag leaked: %#lx.\n", context.EFlags );
    +            value = *data.value;
    +            Sleep( 1 );
    +            previous = *data.value;
    +            ok( value == previous, "Suspended thread advanced from %s to %s.\n",
    +                wine_dbgstr_longlong(value), wine_dbgstr_longlong(previous) );
    +            ret = ResumeThread( thread );
    +            ok( ret == 1, "ResumeThread returned %#lx.\n", ret );
    +        }
    +        ok( value > initial, "No atomic progress at offset %u during repeated suspension.\n", offsets[i] );
    +        InterlockedExchange( &data.stop, 1 );
    +        ret = WaitForSingleObject( thread, 3000 );
    +        ok( ret == WAIT_OBJECT_0, "Worker did not finish, wait returned %#lx.\n", ret );
    +        if (ret != WAIT_OBJECT_0)
    +        {
    +            TerminateThread( thread, 1 );
    +            WaitForSingleObject( thread, INFINITE );
    +        }
    +        CloseHandle( thread );
    +    }
    +    CloseHandle( data.ready );
    +done:
    +    VirtualFree( memory, 0, MEM_RELEASE );
    +}
    +#endif
    +
     START_TEST(thread)
     {
         init_function_pointers();
    @@ -548,4 +637,7 @@ START_TEST(thread)
         test_thread_bypass_process_freeze();
         test_NtQueueApcThreadEx();
         test_skip_thread_attach();
    +#ifdef __i386__
    +    test_split_lock_suspend();
    +#endif
     }
    diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
    index f807caa43191fec95635bdb0e1068ffac77ceee3..51a32eb80b1f183be3c9f6532c40b14cbcf8fb66 100644
    --- a/dlls/ntdll/unix/signal_x86_64.c
    +++ b/dlls/ntdll/unix/signal_x86_64.c
    @@ -516,6 +516,7 @@ struct amd64_thread_data
         DWORD                 fs;            /* 0338 WOW TEB selector */
         DWORD                 mxcsr;         /* 033c Unix-side mxcsr register */
         char                  syscall_dispatch; /* 0340 */
    +    BOOL                  suspend_after_step; /* complete a split lock before suspension */
     };
    
     C_ASSERT( sizeof(struct amd64_thread_data) <= sizeof(((struct teb_data *)0)->cpu_data) );
    @@ -2270,6 +2271,55 @@ static inline BOOL check_invalid_gsbase( struct thread_data *data, ucontext_t *u
     }
    
    
    +/**********************************************************************
    
    + *              suspend_signal_context
    + */
    +static void suspend_signal_context( struct thread_data *data, ucontext_t *sigcontext )
    +{
    +    struct xcontext context;
    +
    +    save_context( data, &context, sigcontext );
    +    context.c.ContextFlags |= CONTEXT_EXCEPTION_REPORTING;
    +    if (is_wow64() && context.c.SegCs == cs64_sel) context.c.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
    +    wait_suspend( &context.c );
    +    restore_context( data, &context, sigcontext );
    +}
    +
    +
    +/**********************************************************************
    + *              defer_split_lock_suspend
    + *
    + * Linux split-lock mitigation sleeps before retrying a faulting instruction.
    + * Repeated suspension can keep interrupting that sleep indefinitely. Complete
    + * the instruction with a single step before reporting the suspended context.
    + * RF identifies an instruction being retried after a fault; asynchronous signal
    + * contexts do not retain the original alignment-check trap number.
    + */
    +static BOOL defer_split_lock_suspend( struct thread_data *data, ucontext_t *sigcontext )
    +{
    +#ifdef __linux__
    +    struct amd64_thread_data *amd64_data = amd64_th
    

    • Screenshot Linux
     

Log in to post a comment.