diff --git a/libstdc++-v3/include/bits/ostream.h b/libstdc++-v3/include/bits/ostream.h
index 8ee63d2d66e54657c7c440bc3afb8147226761e1..d19a76ab2474c774fb0dc2a1ff85f5cb0b68c3d7 100644
--- a/libstdc++-v3/include/bits/ostream.h
+++ b/libstdc++-v3/include/bits/ostream.h
@@ -507,6 +507,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return __d;
 	}
 #pragma GCC diagnostic pop
+
+      // RAII type to clear and restore an ostream's exceptions mask.
+      struct _Disable_exceptions
+      {
+	_Disable_exceptions(basic_ostream& __os)
+	: _M_os(__os), _M_exception(_M_os._M_exception)
+	{ _M_os._M_exception = ios_base::goodbit; }
+
+	~_Disable_exceptions()
+	{ _M_os._M_exception = _M_exception; }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++11-extensions" // deleted functions
+	_Disable_exceptions(const _Disable_exceptions&) = delete;
+	_Disable_exceptions& operator=(const _Disable_exceptions&) = delete;
+#pragma GCC diagnostic pop
+
+      private:
+	basic_ostream& _M_os;
+	const ios_base::iostate _M_exception;
+      };
     };
 
   /**
@@ -543,18 +564,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /**
        *  @brief  Possibly flushes the stream.
        *
-       *  If @c ios_base::unitbuf is set in @c os.flags(), and
-       *  @c std::uncaught_exception() is true, the sentry destructor calls
-       *  @c flush() on the output stream.
+       *  If `ios_base::unitbuf` is set in `os.flags()`, and
+       *  `std::uncaught_exception()` is true, the sentry destructor flushes
+       *  the output stream.
       */
       ~sentry()
       {
-	// XXX MT
-	if (bool(_M_os.flags() & ios_base::unitbuf) && !uncaught_exception())
+	// _GLIBCXX_RESOLVE_LIB_DEFECTS
+	// 397. ostream::sentry dtor throws exceptions
+	// 835. Tying two streams together (correction to DR 581)
+	// 4188. ostream::sentry destructor should handle exceptions
+	if (bool(_M_os.flags() & ios_base::unitbuf) && _M_os.good()
+	      && !uncaught_exception()) // XXX MT
 	  {
-	    // Can't call flush directly or else will get into recursive lock.
-	    if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1)
-	      _M_os.setstate(ios_base::badbit);
+	    _Disable_exceptions __noex(_M_os);
+	    __try
+	      {
+		// Can't call _M_os.flush() directly because that constructs
+		// another sentry.
+		if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1)
+		  _M_os.setstate(ios_base::badbit);
+	      }
+	    __catch(...)
+	      { _M_os.setstate(ios_base::badbit); }
 	  }
       }
 #pragma GCC diagnostic pop
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4a7326665368134d8c7a996e6b49a8a64900cc76
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc
@@ -0,0 +1,50 @@
+// { dg-do run }
+
+// 4188. ostream::sentry destructor should handle exceptions
+
+#include <ostream>
+#include <streambuf>
+#include <testsuite_hooks.h>
+
+struct bad_streambuf : std::streambuf
+{
+  int sync() { return -1; }
+};
+
+void
+test_returns_error()
+{
+  bad_streambuf buf;
+  std::ostream out(&buf);
+  out.setf(std::ios::unitbuf);
+  out.exceptions(std::ios::badbit);
+  out.write("", 0); // constructs sentry
+  VERIFY( out.bad() );
+}
+
+struct exceptionally_bad_streambuf : std::streambuf
+{
+  int sync() { throw std::ios::failure("unsyncable"); }
+};
+
+void
+test_throws()
+{
+  exceptionally_bad_streambuf buf;
+  std::ostream out(&buf);
+  out.setf(std::ios::unitbuf);
+  out.write("", 0); // constructs sentry
+  VERIFY( out.bad() );
+
+  // Repeat with badbit in exceptions mask
+  out.clear();
+  out.exceptions(std::ios::badbit);
+  out.write("", 0); // constructs sentry
+  VERIFY( out.bad() );
+}
+
+int main()
+{
+  test_returns_error();
+  test_throws();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a3354e131dc597480b319db6e12329383904c612
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc
@@ -0,0 +1,50 @@
+// { dg-do run }
+
+// 4188. ostream::sentry destructor should handle exceptions
+
+#include <ostream>
+#include <streambuf>
+#include <testsuite_hooks.h>
+
+struct bad_streambuf : std::wstreambuf
+{
+  int sync() { return -1; }
+};
+
+void
+test_returns_error()
+{
+  bad_streambuf buf;
+  std::wostream out(&buf);
+  out.setf(std::wios::unitbuf);
+  out.exceptions(std::wios::badbit);
+  out.write(L"", 0); // constructs sentry
+  VERIFY( out.bad() );
+}
+
+struct exceptionally_bad_streambuf : std::wstreambuf
+{
+  int sync() { throw std::wios::failure("unsyncable"); }
+};
+
+void
+test_throws()
+{
+  exceptionally_bad_streambuf buf;
+  std::wostream out(&buf);
+  out.setf(std::wios::unitbuf);
+  out.write(L"", 0); // constructs sentry
+  VERIFY( out.bad() );
+
+  // Repeat with badbit in exceptions mask
+  out.clear();
+  out.exceptions(std::wios::badbit);
+  out.write(L"", 0); // constructs sentry
+  VERIFY( out.bad() );
+}
+
+int main()
+{
+  test_returns_error();
+  test_throws();
+}