Skip to content

Commit 61775dc

Browse files
[libc++] Fix money_get::do_get with huge input
`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. The fix should be backported to frozen cxx03 headers as the previously wrong handling caused core language UB.
1 parent b57e63b commit 61775dc

File tree

3 files changed

+127
-6
lines changed

3 files changed

+127
-6
lines changed

libcxx/include/__cxx03/locale

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,8 @@ _LIBCPP_HIDE_FROM_ABI void __double_or_nothing(unique_ptr<_Tp, void (*)(void*)>&
24602460
__throw_bad_alloc();
24612461
if (__owns)
24622462
__b.release();
2463+
else
2464+
std::memcpy(__t, __b.get(), __cur_cap);
24632465
__b = unique_ptr<_Tp, void (*)(void*)>(__t, free);
24642466
__new_cap /= sizeof(_Tp);
24652467
__n = __b.get() + __n_off;
@@ -2655,20 +2657,22 @@ _InputIterator money_get<_CharT, _InputIterator>::do_get(
26552657
char_type __atoms[sizeof(__src) - 1];
26562658
__ct.widen(__src, __src + (sizeof(__src) - 1), __atoms);
26572659
char __nbuf[__bz];
2658-
char* __nc = __nbuf;
2660+
char* __nc = __nbuf;
2661+
const char* __nc_in = __nc;
26592662
unique_ptr<char, void (*)(void*)> __h(nullptr, free);
26602663
if (__wn - __wb.get() > __bz - 2) {
26612664
__h.reset((char*)malloc(static_cast<size_t>(__wn - __wb.get() + 2)));
26622665
if (__h.get() == nullptr)
26632666
__throw_bad_alloc();
2664-
__nc = __h.get();
2667+
__nc = __h.get();
2668+
__nc_in = __nc;
26652669
}
26662670
if (__neg)
26672671
*__nc++ = '-';
26682672
for (const char_type* __w = __wb.get(); __w < __wn; ++__w, ++__nc)
26692673
*__nc = __src[std::find(__atoms, std::end(__atoms), *__w) - __atoms];
26702674
*__nc = char();
2671-
if (sscanf(__nbuf, "%Lf", &__v) != 1)
2675+
if (sscanf(__nc_in, "%Lf", &__v) != 1)
26722676
__throw_runtime_error("money_get error");
26732677
}
26742678
if (__b == __e)

libcxx/include/locale

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,8 @@ _LIBCPP_HIDE_FROM_ABI void __double_or_nothing(unique_ptr<_Tp, void (*)(void*)>&
23852385
std::__throw_bad_alloc();
23862386
if (__owns)
23872387
__b.release();
2388+
else
2389+
std::memcpy(__t, __b.get(), __cur_cap);
23882390
__b = unique_ptr<_Tp, void (*)(void*)>(__t, free);
23892391
__new_cap /= sizeof(_Tp);
23902392
__n = __b.get() + __n_off;
@@ -2580,20 +2582,22 @@ _InputIterator money_get<_CharT, _InputIterator>::do_get(
25802582
char_type __atoms[sizeof(__src) - 1];
25812583
__ct.widen(__src, __src + (sizeof(__src) - 1), __atoms);
25822584
char __nbuf[__bz];
2583-
char* __nc = __nbuf;
2585+
char* __nc = __nbuf;
2586+
const char* __nc_in = __nc;
25842587
unique_ptr<char, void (*)(void*)> __h(nullptr, free);
25852588
if (__wn - __wb.get() > __bz - 2) {
25862589
__h.reset((char*)malloc(static_cast<size_t>(__wn - __wb.get() + 2)));
25872590
if (__h.get() == nullptr)
25882591
std::__throw_bad_alloc();
2589-
__nc = __h.get();
2592+
__nc = __h.get();
2593+
__nc_in = __nc;
25902594
}
25912595
if (__neg)
25922596
*__nc++ = '-';
25932597
for (const char_type* __w = __wb.get(); __w < __wn; ++__w, ++__nc)
25942598
*__nc = __src[std::find(__atoms, std::end(__atoms), *__w) - __atoms];
25952599
*__nc = char();
2596-
if (sscanf(__nbuf, "%Lf", &__v) != 1)
2600+
if (sscanf(__nc_in, "%Lf", &__v) != 1)
25972601
std::__throw_runtime_error("money_get error");
25982602
}
25992603
if (__b == __e)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
#include <cassert>
17+
#include <cstddef>
18+
#include <ios>
19+
#include <locale>
20+
#include <streambuf>
21+
#include <string>
22+
23+
#include "test_macros.h"
24+
#include "test_iterators.h"
25+
26+
typedef std::money_get<char, cpp17_input_iterator<const char*> > Fn;
27+
28+
class my_facet : public Fn {
29+
public:
30+
explicit my_facet(std::size_t refs = 0) : Fn(refs) {}
31+
};
32+
33+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
34+
typedef std::money_get<wchar_t, cpp17_input_iterator<const wchar_t*> > Fw;
35+
36+
class my_facetw : public Fw {
37+
public:
38+
explicit my_facetw(std::size_t refs = 0) : Fw(refs) {}
39+
};
40+
#endif
41+
42+
int main(int, char**) {
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_facet f(1);
53+
for (std::size_t i = 0; i != sizeof(digit_result_cases) / sizeof(digit_result_cases[0]); ++i) {
54+
{
55+
std::string v = "2";
56+
v.append(digit_result_cases[i].digit, '0');
57+
58+
typedef cpp17_input_iterator<const char*> 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::string v = "-2";
68+
v.append(digit_result_cases[i].digit, '0');
69+
70+
typedef cpp17_input_iterator<const char*> 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+
}
80+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
81+
{
82+
const my_facetw f(1);
83+
for (std::size_t i = 0; i != sizeof(digit_result_cases) / sizeof(digit_result_cases[0]); ++i) {
84+
{
85+
std::wstring v = L"2";
86+
v.append(digit_result_cases[i].digit, L'0');
87+
88+
typedef cpp17_input_iterator<const wchar_t*> I;
89+
long double ex;
90+
std::ios_base::iostate err = std::ios_base::goodbit;
91+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
92+
assert(base(iter) == v.data() + v.size());
93+
assert(err == std::ios_base::eofbit);
94+
assert(ex == digit_result_cases[i].result);
95+
}
96+
{
97+
std::wstring v = L"-2";
98+
v.append(digit_result_cases[i].digit, L'0');
99+
100+
typedef cpp17_input_iterator<const wchar_t*> I;
101+
long double ex;
102+
std::ios_base::iostate err = std::ios_base::goodbit;
103+
I iter = f.get(I(v.data()), I(v.data() + v.size()), false, ios, err, ex);
104+
assert(base(iter) == v.data() + v.size());
105+
assert(err == std::ios_base::eofbit);
106+
assert(ex == -digit_result_cases[i].result);
107+
}
108+
}
109+
}
110+
#endif
111+
112+
return 0;
113+
}

0 commit comments

Comments
 (0)