Boost C++ Libraries: Ticket #10109: Wrong overflow checking with uint_parser<int> https://svn.boost.org/trac10/ticket/10109 <p> Running the following code will produce "<code>Parsed -2147483648</code>" on the standard output. </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/spirit/include/qi.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/spirit/include/qi_parse_attr.hpp&gt;</span><span class="cp"></span> <span class="k">using</span> <span class="k">namespace</span> <span class="n">boost</span><span class="o">::</span><span class="n">spirit</span><span class="o">::</span><span class="n">qi</span><span class="p">;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="kt">char</span> <span class="k">const</span> <span class="n">test</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&quot;2147483648&quot;</span><span class="p">;</span> <span class="c1">//INT_MAX + 1</span> <span class="kt">char</span> <span class="k">const</span><span class="o">*</span> <span class="n">begin</span> <span class="o">=</span> <span class="n">test</span><span class="p">;</span> <span class="kt">int</span> <span class="n">n</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span> <span class="n">parse</span><span class="p">(</span> <span class="n">begin</span><span class="p">,</span> <span class="n">test</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">test</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">uint_parser</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(),</span> <span class="n">n</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Parsed &quot;</span> <span class="o">&lt;&lt;</span> <span class="n">n</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Parse failed&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div></div><p> Since I'm requesting a <code>uint_parser</code>, I assume I should never get a negative value. This is what happens, for instance, with <code>uint_parser&lt;double&gt;</code>, which will reject negative values. </p> <p> The issue probably lies in <code>spirit/home/qi/numeric/numeric_utils.hpp</code>, where <code>extract_uint::call</code> invokes </p> <div class="wiki-code"><div class="code"><pre><span class="n">extract_type</span><span class="o">::</span><span class="n">parse</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">detail</span><span class="o">::</span><span class="n">cast_unsigned</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">call</span><span class="p">(</span><span class="n">attr_</span><span class="p">))</span> </pre></div></div><p> but later <code>positive_accumulator::add</code>, will check the overflow condition for the type of its argument (which will be <code>unsigned int</code>, due to the previous cast), rather than for the template type <code>T</code> of the <code>extract_uint</code> struct, which is correctly <code>int</code>. </p> <p> The call stack I'm talking about is: <code>parse -&gt; any_uint_parser::parse -&gt; extract_uint::call -&gt; extract_int::parse -&gt; extract_int::parse_main -&gt; int_extractor::call -&gt; int_extractor::call (with mpl::true_) -&gt; positive_accumulator::add</code> </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/10109 Trac 1.4.3 Joel de Guzman Tue, 10 Jun 2014 15:25:14 GMT <link>https://svn.boost.org/trac10/ticket/10109#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10109#comment:1</guid> <description> <p> You are requesting for a unit parser but you give it an int? uint_parser, for performance reasons, does not test for wrap around -- which is what happens here. it is assumed that your type T does not wrap around as int does. perhaps we should be strict and test it with a static assert instead, but there's no generic way to know if T does or does not wrap around. The alternative, that you seem to be suggesting is to check for wrap, but that would penalise the majority of uses. </p> <p> No this is not a bug. And I don't think it should be "fixed". The best we can do is document this precondition that T should not wrap around. </p> </description> <category>Ticket</category> </item> <item> <author>Giulio Franco <giulio_franco@…></author> <pubDate>Tue, 10 Jun 2014 16:59:16 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10109#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10109#comment:2</guid> <description> <p> According to the docs, <code>uint_parser</code> is supposed to parse an unsigned value, it doesn't make any restriction on the type it will read the value into. In fact, you can also create a uint_parser&lt;double&gt;, and in that case <code>int_extractor::call(Char, size_t, T&amp;)</code> will use a different implementation, that just adds the digits without checking for overflow. </p> <p> As I wrote, the current implementations already checks for wrap around, by using <code>std::numeric_limits&lt;T&gt;::max</code> in </p> <div class="wiki-code"><div class="code"><pre><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span> <span class="n">Char</span><span class="o">&gt;</span> <span class="n">positive_accumulator</span><span class="o">::</span><span class="n">add</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;</span><span class="p">,</span> <span class="n">Char</span><span class="p">,</span> <span class="n">mpl</span><span class="o">::</span><span class="n">true_</span><span class="p">)</span> </pre></div></div><p> The only issue is that it's using the wrong <code>T</code>. It uses <code>numeric_limits&lt;unsigned int&gt;</code>, because it is passed an <code>unsigned int&amp;</code>. If it used <code>numeric_limits&lt;signed int&gt;</code> with the very same algorithm it would probably work perfectly. It looks like it only involves template work, which means no performance impact for those who aren't using it. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Joel de Guzman</dc:creator> <pubDate>Tue, 10 Jun 2014 22:38:31 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10109#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10109#comment:3</guid> <description> <p> OK, if you have a patch, then I'll consider it. Please post a pull request to <a class="ext-link" href="https://github.com/boostorg/spirit/tree/develop"><span class="icon">​</span>https://github.com/boostorg/spirit/tree/develop</a>, making sure that all the tests pass. </p> <p> (Yes, the docs does not give any restrictions on T at the moment, but if there's no optimal way to make it work with wrapping ints, then I'm inclined to make it an unchecked precondition. T == double is irrelevant because double does not wrap. You can also use saturating ints that do not wrap, for example). </p> </description> <category>Ticket</category> </item> <item> <author>Nikita Kniazev <nok.raven@…></author> <pubDate>Mon, 01 Jan 2018 18:20:43 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10109#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10109#comment:4</guid> <description> <p> Fixed in <a class="ext-link" href="https://github.com/boostorg/spirit/pull/297"><span class="icon">​</span>https://github.com/boostorg/spirit/pull/297</a> </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Joel de Guzman</dc:creator> <pubDate>Mon, 19 Feb 2018 23:15:27 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/10109#comment:5 https://svn.boost.org/trac10/ticket/10109#comment:5 <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">fixed</span> </li> </ul> Ticket