[libc-commits] [libc] [libc] Fix strcasecmp/strncasecmp signedness and add tests (PR #192632)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Fri Apr 17 04:20:04 PDT 2026
https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/192632
Fixed character signedness bug in strcasecmp and strncasecmp implementations in src/strings/ where characters > 127 were not correctly handled.
Added LIBC_CRASH_ON_NULLPTR checks to both functions.
Enhanced unit tests in test/src/strings/ to be comprehensive without duplicating basic case insensitivity tests.
Updated assertions in strcasecmp_test, strncasecmp_test, and strcmp_test to check for sign instead of exact value.
>From 3b59e094a9edc0ee781f7a39e698de6d3f455373 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 17 Apr 2026 12:16:57 +0100
Subject: [PATCH] [libc] Fix strcasecmp/strncasecmp signedness and add tests
Fixed character signedness bug in strcasecmp and strncasecmp
implementations in src/strings/ where characters > 127 were not
correctly handled.
Added LIBC_CRASH_ON_NULLPTR checks to both functions.
Enhanced unit tests in test/src/strings/ to be comprehensive without
duplicating basic case insensitivity tests.
Updated assertions in strcasecmp_test, strncasecmp_test, and
strcmp_test to check for sign instead of exact value.
---
libc/src/strings/strcasecmp.cpp | 9 ++--
libc/src/strings/strncasecmp.cpp | 9 ++--
libc/test/src/string/CMakeLists.txt | 2 +
libc/test/src/string/strcmp_test.cpp | 30 ++++--------
libc/test/src/strings/strcasecmp_test.cpp | 47 ++++++++++++++++++
libc/test/src/strings/strncasecmp_test.cpp | 57 ++++++++++++++++++++++
6 files changed, 128 insertions(+), 26 deletions(-)
diff --git a/libc/src/strings/strcasecmp.cpp b/libc/src/strings/strcasecmp.cpp
index 4518647deabe4..1500b68635358 100644
--- a/libc/src/strings/strcasecmp.cpp
+++ b/libc/src/strings/strcasecmp.cpp
@@ -11,14 +11,17 @@
#include "src/__support/common.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
#include "src/string/memory_utils/inline_strcmp.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, strcasecmp, (const char *left, const char *right)) {
- auto case_cmp = [](char a, char b) {
- return static_cast<int>(LIBC_NAMESPACE::internal::tolower(a)) -
- static_cast<int>(LIBC_NAMESPACE::internal::tolower(b));
+ LIBC_CRASH_ON_NULLPTR(left);
+ LIBC_CRASH_ON_NULLPTR(right);
+ auto case_cmp = [](char a, char b) -> int {
+ return static_cast<int>(static_cast<unsigned char>(internal::tolower(a))) -
+ static_cast<int>(static_cast<unsigned char>(internal::tolower(b)));
};
return inline_strcmp(left, right, case_cmp);
}
diff --git a/libc/src/strings/strncasecmp.cpp b/libc/src/strings/strncasecmp.cpp
index a5926495a3e22..c63cf0c08e412 100644
--- a/libc/src/strings/strncasecmp.cpp
+++ b/libc/src/strings/strncasecmp.cpp
@@ -11,15 +11,18 @@
#include "src/__support/common.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
#include "src/string/memory_utils/inline_strcmp.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, strncasecmp,
(const char *left, const char *right, size_t n)) {
- auto case_cmp = [](char a, char b) {
- return static_cast<int>(LIBC_NAMESPACE::internal::tolower(a)) -
- static_cast<int>(LIBC_NAMESPACE::internal::tolower(b));
+ LIBC_CRASH_ON_NULLPTR(left);
+ LIBC_CRASH_ON_NULLPTR(right);
+ auto case_cmp = [](char a, char b) -> int {
+ return static_cast<int>(static_cast<unsigned char>(internal::tolower(a))) -
+ static_cast<int>(static_cast<unsigned char>(internal::tolower(b)));
};
return inline_strncmp(left, right, n, case_cmp);
}
diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt
index 17927ea93ed1e..47587689a3014 100644
--- a/libc/test/src/string/CMakeLists.txt
+++ b/libc/test/src/string/CMakeLists.txt
@@ -119,6 +119,7 @@ add_libc_test(
libc.src.string.strcmp
)
+
add_libc_test(
strcasestr_test
SUITE
@@ -234,6 +235,7 @@ add_libc_test(
libc.src.string.strncat
)
+
add_libc_test(
strncmp_test
SUITE
diff --git a/libc/test/src/string/strcmp_test.cpp b/libc/test/src/string/strcmp_test.cpp
index 8844d8463d416..601fd166909c5 100644
--- a/libc/test/src/string/strcmp_test.cpp
+++ b/libc/test/src/string/strcmp_test.cpp
@@ -24,14 +24,12 @@ TEST(LlvmLibcStrCmpTest, EmptyStringShouldNotEqualNonEmptyString) {
const char *empty = "";
const char *s2 = "abc";
int result = LIBC_NAMESPACE::strcmp(empty, s2);
- // This should be '\0' - 'a' = -97
- ASSERT_EQ(result, '\0' - 'a');
+ ASSERT_LT(result, 0);
// Similar case if empty string is second argument.
const char *s3 = "123";
result = LIBC_NAMESPACE::strcmp(s3, empty);
- // This should be '1' - '\0' = 49
- ASSERT_EQ(result, '1' - '\0');
+ ASSERT_GT(result, 0);
}
TEST(LlvmLibcStrCmpTest, EqualStringsShouldReturnZero) {
@@ -49,51 +47,43 @@ TEST(LlvmLibcStrCmpTest, ShouldReturnResultOfFirstDifference) {
const char *s1 = "___B42__";
const char *s2 = "___C55__";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
- // This should return 'B' - 'C' = -1.
- ASSERT_EQ(result, 'B' - 'C');
+ ASSERT_LT(result, 0);
// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
- // This should return 'C' - 'B' = 1.
- ASSERT_EQ(result, 'C' - 'B');
+ ASSERT_GT(result, 0);
}
TEST(LlvmLibcStrCmpTest, CapitalizedLetterShouldNotBeEqual) {
const char *s1 = "abcd";
const char *s2 = "abCd";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
- // 'c' - 'C' = 32.
- ASSERT_EQ(result, 'c' - 'C');
+ ASSERT_GT(result, 0);
// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
- // 'C' - 'c' = -32.
- ASSERT_EQ(result, 'C' - 'c');
+ ASSERT_LT(result, 0);
}
TEST(LlvmLibcStrCmpTest, UnequalLengthStringsShouldNotReturnZero) {
const char *s1 = "abc";
const char *s2 = "abcd";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
- // '\0' - 'd' = -100.
- ASSERT_EQ(result, -'\0' - 'd');
+ ASSERT_LT(result, 0);
// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
- // 'd' - '\0' = 100.
- ASSERT_EQ(result, 'd' - '\0');
+ ASSERT_GT(result, 0);
}
TEST(LlvmLibcStrCmpTest, StringArgumentSwapChangesSign) {
const char *a = "a";
const char *b = "b";
int result = LIBC_NAMESPACE::strcmp(b, a);
- // 'b' - 'a' = 1.
- ASSERT_EQ(result, 'b' - 'a');
+ ASSERT_GT(result, 0);
result = LIBC_NAMESPACE::strcmp(a, b);
- // 'a' - 'b' = -1.
- ASSERT_EQ(result, 'a' - 'b');
+ ASSERT_LT(result, 0);
}
TEST(LlvmLibcStrCmpTest, Case) {
diff --git a/libc/test/src/strings/strcasecmp_test.cpp b/libc/test/src/strings/strcasecmp_test.cpp
index cd29c213a7324..f086486c758ba 100644
--- a/libc/test/src/strings/strcasecmp_test.cpp
+++ b/libc/test/src/strings/strcasecmp_test.cpp
@@ -44,3 +44,50 @@ TEST(LlvmLibcStrCaseCmpTest, Case) {
result = LIBC_NAMESPACE::strcasecmp(s2, s1);
ASSERT_EQ(result, 0);
}
+
+TEST(LlvmLibcStrCaseCmpTest, EqualStringsShouldReturnZero) {
+ const char *s1 = "abc";
+ const char *s2 = "abc";
+ int result = LIBC_NAMESPACE::strcasecmp(s1, s2);
+ ASSERT_EQ(result, 0);
+
+ result = LIBC_NAMESPACE::strcasecmp(s2, s1);
+ ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcStrCaseCmpTest, ShouldReturnResultOfFirstDifference) {
+ const char *s1 = "___B42__";
+ const char *s2 = "___C55__";
+ int result = LIBC_NAMESPACE::strcasecmp(s1, s2);
+ ASSERT_LT(result, 0);
+
+ result = LIBC_NAMESPACE::strcasecmp(s2, s1);
+ ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcStrCaseCmpTest, UnequalLengthStringsShouldNotReturnZero) {
+ const char *s1 = "abc";
+ const char *s2 = "abcd";
+ int result = LIBC_NAMESPACE::strcasecmp(s1, s2);
+ ASSERT_LT(result, 0);
+
+ result = LIBC_NAMESPACE::strcasecmp(s2, s1);
+ ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcStrCaseCmpTest, StringArgumentSwapChangesSign) {
+ const char *a = "a";
+ const char *b = "b";
+ int result = LIBC_NAMESPACE::strcasecmp(b, a);
+ ASSERT_GT(result, 0);
+
+ result = LIBC_NAMESPACE::strcasecmp(a, b);
+ ASSERT_LT(result, 0);
+}
+
+TEST(LlvmLibcStrCaseCmpTest, CharactersGreaterThan127ShouldBePositive) {
+ const char s1[] = {static_cast<char>(128), '\0'};
+ const char s2[] = {'\0'};
+ int result = LIBC_NAMESPACE::strcasecmp(s1, s2);
+ ASSERT_GT(result, 0);
+}
diff --git a/libc/test/src/strings/strncasecmp_test.cpp b/libc/test/src/strings/strncasecmp_test.cpp
index 870574ed9507c..aa886146aa36f 100644
--- a/libc/test/src/strings/strncasecmp_test.cpp
+++ b/libc/test/src/strings/strncasecmp_test.cpp
@@ -46,3 +46,60 @@ TEST(LlvmLibcStrNCaseCmpTest, Case) {
result = LIBC_NAMESPACE::strncasecmp(s2, s1, 2);
ASSERT_EQ(result, 0);
}
+
+TEST(LlvmLibcStrNCaseCmpTest, EqualStringsShouldReturnZero) {
+ const char *s1 = "abc";
+ const char *s2 = "abc";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 3);
+ ASSERT_EQ(result, 0);
+
+ result = LIBC_NAMESPACE::strncasecmp(s2, s1, 3);
+ ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcStrNCaseCmpTest, ShouldReturnResultOfFirstDifference) {
+ const char *s1 = "___B42__";
+ const char *s2 = "___C55__";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 8);
+ ASSERT_LT(result, 0);
+
+ result = LIBC_NAMESPACE::strncasecmp(s2, s1, 8);
+ ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcStrNCaseCmpTest, UnequalLengthStringsShouldNotReturnZero) {
+ const char *s1 = "abc";
+ const char *s2 = "abcd";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 4);
+ ASSERT_LT(result, 0);
+
+ result = LIBC_NAMESPACE::strncasecmp(s2, s1, 4);
+ ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcStrNCaseCmpTest, StringsEqualUpToNShouldReturnZero) {
+ const char *s1 = "abcD";
+ const char *s2 = "abcE";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 3);
+ ASSERT_EQ(result, 0);
+
+ result = LIBC_NAMESPACE::strncasecmp(s2, s1, 3);
+ ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcStrNCaseCmpTest, StringsEqualUpToNdifferentCaseShouldReturnZero) {
+ const char *s1 = "abcD";
+ const char *s2 = "ABCE";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 3);
+ ASSERT_EQ(result, 0);
+
+ result = LIBC_NAMESPACE::strncasecmp(s2, s1, 3);
+ ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcStrNCaseCmpTest, ZeroNShouldReturnZero) {
+ const char *s1 = "abc";
+ const char *s2 = "def";
+ int result = LIBC_NAMESPACE::strncasecmp(s1, s2, 0);
+ ASSERT_EQ(result, 0);
+}
More information about the libc-commits
mailing list