[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