Date: Fri, 8 Jan 2016 12:54:38 -0500
My two cents: even when memcpy becomes capable of initiating the lifetime
of (and initializing) an object in an "aligned_storage blob", the kosher
way of accessing that object would require std::launder.
Now, I am not sure if it is a copy/paste error, my misunderstanding of the
code, or what, but it seems that (2) crashes because "&erasure()" and
"&storage" overlap (and if the memcpy UB was not enough, it probably does
not do what you intended).
-- HT
On Fri, Jan 8, 2016 at 10:15 AM, David Krauss <david_work_at_[hidden]> wrote:
> Porting my std::function library <https://github.com/potswa/cxx_function/> to
> MSVC, some UB reared its head. The function wrapper class uses an
> aligned_storage blob for its call target under the small-object
> optimization. When the target is small and trivially-copyable, the
> copy/move constructors use memcpy on the blob to avoid determining the
> dynamic type of the target.
>
> MSVC seems to do stricter alias analysis than GCC or Clang. The effect of
> the memcpy is not subsequently observed unless its both its source and
> destination operands are of the appropriate type (before they’re implicitly
> cast to void*).
>
> // Given:
> erasure_base & erasure()
> { return reinterpret_cast< erasure_base & >( this->storage ); }
>
> // Inside copy/move constructor:
> // (1) well-behaved:
> std::memcpy( & erasure(), & o.erasure(), sizeof (storage) );
> // (2) crashes after erasure().foo observes stale bytes:
> std::memcpy( & erasure(), & storage, sizeof (storage) );
>
> In retrospect, (1) is certainly more kosher, but shouldn’t (2) work as
> well?
>
> This doesn’t seem to rise to the level of a bug report, but it might be
> relevant to the new memcpy lifetime-initiation rules. Will they ascribe
> some static type to the source and destination bytes?
>
> More worryingly, erasure_base is a base class. When the memcpy replaces
> one derived object with another of the same type, what’s the guarantee that
> the overwritten value won’t be observed? This problem doesn’t manifest so
> far, but it seems only a matter of time. No easy solution comes to mind,
> nor any practical workaround for type-erasure libraries. The tough solution
> is for alias analysis to somehow work without considering the static types
> passed to memcpy.
>
> I’m no expert on alias analysis, so I don’t know whether GCC and Clang are
> more clever, or less aggressive. Is it luck that the same bug hasn’t
> appeared on other platforms? Any insights about the upcoming rules?
>
> - thanks,
> D
>
>
> _______________________________________________
> ub mailing list
> ub_at_[hidden]
> http://www.open-std.org/mailman/listinfo/ub
>
>
of (and initializing) an object in an "aligned_storage blob", the kosher
way of accessing that object would require std::launder.
Now, I am not sure if it is a copy/paste error, my misunderstanding of the
code, or what, but it seems that (2) crashes because "&erasure()" and
"&storage" overlap (and if the memcpy UB was not enough, it probably does
not do what you intended).
-- HT
On Fri, Jan 8, 2016 at 10:15 AM, David Krauss <david_work_at_[hidden]> wrote:
> Porting my std::function library <https://github.com/potswa/cxx_function/> to
> MSVC, some UB reared its head. The function wrapper class uses an
> aligned_storage blob for its call target under the small-object
> optimization. When the target is small and trivially-copyable, the
> copy/move constructors use memcpy on the blob to avoid determining the
> dynamic type of the target.
>
> MSVC seems to do stricter alias analysis than GCC or Clang. The effect of
> the memcpy is not subsequently observed unless its both its source and
> destination operands are of the appropriate type (before they’re implicitly
> cast to void*).
>
> // Given:
> erasure_base & erasure()
> { return reinterpret_cast< erasure_base & >( this->storage ); }
>
> // Inside copy/move constructor:
> // (1) well-behaved:
> std::memcpy( & erasure(), & o.erasure(), sizeof (storage) );
> // (2) crashes after erasure().foo observes stale bytes:
> std::memcpy( & erasure(), & storage, sizeof (storage) );
>
> In retrospect, (1) is certainly more kosher, but shouldn’t (2) work as
> well?
>
> This doesn’t seem to rise to the level of a bug report, but it might be
> relevant to the new memcpy lifetime-initiation rules. Will they ascribe
> some static type to the source and destination bytes?
>
> More worryingly, erasure_base is a base class. When the memcpy replaces
> one derived object with another of the same type, what’s the guarantee that
> the overwritten value won’t be observed? This problem doesn’t manifest so
> far, but it seems only a matter of time. No easy solution comes to mind,
> nor any practical workaround for type-erasure libraries. The tough solution
> is for alias analysis to somehow work without considering the static types
> passed to memcpy.
>
> I’m no expert on alias analysis, so I don’t know whether GCC and Clang are
> more clever, or less aggressive. Is it luck that the same bug hasn’t
> appeared on other platforms? Any insights about the upcoming rules?
>
> - thanks,
> D
>
>
> _______________________________________________
> ub mailing list
> ub_at_[hidden]
> http://www.open-std.org/mailman/listinfo/ub
>
>
Received on 2016-01-08 18:54:42