[v2] libstdc++: Fix stack corruption in std::bitset stream extraction [PR124370]

Message ID 20261002094031.3409189-1-jwakely@redhat.com
State Committed
Commit 5d419c69ce2e6b86d7da75f18736904885674219
Headers
Series [v2] libstdc++: Fix stack corruption in std::bitset stream extraction [PR124370] |

Commit Message

Jonathan Wakely Oct. 2, 2026, 9:37 a.m. UTC
  The __builtin_alloca argument didn't account for the size or alignment
of wide characters.
Replace __builtin_alloca with an array _CharT[small ? N : 1] which is
used for small, or a heap allocation otherwise.

libstdc++-v3/ChangeLog:

	PR libstdc++/124370
	* include/std/bitset (operator>>): Use array inside _Buffer
	instead of __builtin_alloca.
	* testsuite/20_util/bitset/io/input.cc: Suppress pedwarn about
	C++14 binary literal.
	* testsuite/20_util/bitset/io/wchar_t.cc: New test.
---

v2: replaced __builtin_alloca with _CharT array as suggested by Tomasz.
Added pragmas to disable warnings about 0b literals in tests.  Added
bitset<0> check to new test.

 libstdc++-v3/include/std/bitset               | 31 ++++++++-------
 .../testsuite/20_util/bitset/io/input.cc      |  2 +
 .../testsuite/20_util/bitset/io/wchar_t.cc    | 39 +++++++++++++++++++
 3 files changed, 57 insertions(+), 15 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
  

Comments

Tomasz Kaminski Oct. 2, 2026, 1:19 p.m. UTC | #1
On Fri, Oct 2, 2026 at 11:41 AM Jonathan Wakely <jwakely@redhat.com> wrote:

> The __builtin_alloca argument didn't account for the size or alignment
> of wide characters.
> Replace __builtin_alloca with an array _CharT[small ? N : 1] which is
> used for small, or a heap allocation otherwise.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/124370
>         * include/std/bitset (operator>>): Use array inside _Buffer
>         instead of __builtin_alloca.
>         * testsuite/20_util/bitset/io/input.cc: Suppress pedwarn about
>         C++14 binary literal.
>         * testsuite/20_util/bitset/io/wchar_t.cc: New test.
> ---
>
> v2: replaced __builtin_alloca with _CharT array as suggested by Tomasz.
> Added pragmas to disable warnings about 0b literals in tests.  Added
> bitset<0> check to new test.
>
LGTM thanks. __use_local flags look more readable.

