Skip to content

Improve secure CRT portability by detecting and implementing missing definitions#10442

Open
quesswho wants to merge 12 commits intoshader-slang:masterfrom
quesswho:feature/checkcrt
Open

Improve secure CRT portability by detecting and implementing missing definitions#10442
quesswho wants to merge 12 commits intoshader-slang:masterfrom
quesswho:feature/checkcrt

Conversation

@quesswho
Copy link
Contributor

@quesswho quesswho commented Mar 7, 2026

Partially fixes #5843

Problem

slang-secure-crt.h previously defined implementations of secure CRT functions (e.g. fread_s, sprintf_s, strncpy_s) only on non-Windows platforms using a single #ifndef _WIN32 guard. This caused missing declaration errors on Windows compilers that do not fully implement the secure CRT functions, such as MinGW or Cygwin.

Solution

  1. Added cmake/CheckSecureCRT.cmake, which probes for each secure CRT function individually by attempting a real call with matching argument types using check_source_compiles. This correctly detects inline and attributed declarations that check_symbol_exists/check_function_exists miss, without requiring platform-specific macro checks for each function.

  2. Each function in slang-secure-crt.h is now guarded by its own HAVE_<FUNC> macro rather than the single _WIN32 guard, so fallbacks are only defined when the platform lacks them.

Improvements

The _s implementations have been updated to properly match MSVC semantics,
including null pointer checks, bounds checking, correct error codes, and null termination. Each implementation references the corresponding Microsoft documentation.

Testing

I verified a clean build on MSVC, GCC/G++. Previous redefinition/missing errors on MinGW (MSYS2 mingw64) are now gone. On Mingw there are still some compile errors related to uuids and the dxc lib, hence #5843 is not fully resolved.

As for the _s implementations, I wrote a separate program that verifies the functionality and tests the error output against the MSVC documentation.

@quesswho quesswho requested a review from a team as a code owner March 7, 2026 18:52
@quesswho quesswho requested review from bmillsNV and removed request for a team March 7, 2026 18:52
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a CMake module that probes for Microsoft-style secure CRT functions and exposes successful probes as HAVE_* defines applied to targets; adds guarded inline fallback implementations for those secure-CRT functions in a header when the platform lacks them.

Changes

Cohort / File(s) Summary
Build config
CMakeLists.txt, cmake/CheckSecureCRT.cmake, cmake/SlangTarget.cmake
Adds cmake/CheckSecureCRT.cmake and includes it from CMakeLists.txt. The module compiles small test snippets to detect secure-CRT symbols and collects SLANG_SECURE_CRT_DEFINES (HAVE_* macros). SlangTarget.cmake applies those defines to targets via target_compile_definitions.
Secure CRT fallbacks
source/core/slang-secure-crt.h
Restricts top-level guard to MSVC, introduces per-feature #ifndef HAVE_* guards and provides inline fallback implementations and macros (_TRUNCATE, STRUNCATE, _stricmp) for secure-CRT functions (e.g., memcpy_s, fopen_s, fread_s, wcsnlen_s, strnlen_s, sprintf_s, swprintf_s, wcscpy_s, strcpy_s, wcsncpy_s, strncpy_s).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CMake as CMake (configure)
participant Probe as CheckSecureCRT (module)
participant Compiler as Compiler (test-compile)
participant Target as Target config
participant Source as slang-secure-crt.h

CMake->>Probe: include CheckSecureCRT
Probe->>Compiler: compile small test snippets (header + call)
alt compile succeeds
    Compiler-->>Probe: success -> add HAVE_* to SLANG_SECURE_CRT_DEFINES
else fails
    Compiler-->>Probe: failure (no define)
end
Probe-->>CMake: return SLANG_SECURE_CRT_DEFINES
CMake->>Target: apply SLANG_SECURE_CRT_DEFINES via target_compile_definitions
Target->>Source: compile sources
Source->>Source: include inline fallbacks when HAVE_* absent

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble headers, probe and try,
If safe calls hide, I patch them sly,
Compiling snippets, hopping through,
Small wrappers stitched to see you through,
A rabbit's cheer for builds anew 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR addresses the core objective from #5843 by replacing platform-specific checks with feature detection, enabling MinGW/Cygwin support without MSVC_VER assumption.
Out of Scope Changes check ✅ Passed All changes are directly related to detecting and providing secure CRT fallbacks; no out-of-scope modifications were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately and specifically describes the main change: detecting secure CRT definitions via CMake probing and implementing missing fallbacks when absent.
Description check ✅ Passed The pull request description clearly addresses the problem of missing secure CRT function implementations on non-MSVC Windows compilers like MinGW/Cygwin, explains the solution (CMake probing and per-function guards), and aligns with the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/core/slang-secure-crt.h (1)

