https://github.com/vhscampos updated https://github.com/llvm/llvm-project/pull/197709
>From b36b993f11256c25023c9f6cf780de85ae42a42f Mon Sep 17 00:00:00 2001 From: Victor Campos <[email protected]> Date: Mon, 23 Mar 2026 10:22:57 +0000 Subject: [PATCH 1/3] [libc] Annex K: strcpy_s This patch adds Annex K's `strcpy_s`. --- libc/config/baremetal/aarch64/entrypoints.txt | 1 + libc/config/baremetal/arm/entrypoints.txt | 1 + libc/config/linux/aarch64/entrypoints.txt | 1 + libc/config/linux/arm/entrypoints.txt | 1 + libc/config/linux/x86_64/entrypoints.txt | 1 + libc/include/string.yaml | 9 ++ libc/src/string/CMakeLists.txt | 19 +++ libc/src/string/strcpy_s.cpp | 74 ++++++++++ libc/src/string/strcpy_s.h | 27 ++++ libc/test/src/string/CMakeLists.txt | 13 ++ libc/test/src/string/strcpy_s_test.cpp | 138 ++++++++++++++++++ 11 files changed, 285 insertions(+) create mode 100644 libc/src/string/strcpy_s.cpp create mode 100644 libc/src/string/strcpy_s.h create mode 100644 libc/test/src/string/strcpy_s_test.cpp diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index 452abd985b3a5..110538f8b6be5 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -75,6 +75,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strdup libc.src.string.strerror diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 41c80efc64227..69d13fa293e6e 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -75,6 +75,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strdup libc.src.string.strerror diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index b7c9cabd934b4..30c3034fa7f28 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -68,6 +68,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strdup libc.src.string.strerror diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 906f36d45e337..f006c5476a6e0 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -41,6 +41,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strlcat libc.src.string.strlcpy diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 9970f079abc08..dab5062d18d0c 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -70,6 +70,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strdup libc.src.string.strerror diff --git a/libc/include/string.yaml b/libc/include/string.yaml index c0a96e58dbc94..234cc6e77cfd4 100644 --- a/libc/include/string.yaml +++ b/libc/include/string.yaml @@ -169,6 +169,15 @@ functions: arguments: - type: char *__restrict - type: const char *__restrict + - name: strcpy_s + standards: + - stdc + return_type: errno_t + arguments: + - type: char *__restrict + - type: rsize_t + - type: const char *__restrict + guard: LIBC_HAS_ANNEX_K - name: strcspn standards: - stdc diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index e28fe7af8cf25..accf30e865ad9 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -172,6 +172,25 @@ add_entrypoint_object( .string_utils ) +add_entrypoint_object( + strcpy_s + SRCS + strcpy_s.cpp + HDRS + strcpy_s.h + DEPENDS + .memory_utils.inline_memcpy + .string_utils + .strnlen_s + libc.src.__support.common + libc.src.__support.constraint_handler + libc.src.__support.macros.config + libc.hdr.types.constraint_handler_t + libc.hdr.types.errno_t + libc.hdr.types.rsize_t + libc.hdr.stdint_proxy +) + add_entrypoint_object( strcspn SRCS diff --git a/libc/src/string/strcpy_s.cpp b/libc/src/string/strcpy_s.cpp new file mode 100644 index 0000000000000..00d946dfa1682 --- /dev/null +++ b/libc/src/string/strcpy_s.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation of strcpy_s. +/// +//===----------------------------------------------------------------------===// + +// For RSIZE_MAX +#define __STDC_WANT_LIB_EXT1__ 1 +#include "hdr/stdint_proxy.h" +#undef __STDC_WANT_LIB_EXT1__ + +#include "hdr/types/constraint_handler_t.h" +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/__support/common.h" +#include "src/__support/constraint_handler.h" +#include "src/__support/macros/config.h" +#include "src/string/memory_utils/inline_memcpy.h" +#include "src/string/strcpy_s.h" +#include "src/string/string_utils.h" +#include "src/string/strnlen_s.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(errno_t, strcpy_s, + (char *__restrict s1, rsize_t s1max, + const char *__restrict s2)) { + const char *constraint_violation_msg = 0; + size_t count; + + if (s1 == 0) { + constraint_violation_msg = "strcpy_s: s1 cannot be null"; + } else if (s2 == 0) { + constraint_violation_msg = "strcpy_s: s2 cannot be null"; + } else if (s1max > RSIZE_MAX) { + constraint_violation_msg = "strcpy_s: s1max cannot exceed RSIZE_MAX"; + } else if (s1max == 0) { + constraint_violation_msg = "strcpy_s: s1max cannot be 0"; + } else if (count = strnlen_s(s2, s1max); + s1max == count) { // count can't be greater than s1max by + // definition, so no need to check for this case + constraint_violation_msg = "strcpy_s: s1max is too small for s2"; + } + // Check overlap using the full regions defined by the standard, including the + // terminating null byte: + // destination: [s1, s1 + s1max) + // source: [s2, s2 + count + 1) + // Use s1max for the destination's length, not count + 1, because the + // standard allows for overwriting the entire destination region, even if + // s2's length is smaller than s1max. + else if (s2 < (s1 + s1max) && s1 < (s2 + count + 1)) { + constraint_violation_msg = "strcpy_s: s1 and s2 cannot overlap"; + } + + if (constraint_violation_msg) { + if (s1 != 0 && s1max > 0 && s1max <= RSIZE_MAX) { + s1[0] = '\0'; + } + libc_global_constraint_handler(constraint_violation_msg, 0, 1); + return 1; + } + + inline_memcpy(s1, s2, count + 1); + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/string/strcpy_s.h b/libc/src/string/strcpy_s.h new file mode 100644 index 0000000000000..2c8e0751d1425 --- /dev/null +++ b/libc/src/string/strcpy_s.h @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation header for strcpy_s. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STRING_STRCPY_S_H +#define LLVM_LIBC_SRC_STRING_STRCPY_S_H + +#include "hdr/types/errno_t.h" +#include "hdr/types/rsize_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +errno_t strcpy_s(char *__restrict s1, rsize_t s1max, const char *__restrict s2); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STRING_STRCPY_S_H diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index 17927ea93ed1e..e79d8e6257f5d 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -149,6 +149,19 @@ add_libc_test( libc.src.string.strcpy ) +add_libc_test( + strcpy_s_test + SUITE + libc-string-tests + SRCS + strcpy_s_test.cpp + DEPENDS + libc.src.string.strcpy_s + libc.hdr.stdint_proxy + libc.hdr.types.errno_t + libc.test.UnitTest.ConstraintHandlerCheckingTest +) + add_libc_test( strcspn_test SUITE diff --git a/libc/test/src/string/strcpy_s_test.cpp b/libc/test/src/string/strcpy_s_test.cpp new file mode 100644 index 0000000000000..ee0c820d9cb49 --- /dev/null +++ b/libc/test/src/string/strcpy_s_test.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains unit tests for strcpy_s. +/// +//===----------------------------------------------------------------------===// + +#define __STDC_WANT_LIB_EXT1__ 1 +#include "hdr/stdint_proxy.h" +#undef __STDC_WANT_LIB_EXT1__ + +#include "hdr/types/errno_t.h" +#include "src/string/strcpy_s.h" +#include "test/UnitTest/ConstraintHandlerCheckingTest.h" + +using LlvmLibcStrCpySTest = + LIBC_NAMESPACE::testing::ConstraintHandlerCheckingTest; + +// Success cases +TEST_F(LlvmLibcStrCpySTest, SuccessfulCopy) { + char s1[8]; + const char *s2 = "abc"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), s2); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s1, s2); + ASSERT_FALSE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, ExactFitSuccessfulCopy) { + char s1[4]; + const char *s2 = "abc"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), s2); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s1, s2); + ASSERT_FALSE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, AdjacentObjectsDoNotOverlap) { + char s[8] = {'a', 'b', 'c', '\0', '?', '?', '?', '?'}; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s + 4, 4, s); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s + 4, "abc"); + ASSERT_FALSE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, EmptySourceString) { + char s1[4]; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), ""); + ASSERT_EQ(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_FALSE(error_flag); +} + +// Failure cases +TEST_F(LlvmLibcStrCpySTest, NullS1) { + const char *s2 = "abc"; + char *s1 = 0; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 4, s2); + ASSERT_NE(result, 0); + ASSERT_TRUE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, NullS2) { + char s1[4]; + const char *s2 = 0; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 4, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_TRUE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxGreaterThanRSizeMax) { + char s1[4]; + const char *s2 = "abc"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, RSIZE_MAX + 1, s2); + ASSERT_NE(result, 0); + ASSERT_TRUE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxIsZero) { + char s1[4]; + const char *s2 = "abc"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 0, s2); + ASSERT_NE(result, 0); + ASSERT_TRUE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxTooSmallForS2) { + char s1[3]; + const char *s2 = "abc"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 3, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_TRUE(error_flag); + + s2 = "abcd"; + s1[0] = '?'; + error_flag = false; + ASSERT_FALSE(error_flag); + result = LIBC_NAMESPACE::strcpy_s(s1, 3, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_TRUE(error_flag); +} + +TEST_F(LlvmLibcStrCpySTest, OverlappingObjects) { + char s[10] = "123456789"; + + ASSERT_FALSE(error_flag); + errno_t result = LIBC_NAMESPACE::strcpy_s(s, 6, s + 4); + ASSERT_NE(result, 0); + ASSERT_EQ(s[0], '\0'); + ASSERT_TRUE(error_flag); +} >From b54d6afb18e772d0ee4aaff63390c535880d1f7b Mon Sep 17 00:00:00 2001 From: Victor Campos <[email protected]> Date: Wed, 1 Jul 2026 15:21:14 +0100 Subject: [PATCH 2/3] Protect overlap check against overflow and add some missing entrypoints --- libc/config/darwin/aarch64/entrypoints.txt | 1 + libc/config/darwin/x86_64/entrypoints.txt | 1 + libc/src/string/strcpy_s.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt index 914d2b7918da1..4b14f9f28aace 100644 --- a/libc/config/darwin/aarch64/entrypoints.txt +++ b/libc/config/darwin/aarch64/entrypoints.txt @@ -38,6 +38,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strlcat libc.src.string.strlcpy diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index b941c968255e8..14718b71dbd2a 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -37,6 +37,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy + libc.src.string.strcpy_s libc.src.string.strcspn libc.src.string.strlcat libc.src.string.strlcpy diff --git a/libc/src/string/strcpy_s.cpp b/libc/src/string/strcpy_s.cpp index 00d946dfa1682..f9bf2d5fd480e 100644 --- a/libc/src/string/strcpy_s.cpp +++ b/libc/src/string/strcpy_s.cpp @@ -11,11 +11,9 @@ /// //===----------------------------------------------------------------------===// -// For RSIZE_MAX #define __STDC_WANT_LIB_EXT1__ 1 #include "hdr/stdint_proxy.h" #undef __STDC_WANT_LIB_EXT1__ - #include "hdr/types/constraint_handler_t.h" #include "hdr/types/errno_t.h" #include "hdr/types/rsize_t.h" @@ -55,7 +53,10 @@ LLVM_LIBC_FUNCTION(errno_t, strcpy_s, // Use s1max for the destination's length, not count + 1, because the // standard allows for overwriting the entire destination region, even if // s2's length is smaller than s1max. - else if (s2 < (s1 + s1max) && s1 < (s2 + count + 1)) { + else if (const uintptr_t s1_addr = reinterpret_cast<uintptr_t>(s1), + s2_addr = reinterpret_cast<uintptr_t>(s2); + s1_addr < s2_addr ? s2_addr - s1_addr < s1max + : s1_addr - s2_addr < count + 1) { constraint_violation_msg = "strcpy_s: s1 and s2 cannot overlap"; } >From 4f2222e8e1e93a82758e627376f72f91c843ca68 Mon Sep 17 00:00:00 2001 From: Victor Campos <[email protected]> Date: Fri, 3 Jul 2026 13:51:53 +0100 Subject: [PATCH 3/3] Address comments --- .../include/llvm-libc-macros/annex-k-macros.h | 9 ++- libc/src/string/strcpy_s.cpp | 68 ++++++++++--------- libc/src/string/string_utils.h | 4 ++ libc/src/string/strnlen_s.cpp | 2 +- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/libc/include/llvm-libc-macros/annex-k-macros.h b/libc/include/llvm-libc-macros/annex-k-macros.h index 7cfb5c1d3060b..bc7604d78c5ca 100644 --- a/libc/include/llvm-libc-macros/annex-k-macros.h +++ b/libc/include/llvm-libc-macros/annex-k-macros.h @@ -1,10 +1,15 @@ -//===-- Definition of macros to be used with Annex K functions ------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines macros used with Annex K functions. +/// +//===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_INCLUDE_LLVM_LIBC_MACROS_ANNEX_K_MACROS_H #define LLVM_LIBC_INCLUDE_LLVM_LIBC_MACROS_ANNEX_K_MACROS_H @@ -20,6 +25,8 @@ #define LIBC_HAS_ANNEX_K +#define LIBC_ERRNO_T_ERROR_VALUE 1 + #endif // defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ == 1 #endif // (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || diff --git a/libc/src/string/strcpy_s.cpp b/libc/src/string/strcpy_s.cpp index f9bf2d5fd480e..6897ebd89b7df 100644 --- a/libc/src/string/strcpy_s.cpp +++ b/libc/src/string/strcpy_s.cpp @@ -11,41 +11,39 @@ /// //===----------------------------------------------------------------------===// +#include "src/string/strcpy_s.h" #define __STDC_WANT_LIB_EXT1__ 1 #include "hdr/stdint_proxy.h" #undef __STDC_WANT_LIB_EXT1__ #include "hdr/types/constraint_handler_t.h" #include "hdr/types/errno_t.h" #include "hdr/types/rsize_t.h" +#include "src/__support/CPP/expected.h" #include "src/__support/common.h" #include "src/__support/constraint_handler.h" #include "src/__support/macros/config.h" #include "src/string/memory_utils/inline_memcpy.h" -#include "src/string/strcpy_s.h" #include "src/string/string_utils.h" -#include "src/string/strnlen_s.h" namespace LIBC_NAMESPACE_DECL { -LLVM_LIBC_FUNCTION(errno_t, strcpy_s, - (char *__restrict s1, rsize_t s1max, - const char *__restrict s2)) { - const char *constraint_violation_msg = 0; - size_t count; +LIBC_INLINE static cpp::expected<size_t, const char *> +validate(char *__restrict s1, rsize_t s1max, const char *__restrict s2) { + if (s1 == nullptr) + return cpp::unexpected("strcpy_s: s1 cannot be null"); + if (s2 == nullptr) + return cpp::unexpected("strcpy_s: s2 cannot be null"); + if (s1max > RSIZE_MAX) + return cpp::unexpected("strcpy_s: s1max cannot exceed RSIZE_MAX"); + if (s1max == 0) + return cpp::unexpected("strcpy_s: s1max cannot be 0"); + + // count can't be greater than s1max by definition, so no need to check for + // this case. + size_t count = internal::strnlen_s(s2, s1max); + if (s1max == count) + return cpp::unexpected("strcpy_s: s1max is too small for s2"); - if (s1 == 0) { - constraint_violation_msg = "strcpy_s: s1 cannot be null"; - } else if (s2 == 0) { - constraint_violation_msg = "strcpy_s: s2 cannot be null"; - } else if (s1max > RSIZE_MAX) { - constraint_violation_msg = "strcpy_s: s1max cannot exceed RSIZE_MAX"; - } else if (s1max == 0) { - constraint_violation_msg = "strcpy_s: s1max cannot be 0"; - } else if (count = strnlen_s(s2, s1max); - s1max == count) { // count can't be greater than s1max by - // definition, so no need to check for this case - constraint_violation_msg = "strcpy_s: s1max is too small for s2"; - } // Check overlap using the full regions defined by the standard, including the // terminating null byte: // destination: [s1, s1 + s1max) @@ -53,22 +51,30 @@ LLVM_LIBC_FUNCTION(errno_t, strcpy_s, // Use s1max for the destination's length, not count + 1, because the // standard allows for overwriting the entire destination region, even if // s2's length is smaller than s1max. - else if (const uintptr_t s1_addr = reinterpret_cast<uintptr_t>(s1), - s2_addr = reinterpret_cast<uintptr_t>(s2); - s1_addr < s2_addr ? s2_addr - s1_addr < s1max - : s1_addr - s2_addr < count + 1) { - constraint_violation_msg = "strcpy_s: s1 and s2 cannot overlap"; - } + const uintptr_t s1_addr = reinterpret_cast<uintptr_t>(s1); + const uintptr_t s2_addr = reinterpret_cast<uintptr_t>(s2); + if (s1_addr < s2_addr ? s2_addr - s1_addr < s1max + : s1_addr - s2_addr < count + 1) + return cpp::unexpected("strcpy_s: s1 and s2 cannot overlap"); + + return count; +} + +LLVM_LIBC_FUNCTION(errno_t, strcpy_s, + (char *__restrict s1, rsize_t s1max, + const char *__restrict s2)) { + const auto count_expected = validate(s1, s1max, s2); - if (constraint_violation_msg) { - if (s1 != 0 && s1max > 0 && s1max <= RSIZE_MAX) { + if (!count_expected.has_value()) { + if (s1 != nullptr && s1max > 0 && s1max <= RSIZE_MAX) { s1[0] = '\0'; } - libc_global_constraint_handler(constraint_violation_msg, 0, 1); - return 1; + libc_global_constraint_handler(count_expected.error(), 0, + LIBC_ERRNO_T_ERROR_VALUE); + return LIBC_ERRNO_T_ERROR_VALUE; } - inline_memcpy(s1, s2, count + 1); + inline_memcpy(s1, s2, count_expected.value() + 1); return 0; } diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h index 8c8879b4886e8..758ba9623124e 100644 --- a/libc/src/string/string_utils.h +++ b/libc/src/string/string_utils.h @@ -130,6 +130,10 @@ LIBC_INLINE size_t strnlen(const char *s, size_t max_len) { return temp ? reinterpret_cast<const char *>(temp) - s : max_len; } +LIBC_INLINE size_t strnlen_s(const char *s, size_t n) { + return (s != nullptr) ? internal::strnlen(s, n) : 0; +} + } // namespace internal } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/string/strnlen_s.cpp b/libc/src/string/strnlen_s.cpp index dab0dec77efd8..b19a3d0a97244 100644 --- a/libc/src/string/strnlen_s.cpp +++ b/libc/src/string/strnlen_s.cpp @@ -15,7 +15,7 @@ namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(size_t, strnlen_s, (const char *s, size_t n)) { - return (s != 0) ? internal::strnlen(s, n) : 0; + return internal::strnlen_s(s, n); } } // namespace LIBC_NAMESPACE_DECL _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
