Boost C++ Libraries: Ticket #8512: set member_hook access violation https://svn.boost.org/trac10/ticket/8512 <p> Visual Studio 2012 express, all patches. 32 bits. </p> <p> Iterator dereference returns wrong pointer (offset by 4 bytes from normal). </p> <p> I think it's because of virtual inheritance, mistake in offset_from_pointer_to_member (intrusive/detail/parent_from_member.hpp) </p> <p> Output of program below: </p> <blockquote class="citation"> <blockquote class="citation"> <blockquote class="citation"> </blockquote> </blockquote> </blockquote> <p> offsets: 4 and 8 pointers: 00BEF94C and 00BEF948 &lt;&lt;&lt; </p> <p> #include &lt;iostream&gt; #include &lt;string&gt; #include &lt;boost/noncopyable.hpp&gt; #include &lt;boost/intrusive/set.hpp&gt; </p> <p> namespace intrusive = boost::intrusive; </p> <p> class object : public boost::noncopyable { public: </p> <blockquote> <p> object() </p> <blockquote> <p> { } </p> </blockquote> <p> virtual ~object() </p> <blockquote> <p> { } </p> </blockquote> </blockquote> <p> }; </p> <p> class signal : virtual public object { public: </p> <blockquote> <p> signal() { } virtual ~signal() { } </p> </blockquote> <p> }; </p> <p> class record : public signal { public: </p> <blockquote> <p> record() </p> <blockquote> <p> { } </p> </blockquote> <p> virtual ~record() </p> <blockquote> <p> { } </p> </blockquote> </blockquote> <p> public: </p> <blockquote> <p> virtual const std::string&amp; get_buffer() const { </p> <blockquote> <p> return m_buffer; </p> </blockquote> <p> } </p> </blockquote> <blockquote> <p> typedef intrusive::set_member_hook&lt; </p> <blockquote> <p> intrusive::link_mode&lt;intrusive::auto_unlink&gt; </p> <blockquote class="citation"> <p> hook; </p> </blockquote> </blockquote> <p> hook m_hook; </p> </blockquote> <blockquote> <p> std::string m_buffer; </p> </blockquote> <p> }; </p> <p> template &lt;class T, class M, M (T::*V)&gt; struct member_comparator { </p> <blockquote> <p> bool operator()(const T&amp; t1, const T&amp; t2) const { </p> <blockquote> <p> return (t1.*V) &lt; (t2.*V); </p> </blockquote> <p> } bool operator()(const M&amp; m, const T&amp; t) const { </p> <blockquote> <p> return m &lt; (t.*V); </p> </blockquote> <p> } bool operator()(const T&amp; t, const M&amp; m) const { </p> <blockquote> <p> return (t.*V) &lt; m; </p> </blockquote> <p> } </p> </blockquote> <p> }; </p> <p> typedef member_comparator&lt; </p> <blockquote> <p> record, std::string, &amp;record::m_buffer </p> <blockquote class="citation"> <p> record_comparator; </p> </blockquote> </blockquote> <p> typedef intrusive::set&lt; </p> <blockquote> <p> record, intrusive::compare&lt;record_comparator&gt;, intrusive::member_hook&lt; </p> <blockquote> <p> record, record::hook, &amp;record::m_hook </p> <blockquote class="citation"> <p> , </p> </blockquote> </blockquote> <p> intrusive::constant_time_size&lt;false&gt; </p> <blockquote class="citation"> <p> records </p> </blockquote> <p> ; </p> </blockquote> <p> int main(int argc, char<strong> argv) { </strong></p> <blockquote> <p> union { </p> <blockquote> <p> int32_t as_int; const record::hook record::* ptr_to_member; </p> </blockquote> <p> } sss; sss.ptr_to_member = &amp;record::m_hook; </p> </blockquote> <blockquote> <p> std::cout &lt;&lt; "offsets: " &lt;&lt; sss.as_int &lt;&lt; " and " &lt;&lt; offsetof(record,m_hook) &lt;&lt; std::endl; </p> </blockquote> <blockquote> <p> records rr; </p> </blockquote> <blockquote> <p> std::string key = "123"; records::insert_commit_data icd; std::pair&lt;records::iterator,bool&gt; ir = rr.insert_check( </p> <blockquote> <p> key, record_comparator(), icd ); </p> </blockquote> </blockquote> <blockquote> <p> if ( !ir.second ) { </p> <blockquote> <p> throw std::exception(); </p> </blockquote> <p> } </p> </blockquote> <blockquote> <p> record rec; rec.m_buffer = key; records::iterator i = rr.insert_commit( rec, icd ); </p> </blockquote> <blockquote> <p> record* rrr = &amp;(*i); </p> </blockquote> <blockquote> <p> std::cout &lt;&lt; "pointers: " &lt;&lt; ((void*)rrr) &lt;&lt; " and " &lt;&lt; ((void*)&amp;rec) &lt;&lt; std::endl; </p> </blockquote> <blockquote> <p> return 0; </p> </blockquote> <p> } </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/8512 Trac 1.4.3 toby_toby_toby@… Sun, 28 Apr 2013 20:04:07 GMT attachment set https://svn.boost.org/trac10/ticket/8512 https://svn.boost.org/trac10/ticket/8512 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">Source1.cpp</span> </li> </ul> <p> sorry for unformatted source - readded as separate file </p> Ticket toby_toby_toby@… Sun, 28 Apr 2013 20:46:42 GMT <link>https://svn.boost.org/trac10/ticket/8512#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:1</guid> <description> <p> also must add that standard offsetof macro works well (it's based on getting address of member variable of NULL-object) </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Ion Gaztañaga</dc:creator> <pubDate>Sun, 28 Apr 2013 22:36:47 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:2</guid> <description> <p> Thanks for the report. MSVC pointer to member handling it's quite chaotic. Can you try the following patch? </p> <div class="wiki-code"><div class="code"><pre><span class="n">diff</span> <span class="n">U3</span> <span class="nl">C</span><span class="p">:</span><span class="o">/</span><span class="n">Data</span><span class="o">/</span><span class="n">Libs</span><span class="o">/</span><span class="n">LocalSVN</span><span class="o">/</span><span class="n">boost</span><span class="o">-</span><span class="n">trunk</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">intrusive</span><span class="o">/</span><span class="n">detail</span><span class="o">/</span><span class="n">parent_from_member</span><span class="p">.</span><span class="n">hpp</span> <span class="nl">C</span><span class="p">:</span><span class="o">/</span><span class="n">Data</span><span class="o">/</span><span class="n">Libs</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">intrusive</span><span class="o">/</span><span class="n">detail</span><span class="o">/</span><span class="n">parent_from_member</span><span class="p">.</span><span class="n">hpp</span> <span class="o">---</span> <span class="nl">C</span><span class="p">:</span><span class="o">/</span><span class="n">Data</span><span class="o">/</span><span class="n">Libs</span><span class="o">/</span><span class="n">LocalSVN</span><span class="o">/</span><span class="n">boost</span><span class="o">-</span><span class="n">trunk</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">intrusive</span><span class="o">/</span><span class="n">detail</span><span class="o">/</span><span class="n">parent_from_member</span><span class="p">.</span><span class="n">hpp</span> <span class="n">Sat</span> <span class="n">Nov</span> <span class="mi">24</span> <span class="mi">09</span><span class="o">:</span><span class="mi">48</span><span class="o">:</span><span class="mi">40</span> <span class="mi">2012</span> <span class="o">+++</span> <span class="nl">C</span><span class="p">:</span><span class="o">/</span><span class="n">Data</span><span class="o">/</span><span class="n">Libs</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">boost</span><span class="o">/</span><span class="n">intrusive</span><span class="o">/</span><span class="n">detail</span><span class="o">/</span><span class="n">parent_from_member</span><span class="p">.</span><span class="n">hpp</span> <span class="n">Sun</span> <span class="n">Apr</span> <span class="mi">28</span> <span class="mi">23</span><span class="o">:</span><span class="mi">53</span><span class="o">:</span><span class="mi">19</span> <span class="mi">2013</span> <span class="err">@@</span> <span class="o">-</span><span class="mi">37</span><span class="p">,</span><span class="mi">7</span> <span class="o">+</span><span class="mi">37</span><span class="p">,</span><span class="mi">15</span> <span class="err">@@</span> <span class="n">boost</span><span class="o">::</span><span class="kt">int32_t</span> <span class="n">offset</span><span class="p">;</span> <span class="p">}</span> <span class="n">caster</span><span class="p">;</span> <span class="n">caster</span><span class="p">.</span><span class="n">ptr_to_member</span> <span class="o">=</span> <span class="n">ptr_to_member</span><span class="p">;</span> <span class="o">-</span> <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="p">(</span><span class="n">caster</span><span class="p">.</span><span class="n">offset</span><span class="p">);</span> <span class="o">+</span> <span class="c1">//MSVC ABI can use up to 3 int32 to represent pointer to member data</span> <span class="o">+</span> <span class="c1">//with virtual base classes, in that case we must</span> <span class="o">+</span> <span class="c1">//add the size of the extra __vfptr vtable.</span> <span class="o">+</span> <span class="k">if</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">caster</span><span class="p">)</span> <span class="o">&gt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="kt">int32_t</span><span class="p">)){</span> <span class="o">+</span> <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="p">(</span><span class="n">caster</span><span class="p">.</span><span class="n">offset</span><span class="p">)</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">);</span> <span class="o">+</span> <span class="p">}</span> <span class="o">+</span> <span class="k">else</span><span class="p">{</span> <span class="o">+</span> <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="p">(</span><span class="n">caster</span><span class="p">.</span><span class="n">offset</span><span class="p">);</span> <span class="o">+</span> <span class="p">}</span> <span class="c1">//This works with gcc, msvc, ac++, ibmcpp</span> <span class="cp">#elif defined(__GNUC__) || defined(__HP_aCC) || defined(BOOST_INTEL) || \</span> <span class="cp"> defined(__IBMCPP__) || defined(__DECCXX)</span> </pre></div></div> </description> <category>Ticket</category> </item> <item> <author>toby_toby_toby@…</author> <pubDate>Mon, 29 Apr 2013 00:23:26 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:3</guid> <description> <p> Thanks, it helped. </p> <p> PS: But I've got a feeling that previously I was able to make 8-byte difference (by playing with inheritance/member vars/additional virtual functions). I didn't payed attention because I thought that any difference is the same bad thing, that fix will not be for 4-byte only. Now I'm trying to simulate this 8-byte difference again. I will confirm any result, even if I was mistaken and there were only 4-byte errors. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>anonymous</dc:creator> <pubDate>Mon, 29 Apr 2013 00:55:09 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:4</guid> <description> <p> Well, now it's broken in different place (intrusive::list_member_hook). </p> <p> We hit patched condition "if(sizeof(caster) &gt; sizeof(boost::int32_t))" and add sizeof(void*), 8+4=12, but actual difference is 8. </p> <p> Now I'm gonna update source to include check for this also. </p> <p> PS: Sorry if this question is dumb, but why don't we use VC/stddef.h offsetof or it's approach with getting address of member of null-object? </p> </description> <category>Ticket</category> </item> <item> <author>toby_toby_toby@…</author> <pubDate>Mon, 29 Apr 2013 01:06:47 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/8512 https://svn.boost.org/trac10/ticket/8512 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">set_and_list.cpp</span> </li> </ul> <p> after patch set is working, but list is not </p> Ticket toby_toby_toby@… Mon, 29 Apr 2013 11:43:44 GMT <link>https://svn.boost.org/trac10/ticket/8512#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:5</guid> <description> <p> Switched to base hooks with tags, because fixing of member hook failed:<br /> </p> <p> 1) offsetof's approach not working - access violation, null can't be dereferenced<br /> </p> <p> 2) fake object "char temp[sizeof(Parent)]" also not working - it actually accesses vptr<br /> </p> <p> 3) inspection of additional integers in caster gave nothing - they are the same for both cases, no way to deduce </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Ion Gaztañaga</dc:creator> <pubDate>Mon, 29 Apr 2013 15:45:54 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:6</guid> <description> <p> I don't know if virtual or multiple inheritance is involved in these issues, and it seems that MSVC's pointer to member data can have the size of 1, 2 or 3 ints. </p> <p> Can you please provide a test case with a 8 byte difference? I guess it could help me to deduce MSVC's ABI. </p> </description> <category>Ticket</category> </item> <item> <author>toby_toby_toby@…</author> <pubDate>Mon, 29 Apr 2013 18:56:52 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:7</guid> <description> <p> I was unable to simulate 8 byte difference, it may never exist in fact. I just had feeling that it was, but it seems that I was wrong. </p> <p> But set_and_list.cpp attachment gives you an example when your current patch is not working, so you can try to deduce something on this.. I was unable to. set_item is working fine with +sizeof(void*), but list_item gives wrong pointer with this void* addon. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Ion Gaztañaga</dc:creator> <pubDate>Tue, 30 Apr 2013 22:48:15 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:8</guid> <description> <p> It seems that even there is no virtual inheritance boundary between the member and the parent, MSVC ABI requires pointer adjustment operations with information from the virtual base table. asm code for pointer to member dereferencing shows vbtable is read to correct parent pointer before adding the offset stored in the pointer to member data. </p> <p> So it seems very difficult or maybe impossible, to obtain the pointer adjustment value from the vtable to go back from the member to the parent (we need a way to obtain access to the vbtable without using a valid parent pointer). </p> <p> I think the library should give a compile time error (if pointer to member is not a simple offset) and document that for Windows compilers member hooks have limitations and don't work if types have direct or indirect virtual bases. </p> </description> <category>Ticket</category> </item> <item> <author>toby_toby_toby@…</author> <pubDate>Tue, 30 Apr 2013 23:25:36 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8512#comment:9 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:9</guid> <description> <p> Static assertion is a good idea, even better with unit tests. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Ion Gaztañaga</dc:creator> <pubDate>Tue, 28 May 2013 21:01:25 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/8512#comment:10 https://svn.boost.org/trac10/ticket/8512#comment:10 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">wontfix</span> </li> </ul> <p> After some research even reverse engineering MSVC RTTI we might not solve this issue. Boost 1.54 will include the static assertion for member hooks that need complex pointers to member data structures in MSVC ABI. Thanks for the report. </p> Ticket toby_toby_toby@… Sat, 08 Jun 2013 08:31:17 GMT <link>https://svn.boost.org/trac10/ticket/8512#comment:11 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8512#comment:11</guid> <description> <p> Just an idea, add new option to hook: </p> <pre class="wiki">typedef boost::intrusive::list_member_hook&lt; boost::intrusive::store_pointer_to_parent_inside&lt;true&gt; &gt; </pre><p> So we will have </p> <pre class="wiki">parent* _parent; </pre><p> inside hook (null in constructor) for platforms that require it, if GCC does not need - then just do nothing with this option. </p> <p> Then, when we call container.insert( item ), we actually have reference to item so we can assign it: </p> <pre class="wiki">(item.*hook_member_ptr)._parent = &amp;item; </pre><p> And clear on erase. Now we don't need offsetoff or etc. </p> <p> Static assertion will only be rised when compiler requires this (for people like me who actually expirienced problems). For other users it will be backward compatible: no need to specify option, sizeof same. Option only required for msvc+virtual inheritance. </p> </description> <category>Ticket</category> </item> </channel> </rss>