<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/57964>57964</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            libc++ 15.0.1 segfaults in `std::__1::ios_base::~ios_base()` when object was not yet properly constructed yet
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          marv
      </td>
    </tr>
</table>

<pre>
    When building a project what works perfectly fine with GCC/libstdc++ with Clang/libc++ I'm getting a segfault in `std::__1::ios_base::~ios_base()`. I've boiled the code that causes the crash down to this reproducer:
```
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <filesystem>
#include <iostream>
#include <istream>
#include <sys/stat.h>
#include <sys/types.h>
#include <system_error>
#include <unistd.h>

namespace fs = std::filesystem;

class SafeIFStreamBuf : public std::streambuf {
public:
  SafeIFStreamBuf(const int fd);

  int fd;

protected:
  static const int lookbehind_size = 16;
  static const int buffer_size = 512 + lookbehind_size;
  char buffer[buffer_size];
};

class SafeIFStreamBase {
protected:
  SafeIFStreamBuf buf;

public:
  SafeIFStreamBase(const int fd);
};

class SafeIFStream : protected SafeIFStreamBase, public std::istream {
private:
  const bool _close;

public:
  explicit SafeIFStream(const int fd);
  explicit SafeIFStream(const fs::path &);
  ~SafeIFStream() override;
};

SafeIFStreamBuf::SafeIFStreamBuf(const int f) : fd(f) {
  int n_read =
      read(fd, buffer + lookbehind_size, buffer_size - lookbehind_size);
  if (-1 == n_read)
    throw std::system_error(errno, std::generic_category(),
                            "Error reading from fd " + std::to_string(fd));
}

SafeIFStreamBase::SafeIFStreamBase(const int f) : buf(f) {}

SafeIFStream::SafeIFStream(const int f)
    : SafeIFStreamBase(f), std::istream(&buf), _close(false) {}

namespace {
int open_path(const fs::path &e) {
  int result = open(e.string().c_str(), O_RDONLY | O_CLOEXEC);
  if (-1 == result)
    throw std::system_error(errno, std::generic_category(),
                            "Could not open '" + e.string() + "'");

  return result;
}
} // namespace

SafeIFStream::SafeIFStream(const fs::path &e)
    : SafeIFStreamBase(open_path(e)), std::istream(&buf), _close(true) {}

SafeIFStream::~SafeIFStream() {
  if (_close)
    ::close(buf.fd);
}

int main() {
  fs::path test_dir = fs::current_path() / "test_directory";
  fs::create_directory(test_dir);

  try {
    SafeIFStream ifs(test_dir);
    std::string t;
    ifs >> t;
  } catch (const std::system_error &ex) {
    std::cout << "expected exception thrown" << std::endl;
    return 0;
  }

  std::cout << "expected exception not thrown" << std::endl;
  return 1;
}
```

Building with Clang/libc++ 15.0.1 vs. GCC/libstdc++ 12.2.0:
```
$ clang++-15 -std=c++17 -stdlib=libc++ -o reproducer reproducer.cc 
$ ./reproducer 
Segmentation fault (core dumped)
$ g++-12 -std=c++17 -o reproducer_gcc reproducer.cc 
$ ./reproducer_gcc 
expected exception thrown
```

The important part is the inheritance from `std::istream` and the exception that's thrown during the construction of the `SafeIFStreamBuf` class. In this situation `ios_base`'s member variables are not properly initialized and `~ios_base` tries to emit its callbacks. But since `__event_size_` contains garbage it segfaults trying to access `__fn_[i]`

