diff options
author | A. Jiang <de34@live.cn> | 2025-03-03 07:13:38 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-03 07:13:38 +0800 |
commit | ca0612c383bc1c487b8dabff9e5830af173a7da8 (patch) | |
tree | 30ccfc28b03d8499f1de23c4c52dea996b5a390d | |
parent | 60486292b79885b7800b082754153202bef5b1f0 (diff) | |
download | llvm-ca0612c383bc1c487b8dabff9e5830af173a7da8.zip llvm-ca0612c383bc1c487b8dabff9e5830af173a7da8.tar.gz llvm-ca0612c383bc1c487b8dabff9e5830af173a7da8.tar.bz2 |
[libc++] Fix `money_get::do_get` with huge input (#126273)
`money_get::do_get` needs to be fixed to handle extremely huge input
(e.g. more than 100 digits).
1. `__double_or_nothing` needs to copy the contents of the stack buffer
on the initial allocation.
2. The `sscanf` call in `do_get` needs to scan the dynamic buffer if
dynamic allocation happens.
-rw-r--r-- | libcxx/include/locale | 10 | ||||
-rw-r--r-- | libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp | 115 |
2 files changed, 122 insertions, 3 deletions
diff --git a/libcxx/include/locale b/libcxx/include/locale index 4f2716f..fa11ce3 100644 --- a/libcxx/include/locale +++ b/libcxx/include/locale @@ -2386,6 +2386,8 @@ _LIBCPP_HIDE_FROM_ABI void __double_or_nothing(unique_ptr<_Tp, void (*)(void*)>& std::__throw_bad_alloc(); if (__owns) __b.release(); + else + std::memcpy(__t, __b.get(), __cur_cap); __b = unique_ptr<_Tp, void (*)(void*)>(__t, free); __new_cap /= sizeof(_Tp); __n = __b.get() + __n_off; @@ -2581,20 +2583,22 @@ _InputIterator money_get<_CharT, _InputIterator>::do_get( char_type __atoms[sizeof(__src) - 1]; __ct.widen(__src, __src + (sizeof(__src) - 1), __atoms); char __nbuf[__bz]; - char* __nc = __nbuf; + char* __nc = __nbuf; + const char* __nc_in = __nc; unique_ptr<char, void (*)(void*)> __h(nullptr, free); if (__wn - __wb.get() > __bz - 2) { __h.reset((char*)malloc(static_cast<size_t>(__wn - __wb.get() + 2))); if (__h.get() == nullptr) std::__throw_bad_alloc(); - __nc = __h.get(); + __nc = __h.get(); + __nc_in = __nc; } if (__neg) *__nc++ = '-'; for (const char_type* __w = __wb.get(); __w < __wn; ++__w, ++__nc) *__nc = __src[std::find(__atoms, std::end(__atoms), *__w) - __atoms]; *__nc = char(); - if (sscanf(__nbuf, "%Lf", &__v) != 1) + if (sscanf(__nc_in, "%Lf", &__v) != 1) std::__throw_runtime_error("money_get error"); } if (__b == __e) diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp new file mode 100644 index 0000000..8fe74cd --- /dev/null +++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// <locale> + +// class money_get<charT, InputIterator> + +// iter_type get(iter_type b, iter_type e, bool intl, ios_base& iob, +// ios_base::iostate& err, long double& v) const; + +// Ensure that money_get::do_get correct works when the input doesn't fit into the stack buffer +// (100 characters currently). + +// XFAIL: FROZEN-CXX03-HEADERS-FIXME + +#include <cassert> +#include <cstddef> +#include <ios> +#include <locale> +#include <streambuf> +#include <string> + +#include "make_string.h" +#include "test_macros.h" +#include "test_iterators.h" + +template <class CharT> +class my_basic_facet : public std::money_get<CharT, cpp17_input_iterator<const CharT*> > { +private: + typedef std::money_get<CharT, cpp17_input_iterator<const CharT*> > Base; + +public: + explicit my_basic_facet(std::size_t refs = 0) : Base(refs) {} +}; + +template <class CharT> +void test() { + struct digit_result_case { + std::size_t digit; + long double result; + }; + const digit_result_case digit_result_cases[] = { + {60, 2.0E60L}, {120, 2.0E120L}, {180, 2.0E180L}, {240, 2.0E240L}, {300, 2.0E300L}}; + + std::ios ios(0); + { + const my_basic_facet<CharT> f(1); + for (std::size_t i = 0; i != sizeof(digit_result_cases) / sizeof(digit_result_cases[0]); ++i) { + { + std::basic_string<CharT> v = MAKE_STRING(CharT, "2"); + v.append(digit_result_cases[i].digit, static_cast<CharT>('0')); + + typedef cpp17_input_iterator<const CharT*> I; + long double ex; + std::ios_base::iostate err = std::ios_base::goodbit; + I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex); + assert(base(iter) == v.data() + v.size()); + assert(err == std::ios_base::eofbit); + assert(ex == digit_result_cases[i].result); + } + { + std::basic_string<CharT> v = MAKE_STRING(CharT, "-2"); + v.append(digit_result_cases[i].digit, static_cast<CharT>('0')); + + typedef cpp17_input_iterator<const CharT*> I; + long double ex; + std::ios_base::iostate err = std::ios_base::goodbit; + I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex); + assert(base(iter) == v.data() + v.size()); + assert(err == std::ios_base::eofbit); + assert(ex == -digit_result_cases[i].result); + } + { + std::basic_string<CharT> v = MAKE_STRING(CharT, "0."); + v.append(digit_result_cases[i].digit, static_cast<CharT>('0')); + v += MAKE_CSTRING(CharT, "2"); + + typedef cpp17_input_iterator<const CharT*> I; + long double ex; + std::ios_base::iostate err = std::ios_base::goodbit; + I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex); + assert(base(iter) == v.data() + 1); + assert(err == std::ios_base::goodbit); + assert(ex == 0.0L); + } + { + std::basic_string<CharT> v = MAKE_STRING(CharT, "-0."); + v.append(digit_result_cases[i].digit, static_cast<CharT>('0')); + v += MAKE_CSTRING(CharT, "2"); + + typedef cpp17_input_iterator<const CharT*> I; + long double ex; + std::ios_base::iostate err = std::ios_base::goodbit; + I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex); + assert(base(iter) == v.data() + 2); + assert(err == std::ios_base::goodbit); + assert(ex == 0.0L); + } + } + } +} + +int main(int, char**) { + test<char>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test<wchar_t>(); +#endif + + return 0; +} |