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

    <tr>
        <th>Summary</th>
        <td>
            basic_fstream constructor invokes undefined behavior due to "&__sb_"
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

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

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

<pre>
    In libcxx, several of the constructors of `std::basic_fstream` invoke undefined behavior because they pass the address of an unconstructed `std::basic_filebuf` to the constructor of `std::basic_iostream`.  For example, this is default constructor at [fstream:1419](https://github.com/llvm/llvm-project/blob/llvmorg-18.1.6/libcxx/include/fstream#L1419) and a little context:

```C++
  basic_filebuf<char_type, traits_type> __sb_;
};

template <class _CharT, class _Traits>
inline basic_fstream<_CharT, _Traits>::basic_fstream() : basic_iostream<char_type, traits_type>(&__sb_) {}
```

The issue is the subexpression `&__sb_`.  This has type `basic_filebuf*`, but in order to pass it to the constructor, undergoes derived-to-base conversion to `basic_streambuf*`.  But `__sb_` has not yet been constructed, since we're still in the part of the member initializers that constructs base class subobjects.  Consequently, that conversion is prohibited by [class.cdtor p1](https://eel.is/c++draft/class.cdtor#1), which says:

> For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.  [...]

and has an example with these key lines:

```C++
struct X { int i; };
struct Y : X { Y(); }; // non-trivial
struct A { int a; };
struct B : public A { int j; Y y; };    // non-trivial

extern B bobj;
A* pa = &bobj; // undefined behavior: upcast to a base class type
B bobj; // definition of bobj
```

The easy fix is to pass `nullptr` to the base class constructor and then explicitly call `init` in the `basic_fstream` constructor body.

Now, it is my understanding that the standard library implementation is allowed to do things that would not be allowed in user code, so one could claim an exemption from the rule on that basis here.  However, (1) the fix is easy, and (2) rightly or wrongly, people often look to the standard library implementation as a model for how to write their own code, so there is value to setting a good example.

I'm not aware of any practical consequence of this instance of undefined behavior.  I only became aware of it as a result of a question I asked on Stack Overflow, [Is it undefined behavior to pass a pointer to an unconstructed streambuf object to the ostream constructor?](https://stackoverflow.com/questions/78515255/is-it-undefined-behavior-to-pass-a-pointer-to-an-unconstructed-streambuf-object) in which the code in question was cited as evidence that the practice is allowable in user code.

I have not made any attempt to check for this issue in other classes.

The quoted code is in Clang-18.1.6 and in `main` at time of writing.

(For clarity, although I've indicated how I think it should be fixed, I'm not volunteering to do so myself at this time.)

</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJyUV02P47wN_jXKhYiRyPk85DCZ2aADFO2he-ieBrJFx3pHlrySnMT99QUlOx8zed-iwMC7kSXyIcXnIS28V0eDuGPLPVu-TUQXaut2vilbFJ-Twsp-925Aq6K8XBh_BY8ndEKDrSDUCKU1PriuDNZ5WmOrmQ-S5S8sfymEV-VH5YND0bDVDJQ52U-EzkislEEJBdbipKyDAkvReSSTPbTC-2hcSOnQR7vCQGeuzlA-c6Q0Fl1FjoL9Cu45NmWv4DKAg3WAF9G0GinSUCsPyoPESnQ6PFgTAdhyP4aWv8wX8y1bvjG-qUNoPfngB8YPRxXqrshK2zB-0Po0_jNtnf0Dy8D4odC2GFatO07nm2yerWhhSPlBmVJ3Ehk_jP54_vfokG9BGAkCtApBx3gDXgJ5n72x2fhczdLfK-N7-ourAI9Zy1_LWriP0LcpeCdU8Oln_gM-PnzxwfLhLFu_3f4fnwGbVouAQHY03d_Hay3cTzI1_P4ZLbL8RzqhjFYG4bFG8tfbsbsDT6qJbyh6lr_Al5v8yzjisVUKho6v9xTKY5buw_pZIyjvO3rGivJdgZeWilJZQwV1NRcr6CeVTC08kD96_ZhjHr3wVyi6AMqAdRIdFWuseBWe1C3tJr64o0UqRadOKKfBTgvh48YTuogl2Ju_lIqrxwxg3wV6PUKNGI0N0GOAAtHAHbUiy5UpEc7I-Noh-KC0JsAErhUujOxvsCnQgTIqKKHVf9BRmsQdVzwkoLEGfFfYgsreZwCv1nj83aEJuk90S-fGgJSH1tlaFYroXvREuGgmKyVRsJ0_IxyizpRn_FCmYpdOVMSyu5OM53PGt-TzXKuyBi96_5Uz-Y8oB8JAQgxnFWoQYKyZBqdOSuiv1-SwQueUOdJdCNPHvT6IoMoxUaR0t3QMSRwcFFhZh990q8CjMh7wgmUXKC8OfaeDp-v4LqQZUJqyLKPc3MVDMkF3LsyocCmgUKNH-MQeiI3fsvBUORI0-DfRB5QJoFi-h3tNGHb8ivxM-34lxt52Qrqv-4Q-HH65mhd_Yn4fzbddoVV5t_0P2v4L-jtXAH_qLT3xEtAZ2APV59XPC-Mv0Apg-Rswvhrejaa-J5_gdG0pfCSyuL_qqD_R6NXHaCdaUfFqbZVe_rUgofA9VOoSNWnQDraamU7rNri79nfn_6F3GUmvqRJarUoVdA-l0JqMEJLUqaOFm4LdmvhDcVrZZ_f4_mHPRAUVCFzTJ-nyQRgZeUEUjzJKK8JJmiuccD0oKskGDbElcV9obc8oKRZJ4ShzHLTlbDsto3oVeN1GbPDooLQy6r63YA0xifaWWqgm1T42bfRQOdtEKK7TCKSfZJqC9VCjwwzgb_ZMsw5ZY3xDkhEPDJmnW6BXlE3GN5xeO3WsKZvWwdlZc0zC1qIlvtkqoAFt7ed4P_8rC8RXaKxEDZV1UNsznTw7FaJMKAf2bO4jDoScwJ2E7pA2ewyBMi_gaK0cuf9wZe-Mr5uYTnEWDtOw1UPrRBlUOchcFOoSk2TRWGQIfFp4qkLvYI3u41zX4M2yCimqJGLRF_zu0Md430H4T5R0G_8KovyEf57QVTqVFFvu32OPfDI9jiwQ0FplQuqp3ybGa1ccFXe4hmFyeJDz_PCsuXhCZQdQw1A3oqees94s50u-XNLQ5qcqTK9YpyNWat2EdSqmA1ZaEWb6gHV6xTpNWKm6lBn6VWoREmnlmryz8FDGVik84EnJeF9Xxg3XiVdqiULjA2seiwJqccJYFY2QGCtCBJryYtrKGsvPWJTDkBxHJAOWKjBJDvrsq3D97izhS9BjA3vVwowjb2SSikNVI5QhqSHsqomFQ1WvzPHBJuMb6tGlFk6FREYdatsda6CiPhEkqUpBTok871FGPqmIfB2FoYh0TiPPjQcnqzsTEMdeLi2Rq-k96ipiopgJWEYdLUKZyF0ut_lWTHA3X89XyxWfLfJJveNVvprL9ZbzYp7zrZyvq81sVVYc19v5ohATteMzvpgt-WK-WKx5niGulqtqPV8W6-1mK9ZsMcNGKJ3RF0Jm3XES073b5vlsPdGiQO3j1xvnBs_pLhjn9DHndvFbo-iOni1mWvngb1aCChp3D_L-oO3pY80_45tM4kJertM0n3RO7_7vr58Il7iTwjnt-H8DAAD__29J0zc">