[libc-commits] [libc] [libc] fix oob and overflow bugs in wcslcat and wcsncat (PR #203697)

Hardik Kumar via libc-commits libc-commits at lists.llvm.org
Mon Jun 15 08:09:10 PDT 2026


https://github.com/hardikxk updated https://github.com/llvm/llvm-project/pull/203697

>From 227a06374e3ca1d33d6faec3a7552611c2f48057 Mon Sep 17 00:00:00 2001
From: hardikxk <hardikxk at gmail.com>
Date: Sat, 13 Jun 2026 16:32:06 +0530
Subject: [PATCH 1/3] fix oob and overflow bugs in llc

---
 libc/src/wchar/wcslcat.cpp | 2 ++
 libc/src/wchar/wcsncat.cpp | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/libc/src/wchar/wcslcat.cpp b/libc/src/wchar/wcslcat.cpp
index eb318e066f7a0..374b47c8b3578 100644
--- a/libc/src/wchar/wcslcat.cpp
+++ b/libc/src/wchar/wcslcat.cpp
@@ -21,6 +21,8 @@ LLVM_LIBC_FUNCTION(size_t, wcslcat,
                     size_t dstsize)) {
   const size_t dstlen = internal::string_length(dst);
   const size_t srclen = internal::string_length(src);
+  if (dstlen >= dstsize) 
+    return dstsize + srclen;
   int limit = static_cast<int>(dstsize - dstlen - 1);
   size_t returnval = (dstsize < dstlen ? dstsize : dstlen) + srclen;
   if (limit < 0)
diff --git a/libc/src/wchar/wcsncat.cpp b/libc/src/wchar/wcsncat.cpp
index 62595b4b5418c..985fb5f1cbace 100644
--- a/libc/src/wchar/wcsncat.cpp
+++ b/libc/src/wchar/wcsncat.cpp
@@ -21,7 +21,7 @@ LLVM_LIBC_FUNCTION(wchar_t *, wcsncat,
                     size_t n)) {
   size_t size = internal::string_length(s1);
   size_t i = 0;
-  for (; s2[i] && i < n; ++i)
+  for (; i < n && s2[i]; ++i)
     s1[size + i] = s2[i];
   // Appending null character to the end of the result.
   s1[size + i] = L'\0';

>From f6fdbb1df04ffb9d8cd56e08a772ce3a5594adb9 Mon Sep 17 00:00:00 2001
From: hardikxk <hardikxk at gmail.com>
Date: Mon, 15 Jun 2026 17:09:03 +0530
Subject: [PATCH 2/3] [libc] Fix OOB read in wcsncat and overflow in wcslcat

Prevented a potential buffer overflow in wcslcat and an out-of-bounds
read in wcsncat:

- wcslcat: Added an early exit when dstlen >= dstsize to avoid negative
    limit calculations and integer wrap during static_cast.
- wcsncat: Reordered loop conditions to check bounds (i < n) before
    dereferencing the source pointer (s2[i]).

Fixes [#203649](https://github.com/llvm/llvm-project/issues/203649).
---
 libc/src/wchar/wcslcat.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/wchar/wcslcat.cpp b/libc/src/wchar/wcslcat.cpp
index 374b47c8b3578..9c063d1de131a 100644
--- a/libc/src/wchar/wcslcat.cpp
+++ b/libc/src/wchar/wcslcat.cpp
@@ -24,7 +24,7 @@ LLVM_LIBC_FUNCTION(size_t, wcslcat,
   if (dstlen >= dstsize) 
     return dstsize + srclen;
   int limit = static_cast<int>(dstsize - dstlen - 1);
-  size_t returnval = (dstsize < dstlen ? dstsize : dstlen) + srclen;
+  size_t returnval = dstlen + srclen;
   if (limit < 0)
     return returnval;
   int i = 0;

>From 43a87d257b563f00e61d5452e429619791b8dc39 Mon Sep 17 00:00:00 2001
From: hardikxk <hardikxk at gmail.com>
Date: Mon, 15 Jun 2026 20:38:45 +0530
Subject: [PATCH 3/3] [libc] Add tests for wcslcat for checking buffer overflow
 Add test for wcsncat for checking potential OOB access.

---
 libc/src/wchar/wcslcat.cpp           |  2 +-
 libc/test/src/wchar/wcslcat_test.cpp | 12 ++++++++++++
 libc/test/src/wchar/wcsncat_test.cpp | 12 ++++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/libc/src/wchar/wcslcat.cpp b/libc/src/wchar/wcslcat.cpp
index 9c063d1de131a..456aac4543e41 100644
--- a/libc/src/wchar/wcslcat.cpp
+++ b/libc/src/wchar/wcslcat.cpp
@@ -21,7 +21,7 @@ LLVM_LIBC_FUNCTION(size_t, wcslcat,
                     size_t dstsize)) {
   const size_t dstlen = internal::string_length(dst);
   const size_t srclen = internal::string_length(src);
-  if (dstlen >= dstsize) 
+  if (dstlen >= dstsize)
     return dstsize + srclen;
   int limit = static_cast<int>(dstsize - dstlen - 1);
   size_t returnval = dstlen + srclen;
diff --git a/libc/test/src/wchar/wcslcat_test.cpp b/libc/test/src/wchar/wcslcat_test.cpp
index bad37496384e2..564a5c60006e6 100644
--- a/libc/test/src/wchar/wcslcat_test.cpp
+++ b/libc/test/src/wchar/wcslcat_test.cpp
@@ -55,3 +55,15 @@ TEST(LlvmLibcWCSLCatTest, SmallerNoOverwriteAfter0) {
   ASSERT_TRUE(dst[7] == L'\0');
   ASSERT_EQ(res, size_t(4));
 }
+
+TEST(LlvmLibcWCSLCatTest, DstsizeLessThanDstlen) {
+  const wchar_t *src = L"d";
+  wchar_t dst[4]{L"abc"};
+  // Should return src length + dst size
+  size_t res = LIBC_NAMESPACE::wcslcat(dst, src, 2);
+  ASSERT_TRUE(dst[0] == L'a');
+  ASSERT_TRUE(dst[1] == L'b');
+  ASSERT_TRUE(dst[2] == L'c');
+  ASSERT_TRUE(dst[3] == L'\0');
+  ASSERT_EQ(res, size_t(3));
+}
diff --git a/libc/test/src/wchar/wcsncat_test.cpp b/libc/test/src/wchar/wcsncat_test.cpp
index 47359f88cec9e..0795685713e00 100644
--- a/libc/test/src/wchar/wcsncat_test.cpp
+++ b/libc/test/src/wchar/wcsncat_test.cpp
@@ -80,3 +80,15 @@ TEST(LlvmLibcWCSNCatTest, NonEmptyDest) {
   ASSERT_TRUE(dest[3] == L'\0');
   ASSERT_TRUE(dest[4] == L'Z');
 }
+
+TEST(LlvmLibcWCSNCatTest, OOBAccess) {
+  wchar_t dest[4] = {L'\0'};
+  wchar_t src[3] = {L'a', L'b', L'c'};
+
+  // Checking the bound
+  LIBC_NAMESPACE::wcsncat(dest, src, 3);
+  ASSERT_TRUE(dest[0] == L'a');
+  ASSERT_TRUE(dest[1] == L'b');
+  ASSERT_TRUE(dest[2] == L'c');
+  ASSERT_TRUE(dest[3] == L'\0');
+}



More information about the libc-commits mailing list