2

I was digging into code of flatbuffers and it does seem to me that there is undefined behavior in the core library. But I just can't believe it is there (and it seems to work in some usecases)...

I'd highly appreciate if somebody verifies my way of thinking/shows me that I missed something :)

https://github.com/google/flatbuffers/blob/99fda819058afd617537704abda3f06807aee422/include/flatbuffers/vector.h#L80

  difference_type operator-(const VectorIterator &other) const {
    return (data_ - other.data_) / element_stride;
  }

My rationale, why I think it is UB:

difference_type == std::ptrdiff_t which is based on standard Signed ["something" >= 16b] (https://en.cppreference.com/w/cpp/types/ptrdiff_t)

Let's expect that the first parenthesis end up negative (which should be valid usecase)

  1. Now when I assume the ptrdiff_t is <= size_t:

    • We get to:

      • ptrdiff_t retval = (uint8_t* - uint8_t*)/size_t
    • pointer arithmetics also end up as ptrdiff_t. So we are at:

      • ptrdiff_t retval = ptrdiff_t/size_t
    • This leads me to:

      • Signed = Signed/Unsigned
    • And based on standard (8/11.5.3) we get the first operand of divison promoted to Unsigned: The standard snippet

      • the result will end up as "something positive and huge" as the negative number got promoted to huge unsigned one
  2. Now lets assume ptrdiff_t is > size_t

    • pretty much same steps apply, except the last one:
      • based on standard (8/11.5.4), the second operand of division get promoted to signed number of bigger bitsize
      • this leads to return value to be (the intuitive) negative number that says distance between the two VectorIterators.

So I see there 2 possible outcomes of the operator-, both complying with the standard requirements, based on ptrdiff_t implementation by specific compiler.

Thanks!

4
  • The calculation result may be something positive and huge, but then it's implicitly converted back to signed type in return, so huge positive value becomes normal negative one again. Commented Jan 8 at 13:58
  • 1
    This is the least of it in flatbuffers, if we are language lawyering. Flatbuffers relies heavily on outside-the-standard guarantees. Just because the standard doesn't define a behvior, doesn't mean a concrete implementation can't define what happens. In this case it assumes 2's complement Commented Jan 8 at 14:18
  • Maybe make a PR to cast element_stride to ptrdiff_t ? Commented Jan 8 at 18:38
  • 2
    @Yksisarvinen: After the division it won’t wrap back. Commented Jan 9 at 2:08

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.