>
>  libstdc++-v3/include/std/bitset               | 31 ++++++++-------
>  .../testsuite/20_util/bitset/io/input.cc      |  2 +
>  .../testsuite/20_util/bitset/io/wchar_t.cc    | 39 +++++++++++++++++++
>  3 files changed, 57 insertions(+), 15 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
>
> diff --git a/libstdc++-v3/include/std/bitset
> b/libstdc++-v3/include/std/bitset
> index d581606e7dde..469c8da9ece4 100644
> --- a/libstdc++-v3/include/std/bitset
> +++ b/libstdc++-v3/include/std/bitset
> @@ -1705,30 +1705,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>        typedef std::basic_istream<_CharT, _Traits>  __istream_type;
>        typedef typename __istream_type::ios_base    __ios_base;
>
> -#pragma GCC diagnostic push
> -#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +      static const bool __use_local = _Nb * sizeof(_CharT) <= 256;
> +
>        struct _Buffer
>        {
> -       static _GLIBCXX_CONSTEXPR bool _S_use_alloca() { return _Nb <=
> 256; }
> -
> -       explicit _Buffer(_CharT* __p) : _M_ptr(__p) { }
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +       _Buffer() : _M_ptr(_M_local)
> +       {
> +         if _GLIBCXX_CONSTEXPR (!__use_local)
> +           _M_ptr = new _CharT[_Nb];
> +       }
>
>         ~_Buffer()
>         {
> -         if _GLIBCXX_CONSTEXPR (!_S_use_alloca())
> +         if _GLIBCXX_CONSTEXPR (!__use_local)
>             delete[] _M_ptr;
>         }
> -
> -       _CharT* const _M_ptr;
> -      };
> -      _CharT* __ptr;
> -      if _GLIBCXX_CONSTEXPR (_Buffer::_S_use_alloca())
> -       __ptr = (_CharT*)__builtin_alloca(_Nb);
> -      else
> -       __ptr = new _CharT[_Nb];
> -      const _Buffer __buf(__ptr);
>  #pragma GCC diagnostic pop
>
> +       _CharT* _M_ptr;
> +       _CharT _M_local[__use_local && _Nb != 0 ? _Nb : 1];
> +      } __buf;
> +
> +      _CharT* __ptr = __buf._M_ptr;
> +
>        // _GLIBCXX_RESOLVE_LIB_DEFECTS
>        // 303. Bitset input operator underspecified
>        const char_type __zero = __is.widen('0');
> diff --git a/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
> b/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
> index b0e301a42b10..a01e48cf2847 100644
> --- a/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
> +++ b/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
> @@ -42,6 +42,8 @@ void test01()
>    VERIFY( ss.rdstate() == ios_base::goodbit ); // LWG 3199
>  }
>
> +#pragma GCC diagnostic ignored "-Wc++14-extensions" // 0b literals
> +
>  void
>  test02()
>  {
> diff --git a/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
> b/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
> new file mode 100644
> index 000000000000..6c7c496daa17
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
> @@ -0,0 +1,39 @@
> +// { dg-do run }
> +
> +// C++98 23.3.5.3 bitset operators
> +
> +#include <bitset>
> +#include <sstream>
> +#include <testsuite_hooks.h>
> +
> +#pragma GCC diagnostic ignored "-Wc++14-extensions" // 0b literals
> +
> +// Bug 124370 - Out-of-bounds write for wistream >> bitset
> +void
> +test_pr124370()
> +{
> +  std::wistringstream input(L"10011011001101");
> +  std::bitset<10> b;
> +  input >> b;
> +  VERIFY( b == std::bitset<10>(0b1001101100) );
> +}
> +
> +// Round-trip through a wide stream.
> +template<int N>
> +void
> +test_round_trip()
> +{
> +  std::bitset<N> a(0b100110110011010101101010), b;
> +  std::wstringstream ss;
> +  ss << a;
> +  ss >> b;
> +  VERIFY( b == a );
> +}
> +
> +int main()
> +{
> +  test_pr124370();
> +  test_round_trip<0>();
> +  test_round_trip<24>();
> +  test_round_trip<1024>();
> +}
> --
> 2.55.0
>
>
  

Patch

diff --git a/libstdc++-v3/include/std/bitset b/libstdc++-v3/include/std/bitset
index d581606e7dde..469c8da9ece4 100644
--- a/libstdc++-v3/include/std/bitset
+++ b/libstdc++-v3/include/std/bitset
@@ -1705,30 +1705,31 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef std::basic_istream<_CharT, _Traits>  __istream_type;
       typedef typename __istream_type::ios_base    __ios_base;
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+      static const bool __use_local = _Nb * sizeof(_CharT) <= 256;
+
       struct _Buffer
       {
-	static _GLIBCXX_CONSTEXPR bool _S_use_alloca() { return _Nb <= 256; }
-
-	explicit _Buffer(_CharT* __p) : _M_ptr(__p) { }
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+	_Buffer() : _M_ptr(_M_local)
+	{
+	  if _GLIBCXX_CONSTEXPR (!__use_local)
+	    _M_ptr = new _CharT[_Nb];
+	}
 
 	~_Buffer()
 	{
-	  if _GLIBCXX_CONSTEXPR (!_S_use_alloca())
+	  if _GLIBCXX_CONSTEXPR (!__use_local)
 	    delete[] _M_ptr;
 	}
-
-	_CharT* const _M_ptr;
-      };
-      _CharT* __ptr;
-      if _GLIBCXX_CONSTEXPR (_Buffer::_S_use_alloca())
-	__ptr = (_CharT*)__builtin_alloca(_Nb);
-      else
-	__ptr = new _CharT[_Nb];
-      const _Buffer __buf(__ptr);
 #pragma GCC diagnostic pop
 
+	_CharT* _M_ptr;
+	_CharT _M_local[__use_local && _Nb != 0 ? _Nb : 1];
+      } __buf;
+
+      _CharT* __ptr = __buf._M_ptr;
+
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
       // 303. Bitset input operator underspecified
       const char_type __zero = __is.widen('0');
diff --git a/libstdc++-v3/testsuite/20_util/bitset/io/input.cc b/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
index b0e301a42b10..a01e48cf2847 100644
--- a/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
+++ b/libstdc++-v3/testsuite/20_util/bitset/io/input.cc
@@ -42,6 +42,8 @@  void test01()
   VERIFY( ss.rdstate() == ios_base::goodbit ); // LWG 3199
 }
 
+#pragma GCC diagnostic ignored "-Wc++14-extensions" // 0b literals
+
 void
 test02()
 {
diff --git a/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc b/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
new file mode 100644
index 000000000000..6c7c496daa17
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bitset/io/wchar_t.cc
@@ -0,0 +1,39 @@ 
+// { dg-do run }
+
+// C++98 23.3.5.3 bitset operators
+
+#include <bitset>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+#pragma GCC diagnostic ignored "-Wc++14-extensions" // 0b literals
+
+// Bug 124370 - Out-of-bounds write for wistream >> bitset
+void
+test_pr124370()
+{
+  std::wistringstream input(L"10011011001101");
+  std::bitset<10> b;
+  input >> b;
+  VERIFY( b == std::bitset<10>(0b1001101100) );
+}
+
+// Round-trip through a wide stream.
+template<int N>
+void
+test_round_trip()
+{
+  std::bitset<N> a(0b100110110011010101101010), b;
+  std::wstringstream ss;
+  ss << a;
+  ss >> b;
+  VERIFY( b == a );
+}
+
+int main()
+{
+  test_pr124370();
+  test_round_trip<0>();
+  test_round_trip<24>();
+  test_round_trip<1024>();
+}