Subject: Re: [boost] [beast] Formal review
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2017-07-12 01:19:29


On Wed, Jul 12, 2017 at 2:33 AM, Gavin Lambert via Boost
<boost_at_[hidden]> wrote:
> On 11/07/2017 21:30, Andrey Semashev wrote:
>>
>> You can cast both ways. The casted-from-char pointer can be used as long
>> as the underlying object, in the C++ object model, matches the pointed type.
>> In other words, this is fine:
>>
>> std::size_t* p1 = new std::size_t[10];
>> char* p2 = reinterpret_cast< char* >(p1);
>> std::size_t* p3 = reinterpret_cast< std::size_t* >(p2);
>> assert(p1 == p3);
>> p3[0] = 10; // ok
>>
>> In Beast's case we (and compiler) cannot tell whether the pointers refer
>> to std::size_t objects. For compiler that means it has to assume the objects
>> exist and thus prevent any optimizations that would contradict it.
>
> I'm not an expert, but I believe that strictly according to the standard it
> is UB to cast from a char* to any other pointer type, regardless.

It's not, according to [expr.static.cast]/13 and [expr.reinterpret.cast]/7.

> (It's also important to note that the standard only permits casting to
> "char*" -- not "unsigned char*" or "uint8_t*".)

As noted above, you can cast between any of these types. It is what
you can do with the resulting pointers that is restricted.

Character types (and std::byte since C++17 - which, BTW, has unsigned
char as the base type) are special because storage and object
representation are expressed with these types. See [intro.object]/3,
[basic.types]/4. There are also multiple other places where the spec
allows to obtain a pointer to storage expressed as a pointer to one of
the character types or std::byte.

> Having said that, as long as you are careful to only do so with POD types
> (or at least types that cannot have a vtable) and only when the alignment
> requirements of the type are met, then most compilers should probably let
> you get away with it, because it's far too useful functionality to ban
> outright.

The nature of the type (POD or not) is not significant - all objects
obey the same object model.

> In the example above, since you're starting with a "real" type and then
> casting to char* and back you're guaranteed to meet the alignment
> requirements, so it should be reasonably safe.
>
> You can still run afoul of strict aliasing if you pass both pointers to
> other methods, however (but passing only one should be safe). Hopefully the
> compiler is smart enough to notice that they're aliased as long as you stay
> within the same method.

If you break strict aliasing rules you are in the UB land. The
compiler is not required to notice anything - it simply follows the
assumption that the rules are not violated. The end result is likely
misbehaving code.