llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-libc Author: Victor Campos (vhscampos) <details> <summary>Changes</summary> This patch adds Annex K's `strcpy_s`. --- Full diff: https://github.com/llvm/llvm-project/pull/197709.diff 11 Files Affected: - (modified) libc/config/baremetal/aarch64/entrypoints.txt (+1) - (modified) libc/config/baremetal/arm/entrypoints.txt (+1) - (modified) libc/config/linux/aarch64/entrypoints.txt (+1) - (modified) libc/config/linux/arm/entrypoints.txt (+1) - (modified) libc/config/linux/x86_64/entrypoints.txt (+1) - (modified) libc/include/string.yaml (+9) - (modified) libc/src/string/CMakeLists.txt (+19) - (added) libc/src/string/strcpy_s.cpp (+71) - (added) libc/src/string/strcpy_s.h (+22) - (modified) libc/test/src/string/CMakeLists.txt (+13) - (added) libc/test/src/string/strcpy_s_test.cpp (+122) ``````````diff 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..7553d2284d906 --- /dev/null +++ b/libc/src/string/strcpy_s.cpp @@ -0,0 +1,71 @@ +//===-- Implementation of strcpy_s ----------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// 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" + +#define ERRNO_T_FAIL 1 + +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 is null"; + } else if (s2 == 0) { + constraint_violation_msg = "strcpy_s: s2 is null"; + } else if (s1max > RSIZE_MAX) { + constraint_violation_msg = "strcpy_s: s1max exceeds RSIZE_MAX"; + } else if (s1max == 0) { + constraint_violation_msg = "strcpy_s: s1max is 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 overlap"; + } + + if (constraint_violation_msg) { + if (s1 != 0 && s1max > 0 && s1max <= RSIZE_MAX) { + s1[0] = '\0'; + } + libc_global_constraint_handler(constraint_violation_msg, 0, ERRNO_T_FAIL); + return ERRNO_T_FAIL; + } + + 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..7a38f8dbbf890 --- /dev/null +++ b/libc/src/string/strcpy_s.h @@ -0,0 +1,22 @@ +//===-- Implementation header for strcpy_s ----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#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..40625337b001f --- /dev/null +++ b/libc/test/src/string/strcpy_s_test.cpp @@ -0,0 +1,122 @@ +//===-- Unittests for strcpy_s --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#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"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), s2); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s1, s2); + ASSERT_STREQ(buffer, ""); +} + +TEST_F(LlvmLibcStrCpySTest, ExactFitSuccessfulCopy) { + char s1[4]; + const char *s2 = "abc"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), s2); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s1, s2); + ASSERT_STREQ(buffer, ""); +} + +TEST_F(LlvmLibcStrCpySTest, AdjacentObjectsDoNotOverlap) { + char s[8] = {'a', 'b', 'c', '\0', '?', '?', '?', '?'}; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s + 4, 4, s); + ASSERT_EQ(result, 0); + ASSERT_STREQ(s + 4, "abc"); + ASSERT_STREQ(buffer, ""); +} + +TEST_F(LlvmLibcStrCpySTest, EmptySourceString) { + char s1[4]; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, sizeof(s1), ""); + ASSERT_EQ(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_STREQ(buffer, ""); +} + +// Failure cases +TEST_F(LlvmLibcStrCpySTest, NullS1) { + const char *s2 = "abc"; + char *s1 = 0; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 4, s2); + ASSERT_NE(result, 0); + ASSERT_STREQ(buffer, "strcpy_s: s1 is null"); +} + +TEST_F(LlvmLibcStrCpySTest, NullS2) { + char s1[4]; + const char *s2 = 0; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 4, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_STREQ(buffer, "strcpy_s: s2 is null"); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxGreaterThanRSizeMax) { + char s1[4]; + const char *s2 = "abc"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, RSIZE_MAX + 1, s2); + ASSERT_NE(result, 0); + ASSERT_STREQ(buffer, "strcpy_s: s1max exceeds RSIZE_MAX"); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxIsZero) { + char s1[4]; + const char *s2 = "abc"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 0, s2); + ASSERT_NE(result, 0); + ASSERT_STREQ(buffer, "strcpy_s: s1max is 0"); +} + +TEST_F(LlvmLibcStrCpySTest, S1MaxTooSmallForS2) { + char s1[3]; + const char *s2 = "abc"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s1, 3, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_STREQ(buffer, "strcpy_s: s1max is too small for s2"); + + s2 = "abcd"; + s1[0] = '?'; + buffer[0] = '\0'; + result = LIBC_NAMESPACE::strcpy_s(s1, 3, s2); + ASSERT_NE(result, 0); + ASSERT_EQ(s1[0], '\0'); + ASSERT_STREQ(buffer, "strcpy_s: s1max is too small for s2"); +} + +TEST_F(LlvmLibcStrCpySTest, OverlappingObjects) { + char s[10] = "123456789"; + + errno_t result = LIBC_NAMESPACE::strcpy_s(s, 6, s + 4); + ASSERT_NE(result, 0); + ASSERT_EQ(s[0], '\0'); + ASSERT_STREQ(buffer, "strcpy_s: s1 and s2 overlap"); +} `````````` </details> https://github.com/llvm/llvm-project/pull/197709 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
