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] [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); +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
