Skip to content

Commit c6dae09

Browse files
ldionneMarkMurrayARM
authored andcommitted
Automerge: [libc++] Ensure that we restore invariants in basic_filebuf::overflow (#147389)
In rare circumstances, the invariants could fail to be restored. (cherry picked from commit 6291b63)
2 parents 660ab72 + b7c18c1 commit c6dae09

File tree

2 files changed

+96
-11
lines changed

2 files changed

+96
-11
lines changed

libcxx/include/fstream

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,14 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
822822

823823
template <class _CharT, class _Traits>
824824
typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::overflow(int_type __c) {
825+
auto __failed = [this]() {
826+
if (this->pptr() == this->epptr() + 1) {
827+
this->pbump(-1); // lose the character we overflowed above -- we don't really have a
828+
// choice since we couldn't commit the contents of the put area
829+
}
830+
return traits_type::eof();
831+
};
832+
825833
if (__file_ == nullptr)
826834
return traits_type::eof();
827835
__write_mode();
@@ -842,8 +850,9 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
842850

843851
if (__always_noconv_) {
844852
size_t __n = static_cast<size_t>(this->pptr() - this->pbase());
845-
if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n)
846-
return traits_type::eof();
853+
if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) {
854+
return __failed();
855+
}
847856
} else {
848857
if (!__cv_)
849858
std::__throw_bad_cast();
@@ -855,34 +864,38 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
855864
char* __extbuf_end = __extbuf_;
856865
do {
857866
codecvt_base::result __r = __cv_->out(__st_, __b, __p, __end, __extbuf_, __extbuf_ + __ebs_, __extbuf_end);
858-
if (__end == __b)
859-
return traits_type::eof();
867+
if (__end == __b) {
868+
return __failed();
869+
}
860870

861871
// No conversion needed: output characters directly to the file, done.
862872
if (__r == codecvt_base::noconv) {
863873
size_t __n = static_cast<size_t>(__p - __b);
864-
if (std::fwrite(__b, 1, __n, __file_) != __n)
865-
return traits_type::eof();
874+
if (std::fwrite(__b, 1, __n, __file_) != __n) {
875+
return __failed();
876+
}
866877
break;
867878

868879
// Conversion successful: output the converted characters to the file, done.
869880
} else if (__r == codecvt_base::ok) {
870881
size_t __n = static_cast<size_t>(__extbuf_end - __extbuf_);
871-
if (std::fwrite(__extbuf_, 1, __n, __file_) != __n)
872-
return traits_type::eof();
882+
if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) {
883+
return __failed();
884+
}
873885
break;
874886

875887
// Conversion partially successful: output converted characters to the file and repeat with the
876888
// remaining characters.
877889
} else if (__r == codecvt_base::partial) {
878890
size_t __n = static_cast<size_t>(__extbuf_end - __extbuf_);
879-
if (std::fwrite(__extbuf_, 1, __n, __file_) != __n)
880-
return traits_type::eof();
891+
if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) {
892+
return __failed();
893+
}
881894
__b = const_cast<char_type*>(__end);
882895
continue;
883896

884897
} else {
885-
return traits_type::eof();
898+
return __failed();
886899
}
887900
} while (true);
888901
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
// UNSUPPORTED: no-filesystem
10+
11+
// setrlimit(RLIMIT_FSIZE) seems to only work as intended on Apple platforms
12+
// REQUIRES: target={{.+}}-apple-{{.+}}
13+
14+
// <fstream>
15+
16+
// Make sure that we properly handle the case where we try to write content to a file
17+
// but we fail to do so because std::fwrite fails.
18+
19+
#include <cassert>
20+
#include <csignal>
21+
#include <cstddef>
22+
#include <fstream>
23+
#include <string>
24+
25+
#include "platform_support.h"
26+
#include "test_macros.h"
27+
28+
#if __has_include(<sys/resource.h>)
29+
# include <sys/resource.h>
30+
void limit_file_size_to(std::size_t bytes) {
31+
rlimit lim = {bytes, bytes};
32+
assert(setrlimit(RLIMIT_FSIZE, &lim) == 0);
33+
34+
std::signal(SIGXFSZ, [](int) {}); // ignore SIGXFSZ to ensure std::fwrite fails
35+
}
36+
#else
37+
# error No known way to limit the amount of filesystem space available
38+
#endif
39+
40+
template <class CharT>
41+
void test() {
42+
std::string temp = get_temp_file_name();
43+
std::basic_filebuf<CharT> fbuf;
44+
assert(fbuf.open(temp, std::ios::out | std::ios::trunc));
45+
46+
std::size_t const limit = 100000;
47+
limit_file_size_to(limit);
48+
49+
std::basic_string<CharT> large_block(limit / 10, CharT(42));
50+
51+
std::streamsize ret;
52+
std::size_t bytes_written = 0;
53+
while ((ret = fbuf.sputn(large_block.data(), large_block.size())) != 0) {
54+
bytes_written += ret;
55+
56+
// In theory, it's possible for an implementation to allow writing arbitrarily more bytes than
57+
// set by setrlimit, but in practice if we bust 100x our limit, something else is wrong with the
58+
// test and we'd end up looping forever.
59+
assert(bytes_written < 100 * limit);
60+
}
61+
62+
fbuf.close();
63+
}
64+
65+
int main(int, char**) {
66+
test<char>();
67+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
68+
test<wchar_t>();
69+
#endif
70+
71+
return 0;
72+
}

0 commit comments

Comments
 (0)