C++ Logo

std-discussion

Advanced search

Re: What is an access in [res.on.objects]?

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Thu, 20 Oct 2022 18:04:07 +0100
On Thu, 20 Oct 2022 at 17:38, <language.lawyer_at_[hidden]> wrote:

> >>> This issue came up on a StackOverflow question:
> >>>
> >>>
> >>
> https://stackoverflow.com/questions/74080177/double-free-in-the-c-standard-library-using-only-stdfunction-and-stdshared/74080676
> >>>
> >>> This led to a discussion here:
> >>>
> >>> https://chat.stackoverflow.com/rooms/248862/discuss-lwg2224
> >>>
> >>> Now, https://eel.is/c++draft/res.on.objects states
> >>>
> >>> "If an object of a standard library type is accessed, and the beginning
> >> of the object's lifetime does not happen before the access, or the
> access
> >> does not happen before the end of the object's lifetime, the behavior is
> >> undefined unless otherwise specified."
> >>>
> >>> According to a non-normative note in
> >> https://eel.is/c++draft/defns.access,
> >>>
> >>> "Only glvalues of scalar type can be used to access objects. ...
> >> Attempts to read or modify an object of class type typically invoke a
> >> constructor or assignment operator; such invocations do not themselves
> >> constitute accesses, although they may involve accesses of scalar
> >> subobjects."
> >>>
> >>> The question is, can objects of class type be accessed?
> >>
> >> Can, in some sense. But it would be UB. E.g.
> >>
> >> struct S
> >> {
> >> char buf[sizeof(int)];
> >> } s;
> >>
> >> reinterpret_cast<int&>(s) = 0; // kinda accesses `s`, but this violates
> >> strict aliasing, so the behavior is actually undefined
> >>
> >
> > It accesses `s`'s storage, sure.
>
> What do you mean by explicitly writing «storage»? You disagree with that
> the expression accesses `s`?
>

Yes, since `s` is not a glvalue of scalar type.

> But a read would not have undefined behavior.
>
> [basic.lval]/11 apply uniformly to both kinds of access: reads and writes.
>

Oh oops, didn't spot that the reinterpret_cast was to int. If it was to a
storage-like type, that would be well-defined, no?

>> Is it the intent that an access to a scalar subobject also constitutes an
> >> access to the object itself?
> >>
> >> No
> >>
> >>> If not, then it is impossible for objects of most library types to be
> >> accessed.
> >>
> >> There are other things which are undefined if an object (or rather a
> >> pointer/glvalue referring to it) is used before the lifetime has started
> >> https://timsong-cpp.github.io/cppwp/n4868/basic.life#6.sentence-5
> >> https://timsong-cpp.github.io/cppwp/n4868/basic.life#7.sentence-4
> >
> >
> > That doesn't apply to objects of class type within
> > construction/destruction; it only applies before their constructor starts
> > or after the destructor ends. During construction/destruction, we are
> > referred to [class.cdtor].
>
> I know.
>
> >> If so, then objects of many library types are unavoidably accessed by
> >> their own destructor, after the end of their lifetime. Either way,
> >> [res.on.objects] doesn't work.
> >>
> >> Treat [res.on.objects] as a non-normative reminder about above-mentioned
> >> parts from [basic.life]
> >> (But in general, there are lots of nonsensical wording in the library
> >> part, if read it from strict core part POV.)
> >>
> >
> > The library desires to be more restrictive than the language here. It is
> > possible to write user code that is robust against recursive invocation
> (by
> > ensuring that invariants hold before calling any operation not directly
> > under the control of the user code, and by ensuring that all and any
> > recursive modifications called from therein are handled in some defined
> > way). But we don't want to constrain the library to that extent, so we
> add
> > UB under [constraints].
>
> AFAIK, recursive invocations are handled by
> https://timsong-cpp.github.io/cppwp/n4868/reentrancy (plus some LWG issue
> saying that invoking method A on a library object during execution of
> method B on the same object is also (should also be) covered by this
> paragraph as a recursive invocation)
>

That's LWG 2414, no?

So you do not need [res.on.objects] to restrict a user destructor in
> calling a vector's methods during that vector's destruction which caused
> the invocation of the user's destructor.
>

You're saying that with LWG 2414 resolved, [res.on.objects]/2 becomes a
dead letter? If so, agree that this looks like an improvement, and fixes
the issue of a library class being able to access its internals during
cdestruction.

One issue is that ctdors are not non-static member functions, so that
proposed resolution (to LWG 2414) would need to be extended to mention
those as well.

Another is the virtual member function issue (though that's probably not a
problem in practice; I don't *think* the Standard has any virtual member
functions not accessing object state).

But most crucially, we're still left with the issue of whether invoking a
non-static member function (or cdtor) is or causes access. Again, the
proposed language in LWG 2414 says "During the execution of a standard
library non-static member function F on an object, if that object is
accessed [...]", implying that that object is of a class type, so not a
scalar, so per se cannot be accessed.

Received on 2022-10-20 17:04:20