82-95: ⚠️ Potential issue | 🟠 Major

__attribute__((format)) is not portable to MSVC.

The __attribute__((format(printf, ...))) syntax is GCC/Clang-specific and will cause compilation errors on MSVC.

Consider using a compiler-detection macro:

🛠️ Suggested fix
 `#ifndef` HAVE_SPRINTF_S
+#if defined(__GNUC__) || defined(__clang__)
 __attribute__((format(printf, 3, 4))) inline int sprintf_s(
+#else
+inline int sprintf_s(
+#endif
     char* buffer,
     size_t sizeOfBuffer,
     const char* format,

Apply the same pattern to swprintf_s (lines 97-109), though GCC's wprintf format checking is noted as incomplete in the code comment.

🧹 Nitpick comments (2)
source/core/slang-secure-crt.h (1)

64-80: Inconsistent null-check between Cygwin and non-Cygwin paths.

The Cygwin path (lines 68-75) checks if (str) for null before dereferencing, but the non-Cygwin path (line 77) passes str directly to strnlen without a null check. While strnlen typically handles null gracefully on most platforms, adding a consistent null check would be safer.

cmake/CheckSecureCRT.cmake (1)

15-47: Consider adding a probe for _stricmp or a platform guard.

The probes cover all the secure CRT functions with HAVE_* guards in slang-secure-crt.h, but there's no probe for _stricmp. This creates a gap where the #ifndef _stricmp guard in the header (lines 24-26) doesn't have a corresponding CMake-detected macro.

If the _stricmp mapping in slang-secure-crt.h is fixed with a platform guard (e.g., #if !defined(_MSC_VER)), then no probe is needed here. However, if you prefer consistency with the other functions, you could add:

slang_probe_symbol_call(HAVE_STRICMP
    "_stricmp(\"a\", \"b\")"
    "string.h")

This is a coordination note with the fix needed in slang-secure-crt.h.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a51d9aed-e3d8-4a13-b2cf-c5f9a666a1f6

📥 Commits

Reviewing files that changed from the base of the PR and between aa13deb and 6a4197d.

📒 Files selected for processing (3)
  • CMakeLists.txt
  • cmake/CheckSecureCRT.cmake
  • source/core/slang-secure-crt.h

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 7, 2026 18:57

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 7, 2026 19:23

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/core/slang-secure-crt.h (1)

26-39: ⚠️ Potential issue | 🟡 Minor

Initialize the output pointer on every fopen_s failure path.

When fileName or mode is null, the function returns EINVAL without clearing *f, allowing callers to observe a stale FILE* value in this failure case. Initialize *f before the parameter checks to ensure all failure paths properly clear the pointer.

Proposed fix
 inline int fopen_s(FILE** f, const char* fileName, const char* mode)
 {
-    if (f == nullptr || fileName == nullptr || mode == nullptr)
+    if (f == nullptr)
+    {
+        return EINVAL;
+    }
+    *f = nullptr;
+    if (fileName == nullptr || mode == nullptr)
     {
         return EINVAL;
     }
     *f = fopen(fileName, mode);
♻️ Duplicate comments (1)
source/core/slang-secure-crt.h (1)

55-76: ⚠️ Potential issue | 🔴 Critical

Restore _s semantics for the bounded-length helpers.

Line 58 ignores the caller-provided bound entirely, and Line 75 still forwards to strnlen(...), which does not accept nullptr. That means wcsnlen_s can read past an unterminated buffer, while strnlen_s can hit UB/crash on null input instead of returning 0.

Proposed fix
 `#ifndef` HAVE_WCSNLEN_S
-inline size_t wcsnlen_s(const wchar_t* str, size_t /*numberofElements*/)
+inline size_t wcsnlen_s(const wchar_t* str, size_t numberOfElements)
 {
-    return wcslen(str);
+    if (!str)
+        return 0;
+    size_t len = 0;
+    while (len < numberOfElements && str[len] != L'\0')
+        ++len;
+    return len;
 }
 `#endif` // HAVE_WCSNLEN_S
 
 `#ifndef` HAVE_STRNLEN_S
 inline size_t strnlen_s(const char* str, size_t numberOfElements)
 {
-#if defined(__CYGWIN__)
-    const char* cur = str;
-    if (str)
-    {
-        const char* const end = str + numberOfElements;
-        while (*cur && cur < end)
-            cur++;
-    }
-    return size_t(cur - str);
-#else
-    return strnlen(str, numberOfElements);
-#endif
+    if (!str)
+        return 0;
+    size_t len = 0;
+    while (len < numberOfElements && str[len] != '\0')
+        ++len;
+    return len;
 }
 `#endif` // HAVE_STRNLEN_S

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93ad447f-9b78-4454-8df5-0d22cf78003c

📥 Commits

Reviewing files that changed from the base of the PR and between 6a4197d and 4675790.

📒 Files selected for processing (1)
  • source/core/slang-secure-crt.h

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
cmake/CheckSecureCRT.cmake (1)

46-48: Add trailing newline at end of file.

The file is missing a trailing newline. Many tools and git diff will flag this.

Proposed fix
 slang_probe_symbol_call(HAVE_STRNCPY_S
     "char b[1]; strncpy_s(b, 1, \"x\", 1)"
     "string.h")
+
source/core/slang-secure-crt.h (2)

109-140: Copy function fallbacks ignore destination buffer size.

The wcscpy_s, strcpy_s, wcsncpy_s, and strncpy_s fallbacks comment out numberOfElements and use the unsafe underlying functions without bounds checking. This creates a contract mismatch where callers expect bounds validation but receive none.

Similarly, memcpy_s (line 16) and fread_s (line 50) use assert() for bounds checking which is disabled in release builds.

Consider adding explicit bounds checks (not just assert) for defense-in-depth, or at minimum document that these fallbacks assume caller-validated sizes.


142-143: Add trailing newline at end of file.

Proposed fix
 `#endif` // SLANG_CORE_SECURE_CRT_H
 `#endif` // _MSC_VER
+

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a1afb5e9-a29e-4297-8ebf-697d8e622dff

📥 Commits

Reviewing files that changed from the base of the PR and between 4675790 and 027a931.

📒 Files selected for processing (3)
  • cmake/CheckSecureCRT.cmake
  • cmake/SlangTarget.cmake
  • source/core/slang-secure-crt.h

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 7, 2026 20:18

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 9, 2026 13:49

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 9, 2026 14:56

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

Copy link
Collaborator

@jkwak-work jkwak-work left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with cmake syntax used in thie PR
But it seems harmless and helpful for MingW

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/core/slang-secure-crt.h (1)

37-48: ⚠️ Potential issue | 🔴 Critical

Initialize *f to nullptr on all return paths, including parameter validation failures.

When parameter validation fails at line 39, the function returns EINVAL without setting *f, leaving the output parameter uninitialized. This breaks the fopen_s contract that callers rely on: *f == nullptr should be guaranteed on any non-zero return. Some callers in the codebase (e.g., tools/slang-generate/main.cpp) do not pre-initialize their FILE* pointer, making them vulnerable to reading garbage values on validation failures.

The fix is straightforward: set *f = nullptr; before the parameter validation check (when f is valid).

Proposed fix
 inline int fopen_s(FILE** f, const char* fileName, const char* mode)
 {
+    if (f)
+        *f = nullptr;
     if (f == nullptr || fileName == nullptr || mode == nullptr)
     {
         return EINVAL;
     }
     *f = fopen(fileName, mode);
♻️ Duplicate comments (1)
source/core/slang-secure-crt.h (1)

1-16: ⚠️ Potential issue | 🔴 Critical

MinGW still takes the wrong compatibility path.

Line 1 makes this whole block active on MinGW, and Lines 14-16 then redefine _TRUNCATE / _stricmp even though MinGW already provides Windows-flavored CRT declarations there. That can reintroduce the same redefinition/shadowing failures this PR is trying to remove. Please separate the “enable fallbacks on MinGW” guard from the POSIX alias section.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e93edaac-241c-4639-93fd-d41044ed1145

📥 Commits

Reviewing files that changed from the base of the PR and between 36b03ec and 2eb7ea0.

📒 Files selected for processing (1)
  • source/core/slang-secure-crt.h

github-actions[bot]

This comment was marked as outdated.

@quesswho quesswho changed the title Detect secure CRT definitions and add if missing Improve secure CRT portability by detecting and implementing missing definitions Mar 10, 2026
@github-actions github-actions bot dismissed their stale review March 10, 2026 20:18

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

@quesswho
Copy link
Contributor Author

I've pushed additional commits that improves the _s implementations to properly match MSVC semantics. I've also added Microsoft documentation links to each implementation for reference.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 10, 2026 20:44

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 10, 2026 21:35

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 10, 2026 21:48

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@github-actions github-actions bot dismissed their stale review March 10, 2026 22:29

Automated review dismissed: bot reviews must use COMMENT only, not APPROVE or REQUEST_CHANGES.

github-actions[bot]

This comment was marked as outdated.

@@ -0,0 +1,48 @@
# Probe for secure CRT functions that may not be available on all platforms,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Trailing whitespace at end of lines 1 and 5 (after the commas). The project's formatting script may catch this, but worth cleaning up.

Suggested change
# Probe for secure CRT functions that may not be available on all platforms,
# Probe for secure CRT functions that may not be available on all platforms,

Comment on lines +558 to 567
#
if(SLANG_SECURE_CRT_DEFINES)
target_compile_definitions(
${target}
PRIVATE ${SLANG_SECURE_CRT_DEFINES}
)
endif()

#
# Precompiled headers (PCH)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This propagates the HAVE_* defines to every target built through slang_add_target, even those that never include slang-secure-crt.h. That's harmless (the defines are simply unused), but an alternative approach would be to create a small interface library (e.g., add_library(slang-secure-crt INTERFACE) with the defines) and link it only from the targets that actually include the header.

Not blocking — the current approach is simple and correct. Just a potential future cleanup.

@@ -1,4 +1,4 @@
#ifndef _WIN32
#if !defined(_WIN32) || defined(__MINGW32__) || defined(__CYGWIN__)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good change. On MinGW, _WIN32 is defined but the secure CRT functions may be missing, so the old #ifndef _WIN32 guard was too broad.

One consideration: for non-CMake builds on MinGW/Cygwin, none of the HAVE_* macros will be defined, so all fallback implementations will be provided. If MinGW's CRT headers already declare some of these functions (with potentially different signatures like errno_t vs int), this could cause compilation errors.

It might be worth adding a brief comment near the top explaining the dependency on the CMake build system for the HAVE_* macros, e.g.:

// NOTE: The HAVE_<func> macros below are set by cmake/CheckSecureCRT.cmake.
// When building without CMake, all fallback implementations are provided.

Comment on lines +19 to +21
#endif
#if !defined(_stricmp) && !defined(__MINGW32__)
#define _stricmp strcasecmp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sensible guard — MinGW provides _stricmp natively through its CRT headers. The !defined(_stricmp) check is a nice defensive touch for other compilers that might define it as a macro.

Verified: the only usage of _stricmp in the codebase is in slang-string.h inside an #ifdef _MSC_VER block, so this change has no impact on existing behavior.

Comment on lines +23 to +42

#ifndef HAVE_MEMCPY_S
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/memcpy-s-wmemcpy-s
inline int memcpy_s(void* dest, size_t destSize, const void* src, size_t count)
{
assert(destSize >= count);
if (count == 0)
return 0;
if (dest == nullptr)
{
errno = EINVAL;
return EINVAL;
}
if (src == nullptr || destSize < count)
{
memset(dest, 0, destSize);
if (src == nullptr)
{
errno = EINVAL;
return EINVAL;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type changed from void to int, and the implementation now properly matches MSVC semantics (null checks, zeroing dest on failure, returning appropriate error codes). I verified that the only call site (memcpy_s is currently unused in .cpp files) won't be affected, and memcpy_s isn't called from any .cpp files in the codebase.

One minor note: MSVC's memcpy_s also validates for overlapping buffers and invokes the invalid parameter handler rather than simply returning an error code. The comment references the MSVC docs which is good — readers can understand the semantic differences.

Comment on lines +70 to +86
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fread-s
inline size_t fread_s(
void* buffer,
[[maybe_unused]] size_t bufferSize,
size_t bufferSize,
size_t elementSize,
size_t count,
FILE* stream)
{
assert(bufferSize >= elementSize * count);
if (elementSize == 0 || count == 0)
return 0;
if (buffer == nullptr || stream == nullptr)
{
errno = EINVAL;
return 0;
}
if (count > bufferSize / elementSize)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bounds check count > bufferSize / elementSize is a nice pattern — it avoids the elementSize * count multiplication that could overflow on 32-bit platforms.

One edge case worth considering: MSVC's fread_s documentation says the buffer contents are indeterminate on error (some implementations zero it). This implementation just returns 0 without touching the buffer, which is fine and matches the documented "returns 0" behavior. Good pragmatic choice.

Comment on lines +106 to 118
#ifndef HAVE_STRNLEN_S
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strnlen-strnlen-s
inline size_t strnlen_s(const char* str, size_t numberOfElements)
{
#if defined(__CYGWIN__)
const char* cur = str;
if (str)
{
const char* const end = str + numberOfElements;
while (*cur && cur < end)
cur++;
}
return size_t(cur - str);
#else
return strnlen(str, numberOfElements);
#endif
if (str == nullptr)
return 0;
size_t count = 0;
while (count < numberOfElements && str[count] != '\0')
count++;
return count;
}
#endif // HAVE_STRNLEN_S

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sprintf_s comment about diverging from MSVC behavior (truncation via vsnprintf vs. invalid parameter handler) is excellent documentation. This is a pragmatic and correct choice for the fallback — callers in the codebase don't rely on the buffer being invalidated on overflow.

Good that the __GNUC__ guard was added for the format attribute — this makes it correct for MSVC builds (where this code path won't be reached anyway, but it's good hygiene).

Comment on lines +209 to 245
return EINVAL;
}
if (dest_size == 0)
{
errno = ERANGE;
return ERANGE;
}
if (src == nullptr)
{
dest[0] = '\0';
errno = EINVAL;
return EINVAL;
}
size_t i = 0;
while (i < dest_size - 1 && src[i] != '\0')
{
dest[i] = src[i];
i++;
}
dest[i] = '\0';
if (src[i] != '\0')
{
dest[0] = '\0';
errno = ERANGE;
return ERANGE;
}
return 0;
}
#endif // HAVE_STRCPY_S

inline void wcsncpy_s(
wchar_t* strDestination,
size_t /*numberOfElements*/,
#ifndef HAVE_WCSNCPY_S
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strncpy-s-strncpy-s-l-wcsncpy-s-wcsncpy-s-l-mbsncpy-s-mbsncpy-s-l?view=msvc-170
inline int wcsncpy_s(
wchar_t* strDest,
size_t numberOfElements,
const wchar_t* strSource,
size_t count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I traced through the logic for several edge cases and it's correct:

  • count == _TRUNCATE: copies up to numberOfElements-1, returns STRUNCATE if truncated ✓
  • count < numberOfElements-1 and source longer than count: copies exactly count chars, null-terminates, returns 0 ✓
  • count >= numberOfElements and source is long: detects overflow, zeros dest, returns ERANGE
  • count == 0: null-terminates dest immediately, returns 0 ✓
  • strSource == nullptr: null-terminates dest, returns EINVAL

Well done on the correctness here — the original wcsncpy/strncpy fallback didn't null-terminate and had no error handling at all.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: Improve secure CRT portability. Overall: Looks good. Well-structured improvement to MinGW/Cygwin portability with better secure CRT fallback implementations. What this PR does: (1) CMake probing via check_source_compiles to detect available secure CRT functions. (2) Per-function HAVE_* guards replacing the single ifndef _WIN32 guard. (3) Improved implementations matching MSVC semantics. Correctness verified for return type changes, strncpy_s/wcsncpy_s edge cases, CMake PARENT_SCOPE propagation, and stricmp guard. Suggestions (non-blocking, see inline comments): trailing whitespace in cmake file, add comment about CMake dependency for HAVE* macros, consider interface library for scoping defines. Missing: PR needs a pr: non-breaking label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow cross compiling via mingw?

2 participants