Skip to content

Commit ca0612c

Browse files
[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.
1 parent 6048629 commit ca0612c

File tree

2 files changed

+122
-3
lines changed

2 files changed

+122
-3
lines changed

libcxx/include/locale

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,6 +2386,8 @@ _LIBCPP_HIDE_FROM_ABI void __double_or_nothing(unique_ptr<_Tp, void (*)(void*)>&
23862386
std::__throw_bad_alloc();
23872387
if (__owns)
23882388
__b.release();
2389+
else
2390+
std::memcpy(__t, __b.get(), __cur_cap);
23892391
__b = unique_ptr<_Tp, void (*)(void*)>(__t, free);
23902392
__new_cap /= sizeof(_Tp);
23912393
__n = __b.get() + __n_off;
@@ -2581,20 +2583,22 @@ _InputIterator money_get<_CharT, _InputIterator>::do_get(
25812583
char_type __atoms[sizeof(__src) - 1];
25822584
__ct.widen(__src, __src + (sizeof(__src) - 1), __atoms);
25832585
char __nbuf[__bz];
2584-
char* __nc = __nbuf;
2586+
char* __nc = __nbuf;
2587+
const char* __nc_in = __nc;
25852588
unique_ptr<char, void (*)(void*)> __h(nullptr, free);
25862589
if (__wn - __wb.get() > __bz - 2) {
25872590
__h.reset((char*)malloc(static_cast<size_t>(__wn - __wb.get() + 2)));
25882591
if (__h.get() == nullptr)
25892592
std::__throw_bad_alloc();
2590-
__nc = __h.get();
2593+
__nc = __h.get();
2594+
__nc_in = __nc;
25912595
}
25922596
if (__neg)
25932597
*__nc++ = '-';
25942598
for (const char_type* __w = __wb.get(); __w < __wn; ++__w, ++__nc)
25952599
*__nc = __src[std::find(__atoms, std::end(__atoms), *__w) - __atoms];
25962600
*__nc = char();
2597-
if (sscanf(__nbuf, "%Lf", &__v) != 1)
2601+
if (sscanf(__nc_in, "%Lf", &__v) != 1)
25982602
std::__throw_runtime_error("money_get error");
25992603
}
26002604
if (__b == __e)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// <locale>
10+
11+
// class money_get<charT, InputIterator>
12+
13+
// iter_type get(iter_type b, iter_type e, bool intl, ios_base& iob,
14+
// ios_base::iostate& err, long double& v) const;
15+
16+
// Ensure that money_get::do_get correct works when the input doesn't fit into the stack buffer
17+
// (100 characters currently).
18+
19+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
20+
21+
#include <cassert>
22+
#include <cstddef>
23+
#include <ios>
24+
#include <locale>
25+
#include <streambuf>
26+
#include <string>
27+
28+
#include "make_string.h"
29+
#include "test_macros.h"
30+
#include "test_iterators.h"
31+
32+
template <class CharT>
33+
class my_basic_facet : public std::money_get<CharT, cpp17_input_iterator<const CharT*> > {
34+
private:
35+
typedef std::money_get<CharT, cpp17_input_iterator<const CharT*> > Base;
36+
37+
public:
38+
explicit my_basic_facet(std::size_t refs = 0) : Base(refs) {}
39+
};
40+
41+
template <class CharT>
42+
void test() {
43+
struct digit_result_case {
44+
std::size_t digit;
45+
long double result;
46+
};
47+
const digit_result_case digit_result_cases[] = {
48+
{60, 2.0E60L}, {120, 2.0E120L}, {180, 2.0E180L}, {240, 2.0E240L}, {300, 2.0E300L}};
49+
50+
std::ios ios(0);
51+
{
52+
const my_basic_facet<CharT> f(1);
53+
for (std::size_t i = 0; i != sizeof(digit_result_cases) / sizeof(digit_result_cases[0]); ++i) {
54+
{
55+
std::basic_string<CharT> v = MAKE_STRING(CharT, "2");
56+
v.append(digit_result_cases[i].digit, static_cast<CharT>('0'));
57+
58+
typedef cpp17_input_iterator<const CharT*> I;
59+
long double ex;
60+
std::ios_base::iostate err = std::ios_base::goodbit;
61+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
62+
assert(base(iter) == v.data() + v.size());
63+
assert(err == std::ios_base::eofbit);
64+
assert(ex == digit_result_cases[i].result);
65+
}
66+
{
67+
std::basic_string<CharT> v = MAKE_STRING(CharT, "-2");
68+
v.append(digit_result_cases[i].digit, static_cast<CharT>('0'));
69+
70+
typedef cpp17_input_iterator<const CharT*> I;
71+
long double ex;
72+
std::ios_base::iostate err = std::ios_base::goodbit;
73+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
74+
assert(base(iter) == v.data() + v.size());
75+
assert(err == std::ios_base::eofbit);
76+
assert(ex == -digit_result_cases[i].result);
77+
}
78+
{
79+
std::basic_string<CharT> v = MAKE_STRING(CharT, "0.");
80+
v.append(digit_result_cases[i].digit, static_cast<CharT>('0'));
81+
v += MAKE_CSTRING(CharT, "2");
82+
83+
typedef cpp17_input_iterator<const CharT*> I;
84+
long double ex;
85+
std::ios_base::iostate err = std::ios_base::goodbit;
86+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
87+
assert(base(iter) == v.data() + 1);
88+
assert(err == std::ios_base::goodbit);
89+
assert(ex == 0.0L);
90+
}
91+
{
92+
std::basic_string<CharT> v = MAKE_STRING(CharT, "-0.");
93+
v.append(digit_result_cases[i].digit, static_cast<CharT>('0'));
94+
v += MAKE_CSTRING(CharT, "2");
95+
96+
typedef cpp17_input_iterator<const CharT*> I;
97+
long double ex;
98+
std::ios_base::iostate err = std::ios_base::goodbit;
99+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
100+
assert(base(iter) == v.data() + 2);
101+
assert(err == std::ios_base::goodbit);
102+
assert(ex == 0.0L);
103+
}
104+
}
105+
}
106+
}
107+
108+
int main(int, char**) {
109+
test<char>();
110+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
111+
test<wchar_t>();
112+
#endif
113+
114+
return 0;
115+
}

0 commit comments

Comments
 (0)