The following patch fixes the crash:
```
diff --git a/libcxx/include/ios b/libcxx/include/ios
index 7140e00b406a..b1a796418ba3 100644
--- a/libcxx/include/ios
+++ b/libcxx/include/ios
@@ -350,8 +350,11 @@ public:
 
 protected:
     _LIBCPP_INLINE_VISIBILITY
-    ios_base() {// purposefully does no initialization
-               }
+    ios_base() :
+        __event_size_{0}
+    {
+        // purposefully does no initialization
+    }
 
     void init(void* __sb);
     _LIBCPP_INLINE_VISIBILITY void* rdbuf() const {return __rdbuf_;}
@@ -381,12 +384,12 @@ private:
     streamsize      __width_;
     iostate         __rdstate_;
     iostate         __exceptions_;
-    void*           __rdbuf_;
-    void*           __loc_;
-    event_callback* __fn_;
-    int*            __index_;
-    size_t          __event_size_;
-    size_t          __event_cap_;
+    void*           __rdbuf_ = nullptr;
+    void*           __loc_ = nullptr;
+    event_callback* __fn_ = nullptr;
+    int*            __index_ = nullptr;
+    size_t          __event_size_ = 0;
+    size_t          __event_cap_ = 0;
 // TODO(EricWF): Enable this for both Clang and GCC. Currently it is only
 // enabled with clang.
 #if defined(_LIBCPP_HAS_C_ATOMIC_IMP) && !defined(_LIBCPP_HAS_NO_THREADS)
@@ -394,12 +397,12 @@ private:
 #else
     static int      __xindex_;
 #endif
-    long*           __iarray_;
-    size_t          __iarray_size_;
-    size_t          __iarray_cap_;
-    void**          __parray_;
-    size_t          __parray_size_;
-    size_t          __parray_cap_;
+    long*           __iarray_ = nullptr;
+    size_t          __iarray_size_ = 0;
+    size_t          __iarray_cap_ = 0;
+    void**          __parray_ = nullptr;
+    size_t          __parray_size_ = 0;
+    size_t          __parray_cap_ = 0;
 };
 
 //enum class io_errc
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 218b27f1a6b5..40ddbe53052d 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -233,8 +233,10 @@ ios_base::register_callback(event_callback fn, int index)
 ios_base::~ios_base()
 {
     __call_callbacks(erase_event);
-    locale& loc_storage = *reinterpret_cast<locale*>(&__loc_);
-    loc_storage.~locale();
+    if (__loc_ != nullptr) {
+        locale& loc_storage = *reinterpret_cast<locale*>(&__loc_);
+        loc_storage.~locale();
+    }
     free(__fn_);
     free(__index_);
     free(__iarray_);

```

The first hunk is a left-over, because I started with an initializer list but noticed that more variables than `__event_size_` needed to be initialized and switched to default-member-initialization. This is more of an FYI/RFC instead of a real attempt to fix it, because I'm not familiar with the code at all.

In libstdc++'s code the `ios_base` constructor mentions the need for proper initialization of the member variables:
```
  ios_base::ios_base() throw()
  : _M_precision(), _M_width(), _M_flags(), _M_exception(),
  _M_streambuf_state(), _M_callbacks(0), _M_word_zero(),
  _M_word_size(_S_local_word_size), _M_word(_M_local_word), _M_ios_locale()
  {
    // Do nothing: basic_ios::init() does it.
    // NB: _M_callbacks and _M_word must be zero for non-initialized
    // ios_base to go through ~ios_base gracefully.
  }
``` 

