[libcxx-commits] [libcxx] [libc++] Fix wraparound issue with -fsanitize=integer in string operator>> (PR #106263)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 28 12:20:14 PDT 2024


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/106263

>From 82371525c5b169b704e3bd3288f1eaab4945803c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 27 Aug 2024 14:13:26 -0400
Subject: [PATCH 1/3] [libc++] Fix wraparound issue with -fsanitize=integer in
 string operator>>

Fixes #106261
rdar://133991190
---
 libcxx/include/istream | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/istream b/libcxx/include/istream
index d2b577a9ad9efc..84cb0ac9d9f252 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -1211,12 +1211,12 @@ operator>>(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _
     try {
 #endif
       __str.clear();
-      streamsize __n = __is.width();
-      if (__n <= 0)
-        __n = __str.max_size();
-      if (__n <= 0)
-        __n = numeric_limits<streamsize>::max();
-      streamsize __c            = 0;
+      using _Size = typename basic_string<_CharT, _Traits, _Allocator>::size_type;
+      static_assert(numeric_limits<_Size>::max() >= numeric_limits<streamsize>::max(),
+                    "Stream width could be too large to be represented in the string's size_type");
+      streamsize const __width  = __is.width();
+      _Size const __n           = __width <= 0 ? __str.max_size() : static_cast<_Size>(__width);
+      _Size __c                 = 0;
       const ctype<_CharT>& __ct = std::use_facet<ctype<_CharT> >(__is.getloc());
       while (__c < __n) {
         typename _Traits::int_type __i = __is.rdbuf()->sgetc();

>From 2e12c321bd8d738d6f965fe83e2f0f1db0e78f2a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 27 Aug 2024 16:30:08 -0400
Subject: [PATCH 2/3] Accept smaller size_types than streamsizes

---
 libcxx/include/istream | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/istream b/libcxx/include/istream
index 84cb0ac9d9f252..aa539177e295ad 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -1211,11 +1211,10 @@ operator>>(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _
     try {
 #endif
       __str.clear();
-      using _Size = typename basic_string<_CharT, _Traits, _Allocator>::size_type;
-      static_assert(numeric_limits<_Size>::max() >= numeric_limits<streamsize>::max(),
-                    "Stream width could be too large to be represented in the string's size_type");
+      using _Size               = typename basic_string<_CharT, _Traits, _Allocator>::size_type;
       streamsize const __width  = __is.width();
-      _Size const __n           = __width <= 0 ? __str.max_size() : static_cast<_Size>(__width);
+      _Size const __max_size    = __str.max_size();
+      _Size const __n           = __width <= 0 ? __max_size : std::min(__max_size, static_cast<_Size>(__width));
       _Size __c                 = 0;
       const ctype<_CharT>& __ct = std::use_facet<ctype<_CharT> >(__is.getloc());
       while (__c < __n) {

>From c31b0fbb0ea5bc44e9dc6e90125417a2b8804590 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 28 Aug 2024 15:18:45 -0400
Subject: [PATCH 3/3] Review comments, take two

---
 libcxx/include/__type_traits/make_unsigned.h |  4 +---
 libcxx/include/istream                       | 15 +++++++++++----
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__type_traits/make_unsigned.h b/libcxx/include/__type_traits/make_unsigned.h
index 282cd2d9113166..8757f451eb807b 100644
--- a/libcxx/include/__type_traits/make_unsigned.h
+++ b/libcxx/include/__type_traits/make_unsigned.h
@@ -86,12 +86,10 @@ template <class _Tp>
 using make_unsigned_t = __make_unsigned_t<_Tp>;
 #endif
 
-#ifndef _LIBCPP_CXX03_LANG
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr __make_unsigned_t<_Tp> __to_unsigned_like(_Tp __x) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __make_unsigned_t<_Tp> __to_unsigned_like(_Tp __x) _NOEXCEPT {
   return static_cast<__make_unsigned_t<_Tp> >(__x);
 }
-#endif
 
 template <class _Tp, class _Up>
 using __copy_unsigned_t = __conditional_t<is_unsigned<_Tp>::value, __make_unsigned_t<_Up>, _Up>;
diff --git a/libcxx/include/istream b/libcxx/include/istream
index aa539177e295ad..7c65a24bc313d9 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -165,6 +165,7 @@ template <class Stream, class T>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_base_of.h>
+#include <__type_traits/make_unsigned.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <bitset>
@@ -1211,10 +1212,16 @@ operator>>(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _
     try {
 #endif
       __str.clear();
-      using _Size               = typename basic_string<_CharT, _Traits, _Allocator>::size_type;
-      streamsize const __width  = __is.width();
-      _Size const __max_size    = __str.max_size();
-      _Size const __n           = __width <= 0 ? __max_size : std::min(__max_size, static_cast<_Size>(__width));
+      using _Size              = typename basic_string<_CharT, _Traits, _Allocator>::size_type;
+      streamsize const __width = __is.width();
+      _Size const __max_size   = __str.max_size();
+      _Size __n;
+      if (__width <= 0) {
+        __n = __max_size;
+      } else {
+        __n = std::__to_unsigned_like(__width) < __max_size ? static_cast<_Size>(__width) : __max_size;
+      }
+
       _Size __c                 = 0;
       const ctype<_CharT>& __ct = std::use_facet<ctype<_CharT> >(__is.getloc());
       while (__c < __n) {



More information about the libcxx-commits mailing list