Best regards,
Marvin
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzFGltvozz216QvVhCQ-0MfmqTdL9LMdDSt9tt5QgZM4h2CkW162V-_59hATAJpZ6TVVqhN7XOOz_1iEov0_fbvAytIXPE85cWeUFJK8W-WaPJ6oPBLyF-KlExmsJS_k4wXjLxyfSD_2GxG4UPOY6XTZBSu4bEbm5wWe7vVrO9G4eJI9kxre4Ri-4xWuSa8IKO5DxRGkzt4oiiwH7hQUUwVs_-NFvftQrgchSvA8QzRF0ZiwXOWEn1gJBEpgw_AdkIrxZRdlFQdSCpeC6IFrHBFJAMZ0yphEon725F_BxTrx_4bTniR5BXQG002ACgLMZrc924qLUGqgd0sKXTuHYZ2gXX1rjQ7DgCA2FoyOrh9dRcogxmUpnqQAQui30umrsEAgxHoQMgBkKoATlKXgvld0CNTJU0YyRSAbUlraFfwtYuS5FQp8kQztnt4MsKtqwxw70hZxTlPTiSs6DHuLmoSFqS1KTmnA86TiEKh22mSpehI3dNJs9NdBm_R4P4sdUijWoGdE71ciF8xO_AijRT_DzPyBvOWUg8G8J4xeYKeBSHBaDmj5JBIDlTWaKPZ2sEfzbYnnhfbj5UKoeTorUe-cxPAYedauaptG6uD6v4Ej9bqDWs91DcXPlHHgysZf6GaOUxajmIhchIluVDsI6nYWwkLXHcYuCLaRxiZsryWFHLlKJx3cSHVnWHBPhEvEH08ZVf0d-7q5oyr_o-EUcXI_tL-t1i7cVBEgJeiazar-INriJCiAawL9nptu2sdfHwJ4MrNIYzD5Tgwp0Es2LMRpj1aH6R4deLfTUvh0qZoOLQF2LOCSZ5ECTjAXsj3unSEG1ea_p9RGN4jXSMslqxMiiMoCjeMsO0hWkR1_rcqWZ17-ZCJ2uJ2PWoaI8XGeq2VhglfEr0geJIfKfecn1k9XcSV0eDcsGL26wACBJrj3z7eTjWg9S7kQ5SsiDAEBuOC9XikZAq7BnQQJIBm91r1A7yXoDVaQ5PH6Mf28duXn0AH_9l8ebz_1_3muufZM_6PnrcRVZ6SQlglwcqi8bqusGYJtixAXzGTTFeyaCS69MrFFvAf4CGtlf7ArfoM96GLufZnddj8jsdpWQ043CXX_TnVdS3jAg3tLvPwNGcCG95lGXOORhc9Ul5cntDRkWZKRymXxo-bnaSSkhW60Ym17wPatwGHMmh8KXSct8UGwTRzoZYNWp9jaPnuctct3aAONYCPoG4DhqlRd3a56fTu4elsoKtBNCToILXb9IaT8Z-3M-U5ZyaiwvDfwIOqgUprmwP2lrBSc1HYWC1MxFiwFpcVad7htQ4Pv8tnV1O_cTJG7KdPr88OelzpbBIxv9fNeDY0YQUzz_cC8qK83sEsCL3Q84ennSlJLE2EHgczMjaMb2v8YGEWgCasOaeOhTNLOR-9JCEn0h6w44DVYcr2R_B3alRnh0HjGpKRtDqW7FT7kUTLWdjDmctEtIejP8eIAbWbw340bJJnmC35sRRSUwj7kkoosHbi5MUB0j8s4-CDjYM74jaJbe4TWtix1T2UQuVZqPp00IQNMTPbQtDIKjFwIjNrQOS8wwOyppOG8biww67iurJahs12jAZZ8JwjO8ZgkhcqOY1hJCMU9I-ODEqCoR8Gfl5wzWkODVtqGEZMZxyH8yAN4KgtCDtCz8u1gkjP85gmv4CLNUSN4qgJAI0i9oI5Dtu_yLAqwAN4ocieypjuQXW6vRtQmKWM9ILQJGEwHRgSWRHB7MNx4rk0SCbyXLwiVmmyTcbf3GuAoQBIeZaR8XgPx9M6rt4gCz3UIy5-EorEg1tN-k_ZG1kEU5_5fjz159Tz4oAuVvNpsIzphAS-P59OLfB4PL5yVuOz6zrQPjp6NPXhIePJDOy6WWJrYD8G0NfYvfPBpv7TN_vBT_Rlt958_x7tvn3ZfbuP_rl72q13X3bPP2vuTa7v3MmYjG27ibKSJZTMrMrBgVIBFijEyZGMMzpk3O6nTYIgc88JrfnstmG041OLtX9Ooy0kDtLv8tmS2naVhz8vgqcGCZjEz6PwDphS8XndHFYpadBkatt8lNUWSWC-rhRRZHYjpNlK2Fh9GaCpTZM4WU7rz7XZLyZgU08xX5i5rFbiK0_1IeoyjJdPgNpaB1kwKx_CtRlNnUDHjbJQUtIh2sh1HS4XyRmUtXyTbaziMT10gKAp61ICIBOpZ3DGgbQL5TrWp0ATWp4ga58Zlti0fwX4XgkjyyewUP5rOAPauIYyrJtrWFc1ZRD9z6Kgxs4wmuB8ftw-QizcwyD194MJpjtyX2CVsnUtg24xFk03ZGoTdD4e2dhGGkuXqciiyN-7lJmhktpeyvQ9XgswgVkgZXjLjZccTcj-dfcUbaK758evu020-_rddudzeOBP0A__7TF6_uvH_d326dTJNPG6mp7idbW4Hq_AE8Pp2o1ec5GI00atzLczhzZIBVQ1x2lzgf1d16U4lZK-f-jdNdinIqGG7YSCG9IdFiIYdz7FQfkbHJQ9HNRueEUHv-fwrkY-6_GOZvpRrmro9_gr_4C_cpA_4l43kk4wsaI62o4TygDOcMn1jkoBgOlbvKQsu21NZ8vtqELonsJFFtB5PPO8qZ-mMZtN_FmYXu-oeggOdFR9kHWshpNJ01HZj4HfhGr3JZVke-jtYaw4ZeBlNyWTrMA7DIxbI9jpquGj112NFTrthKHbElfmJgpQbGZ1u486_AGSYcbCOqK0kNhvo53B3yQDppgsodcAggom900Df4fTvLmFqStwD-GGnAecN3jLzr1DXW7sPUtdycLAdWl35Hc6tf8F213yn2X-1PzhTyYZM7Jgv3He6bWbdVoe3q-z38UVzeDEmXEJbeGhKn5hbaMkZ5ke4-sBc9_OzHtPssMSIXVT4mjhTHGS5BxfgMBcBnMeT8yrU6rJESfv0xwIa0XvyFYwliKOgNMuhkMF5yUHuw1VEce4sR0xx9222iPPWMPhMefCPAvHPfzcQRT-eMAQgUiiqVnHO_icUK3ZsdRIGGY6gt22I695vYxja0aPPAetWsHbd8IgH4SJ5yoTxuPOFYkZh-sXyOxsVD4N39Bz4K0FdrYGDrVhOhE7L58ND82cfj5lD42h5CwRnI0_5lKgkxHM7Wr0NYIISLjCcaW9-oZV09N3VrKc7lVnpe3UL-6oYbN9xRqZBr-D6OYd3zlTyDQCLxN99MymffezjJ4iE23uoksEQb46IKdd1EonUNu7OyfG6lZvK9AtDub9_B0BXfIE8Wv12qENVWuGP669CwLf1rWKW3mNo9dMkmOFscQISmz8oBDF2ImKC3qNSdGR98KYtNofiJPyyV7SxM6k3km088tB4vrymil8N7KnMJ-1Gv9K5QsvbthtMJ9P_dVyufRv0ttJupqs6I3mOme3l_eHp8uXP_9mBnnFL5SI2H6JhCoTmO_MuVNqwwmCBzZuKpnfHrQujV2MnqBvOFSxl4gjlun8pfkzrr-cgtVaqYrhtxdmeMFyc7hNkkW6WITLCZ2vFpTBcuwvp8vFhKZp4i_im5zG0Enfjmbr0Wx7w29DPwz9VTjzV9MgWHkLFiR-4AezNJ2tAn8BJZ4dKc89PNgTcn8jbw0PcQVBNPUxj6rTJrRAfA_5oKFPK30Q8vYIZrgxvN4aRv8LkX9btw">