From: David Malcolm <dmalcolm@redhat.com>
To: Alejandro Colomar <alx@kernel.org>, Paul Eggert <eggert@cs.ucla.edu>
Cc: gcc@gcc.gnu.org, linux-man@vger.kernel.org, xry111@xry111.site,
jakub@redhat.com, uecker@tugraz.at, lh_mouse@126.com,
jwakely.gcc@gmail.com, Richard.Earnshaw@arm.com, sam@gentoo.org,
ben.boeckel@kitware.com, heiko.eissfeldt@siemens.com,
libc-alpha@sourceware.org
Subject: Re: WG14 paper for removing restrict from nptr in strtol(3)
Date: Mon, 08 Jul 2024 10:30:48 -0400 [thread overview]
Message-ID: <e2d7b36d5716f1799ba43d1373a0a2d65cc7cb16.camel@redhat.com> (raw)
In-Reply-To: <tuosvljyewh6oxpjqwgc6jbs7caknva572zij2u6hr4xwphlt2@kxz4xcdghaxl>
On Sun, 2024-07-07 at 14:42 +0200, Alejandro Colomar wrote:
> Hi Paul,
>
> On Sun, Jul 07, 2024 at 12:42:51PM GMT, Paul Eggert wrote:
> > On 7/7/24 03:58, Alejandro Colomar wrote:
> >
> > > I've incorporated feedback, and here's a new revision, let's call
> > > it
> > > v0.2, of the draft for a WG14 paper.
> > Although I have not followed the email discussion closely, I read
> > v0.2 and
> > think that as stated there is little chance that its proposal will
> > be
> > accepted.
>
> Thanks for reading thoroughly, and the feedback!
>
> > Fundamentally the proposal is trying to say that there are two
> > styles X and
> > Y for declaring strtol and similar functions, and that although
> > both styles
> > are correct in some sense, style Y is better than style X. However,
> > the
> > advantages of Y are not clearly stated and the advantages of style
> > X over Y
> > are not admitted, so the proposal is not making the case clearly
> > and fairly.
> >
> > One other thing: to maximize the chance of a proposal being
> > accepted, please
> > tailor it for its expected readership. The C committee is expert on
> > ‘restrict’, so don’t try to define ‘restrict’ in your own way.
> > Unless merely
> > repeating the language of the standard, any definition given for
> > ‘restrict’
> > is likely to cause the committee to quibble with the restatement of
> > the
> > standard wording. (It is OK to mention some corollaries of the
> > standard
> > definition, so long as the corollaries are not immediately
> > obvious.)
> >
> > Here are some comments about the proposal. At the start these
> > comments are
> > detailed; towards the end, as I could see the direction the
> > proposal was
> > headed and was convinced it wouldn’t be accepted as stated, the
> > comments are
> > less detailed.
> >
> >
> > "The API may copy"
> >
> > One normally doesn’t think of the application programming interface
> > as
> > copying. Please replace the phrase “the API” with “the caller” or
> > “the
> > callee” as appropriate. (Although ‘restrict’ can be used in places
> > other
> > than function parameters, I don’t think the proposal is concerned
> > about
> > those cases and so it doesn’t need to go into that.)
>
> Ok.
>
> > "To avoid violations of for example C11::6.5.16.1p3,"
> >
> > Code that violates C11::6.5.16.1p3 will do so regardless of whether
> > ‘restrict’ is present. I would not mention C11::6.5.16.1p3 as it’s
> > a red
> > herring. Fundamentally, ‘restrict’ is not about the consequences of
> > caching
> > when one does overlapping moves; it’s about caching in a more
> > general sense.
>
> The violations are UB regardless of restrict, but consistent use of
> restrict allows the caller to have a rough model of what the callee
> will
> do with the objects, and prevent those violations via compiler
> diagnostics. I've reworded that part to make it more clear why I'm
> mentioning that.
>
> > “As long as an object is only accessed via one restricted pointer,
> > other
> > restricted pointers are allowed to point to the same object.”
> >
> > “only accessed” → “accessed only”
>
> Ok.
>
> > “This is less strict than I think it should be, but this proposal
> > doesn’t
> > attempt to change that definition.”
> >
> > I would omit this sentence and all similar sentences. Don’t
> > distract the
> > reader with other potential proposals. The proposal as it stands is
> > complicated enough.
>
> Ok.
>
> > “return ca > a;”
> > “return ca > *ap;”
> >
> > I fail to understand why these examples are present. It’s not
> > simply that
> > nobody writes code like that: the examples are not on point. I
> > would remove
> > the entire programs containing them, along with the sections that
> > discuss
> > them. When writing to the C committee one can assume the reader is
> > expert in
> > ‘restrict’, there is no need for examples such as these.
>
> Those are examples of how consistent use of restrict can --or could,
> in
> the case of g()-- detect, via compiler diagnostics, (likely)
> violations
> of seemingly unrelated parts of the standard, such as the referenced
> C11::6.5.16.1p3, or in this case, C11::6.5.8p5.
>
> > “strtol(3) accepts 4 objects via pointer parameters and global
> > variables.”
> >
> > Omit the “(3)”, here and elsewhere, as the audience is the C
> > standard
> > committee.
>
> The C standard committee doesn't know about historic use of (3)?
> That
> predates the standard, and they built on top of that (C originated in
> Unix). While they probably don't care about it anymore, I expect my
> paper to be read by other audience, including GCC and glibc, and I
> prefer to keep it readable for that audience. I expect the standard
> committee to at least have a rough idea of the existence of this
> syntax,
> and respect it, even if they don't use it or like it.
>
> > “accepts” is a strange word to use here: normally one says
> > “accepts” to talk
> > about parameters, not global variables.
>
> The thing is, strtol(3) does not actually access *endptr. I thought
> that might cause more confusion than using "accepts".
>
> > Also, “global variables” is not
> > right here. The C standard allows strtol, for example, to read and
> > write an
> > internal static cache. (Yes, that would be weird, but it’s
> > allowed.)
>
> That's not part of the API. A user must not access internal static
> cache, and so the implementation is free to assume that it doesn't,
> regardless of the use of restrict in the API, so it is not relevant
> for
> the purpose of this discussion, I think.
>
> > I
> > suggest rephrasing this sentence to talk about accessing, not
> > accepting.
>
> I don't want to use accessing, for it would be inconsistent with
> later
> saying that *endptr is not accessed. However, I'm open to other
> suggested terms that might be more appropriate than both.
>
> > “endptr access(write_only) ... *endptr access(none)”
> >
> > This is true for glibc, but it’s not necessarily true for all
> > conforming
> > strtol implementations. If endptr is non-null, a conforming strtol
> > implementation can both read and write *endptr;
>
> It can't, I think. It's perfectly valid to pass an uninitialized
> endptr, which means the callee must not read the original value.
>
> char *end;
> strtol("0", &end, 0);
>
> If strtol(3) would be allowed to read it, the user would need to
> initialize it.
>
> > it can also both read and
> > write **endptr. (Although it would need to write before reading,
> > reading is
> > allowed.)
>
> Here, we need to consider two separate objects. The object pointed-
> to
> by *endptr _before_ the object pointed to by endptr is written to,
> and
> the object pointed-to by *endptr _after_ the object pointed to by
> endptr
> is written to.
>
> For the former (the original *endptr):
>
> Since *endptr might be uninitialized, strtol(3) must NOT
> access
> the object pointed to by an uninitialized pointer.
>
> For the latter (the final *endptr):
>
> The callee cannot write to it, since the specification of the
> function is that the string will not be modified. And in any
> case, such an access is ultimately derived from nptr, not
> from
> *endptr, so it does not matter for the discussion of *endptr.
>
> Of course, that's derived from the specification of the function, and
> not from its prototype, since ISO C doesn't provide such detailed
> prototypes (since it doesn't have the [[gnu::access()]] attribute).
> But
> the standard must abide by its own specification of functions,
> anyway.
>
> > “This qualifier helps catch obvious bugs such as strtol(p, p, 0)
> > and
> > strtol(&p, &p, 0) .”
> >
> > No it doesn’t. Ordinary type checking catches those obvious bugs,
> > and
> > ‘restrict’ provides no additional help there. Please complicate the
> > examples
> > to make the point more clearly.
>
> To be pedantic, I didn't specify the type of p, so it might be (void
> *),
> and thus avoid type checking at all. However, to avoid second
> guessing
> from the standards committee, I'll add casts, to make it more obvious
> that restrict is catching those.
>
> > “The caller knows that errno doesn’t alias any of the function
> > arguments.”
> >
> > Only because all args are declared with ‘restrict’. So if the
> > proposal is
> > accepted, the caller doesn’t necessarily know that.
>
> Not really. The caller has created the string (or has received it
> via a
> restricted pointer), and so it knows it's not derived from errno.
>
> char buf[LINE_MAX + 1];
>
> fgets(...);
> n = strtol(buf, ...);
>
> This caller knows with certainty that errno does not alias buf. Of
> course, in some complex cases, it might not know, but I ommitted that
> for simplicity. And in any case, I don't think any optimizations are
> affected by that in the caller.
>
> >
> >
> > “The callee knows that *endptr is not accessed.”
> >
> > This is true for glibc, but not necessarily true for every
> > conforming strtol
> > implementation.
>
> The original *endptr may be uninitialized, and so must not be
> accessed.
>
> > “It might seem that it’s a problem that the callee doesn’t know if
> > nptr can
> > alias errno or not. However, the callee will not write to the
> > latter
> > directly until it knows it has failed,”
> >
> > Again this is true for glibc, but not necessarily true for every
> > conforming
> > strtol implementation.
>
> An implementation is free to set errno = EDEADLK in the middle of it,
> as
> long as it later removes that. However, I don't see how it would
> make
> any sense.
>
> If that's done, it's probably done via a helper internal function,
> which
> as said below, can use restrict for nptr, and thus know with
> certainty
> that nptr is distinct from errno.
>
> If that's done directly in the body of strtol(3) (the only place
> where
> it's not known that nptr is distinct from errno) we can probably
> agree
> that the implementation is doing that just for fun, and doesn't care
> about optimization, and thus we can safely ignore it.
>
> > To my mind this is the most serious objection. The current standard
> > prohibits calls like strtol((char *) &errno, 0, 0). The proposal
> > would relax
> > the standard to allow such calls. In other words, the proposal
> > would
> > constrain implementations to support such calls.
>
> I don't think it does. ISO C specifies that strtol(3) takes a string
> as
> its first parameter, and errno is not (unless you do this:).
>
> (char *)&errno = "111";
>
> Okay, let's assume you're allowed to do that, since a char* can alias
> anything.
>
> I still don't think ISO C constrains implementations to allow passing
> (char *)&errno as a char*, just because it's not restrict. Let's
> find
> an ISO C function that accepts a non-restrict string:
>
> int system(const char *string);
>
> Does ISO C constrain implementations to support system((char
> *)&errno)?
> I don't think so. Maybe it does implicitly because of a defect in
> the
> wording, but even then it's widely understood that it doesn't.
>
> > Why is this change worth
> > making? Real-world programs do not make calls like that.
>
> Because it makes analysis of 'restrict' more consistent. The obvious
> improvement of GCC's analyzer to catch restrict violations will
> trigger
> false positives in normal uses of strtol(3).
Hi Alejandro
I'm author/maintainer of GCC's -fanalyzer option, which is presumably
why you CCed me on this. One of my GSoC 2022 students (Tim Lange)
looked at making use of 'restrict' in -fanalyzer, see e.g.
https://lists.gnu.org/archive/html/bug-gnulib/2022-07/msg00062.html
Based on Paul's comment here:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99860#c2 (and its
references) I came to the conclusion at the time that we should work on
something else, as the meaning of 'restrict' is too ambiguous.
Later, I added a new -Wanalyzer-overlapping-buffers warning in GCC 14,
which simply has a hardcoded set of standard library functions that it
"knows" to warn about.
Has the C standard clarified the meaning of 'restrict' since that
discussion? Without that, I wasn't planning to touch 'restrict' in
GCC's -fanalyzer.
Sorry if I'm missing anything here; I confess I've skimmed through
parts of this thread.
Dave
>
> > “But nothing prohibits those internal helper functions to specify
> > that nptr
> > is restrict and thus distinct from errno.”
> >
> > Although true, it’s also the case that the C standard does not
> > *require*
> > internal helper functions to use ‘restrict’. All that matters is
> > the
> > accesses. So I’m not sure what the point of this statement is.
>
> If an implementation wants to optimize, it should be careful and use
> restrict. If it doesn't, then it can't complain that ISO C doesn't
> allow it to. It's actually allowed to optimize, but it has to do
> some
> work for it.
>
> > “m = strtol(p, &p, 0); An analyzer more powerful than the current
> > ones
> > could extend the current -Wrestrict diagnostic to also diagnose
> > this case.”
> >
> > Why would an analyzer want to do that? This case is a perfectly
> > normal thing
> > to do and it has well-defined behavior.
>
> Because without an analyzer, restrict cannot emit many useful
> diagnostics. It's a qualifier that's all about data flow analysis,
> and
> normal diagnostics aren't able to do that.
>
> A qualifier that enables optimizations but doesn't enable diagnostics
> is
> quite dangerous, and probably better not used. If however, the
> analyzer
> emits advanced diagnostics for misuses of it, then it's a good
> qualifier.
>
> Have a lovely day!
> Alex
>
> >
> > “To prevent triggering diagnostics in a powerful analyzer that
> > would be
> > smart enough to diagnose the example function g(), the prototype of
> > strtol(3) should be changed to ‘long int strtol(const char *nptr,
> > char
> > **restrict endptr, int base);’”
> >
> > Sorry, but the case has not been made to make any such change to
> > strtol’s
> > prototype. On the contrary, what I’m mostly gathering from the
> > discussion is
> > that ‘restrict’ can be confusing, which is not news.
> >
> > n3220 §6.7.4.2 examples 5 through 7 demonstrate that the C
> > committee has
> > thought through the points you’re making. (These examples were not
> > present
> > in C11.) This may help to explain why the standard specifies strtol
> > with
> > ‘restrict’ on both arguments.
> >
>
next prev parent reply other threads:[~2024-07-08 14:30 UTC|newest]
Thread overview: 76+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20240705130249.14116-2-alx@kernel.org>
[not found] ` <38982a470643f766747b0ca06b27ca859a87b101.camel@xry111.site>
2024-07-05 14:37 ` [PATCH v1] Remove 'restrict' from 'nptr' in strtol(3)-like functions Alejandro Colomar
2024-07-05 15:02 ` Martin Uecker
2024-07-05 15:23 ` Alejandro Colomar
2024-07-05 15:34 ` Martin Uecker
2024-07-05 15:53 ` Alejandro Colomar
2024-07-05 16:01 ` Xi Ruoyao
2024-07-05 16:17 ` Xi Ruoyao
2024-07-05 16:24 ` Jonathan Wakely
2024-07-05 16:30 ` Martin Uecker
2024-07-05 19:28 ` Alejandro Colomar
2024-07-05 19:38 ` Jonathan Wakely
2024-07-05 19:47 ` Alejandro Colomar
2024-07-05 19:52 ` Jonathan Wakely
2024-07-05 20:11 ` Alejandro Colomar
2024-07-05 20:15 ` Emanuele Torre
2024-07-05 20:31 ` Ben Boeckel
2024-07-05 20:25 ` Martin Uecker
2024-07-05 20:28 ` Jonathan Wakely
2024-07-05 20:41 ` Alejandro Colomar
2024-07-05 20:55 ` Alejandro Colomar
2024-07-05 21:39 ` Jonathan Wakely
2024-07-05 22:02 ` Alejandro Colomar
2024-07-05 22:04 ` Alejandro Colomar
2024-07-06 2:24 ` Xi Ruoyao
2024-07-06 2:39 ` Xi Ruoyao
2024-07-06 5:51 ` [[gnu::null_terminated_string_arg(1)]] on strtol(1) (was: [PATCH v1] Remove 'restrict' from 'nptr' in strtol(3)-like) functions Alejandro Colomar
2024-07-06 6:10 ` [PATCH v1] Remove 'restrict' from 'nptr' in strtol(3)-like functions Alejandro Colomar
2024-07-06 6:11 ` Alejandro Colomar
2024-07-05 16:32 ` Sam James
2024-07-05 16:02 ` Martin Uecker
2024-07-05 16:11 ` Jonathan Wakely
2024-07-05 16:21 ` Richard Earnshaw (lists)
2024-07-05 15:54 ` LIU Hao
2024-07-05 15:55 ` Xi Ruoyao
2024-07-05 16:32 ` Alejandro Colomar
2024-07-05 17:32 ` Alejandro Colomar
2024-07-05 19:41 ` [WG14] Request for document number; strtol restrictness Alejandro Colomar
2024-07-07 15:46 ` Daniel Plakosh
2024-07-09 19:00 ` Alejandro Colomar
2024-07-09 20:04 ` Daniel Plakosh
2024-07-07 1:58 ` WG14 paper for removing restrict from nptr in strtol(3) Alejandro Colomar
2024-07-07 7:15 ` Martin Uecker
2024-07-07 11:07 ` Alejandro Colomar
2024-07-07 12:21 ` Martin Uecker
2024-07-07 13:10 ` Alejandro Colomar
2024-07-07 10:42 ` Paul Eggert
2024-07-07 12:42 ` Alejandro Colomar
2024-07-07 17:30 ` Paul Eggert
2024-07-07 22:52 ` Alejandro Colomar
2024-07-09 12:09 ` Paul Eggert
2024-07-09 17:36 ` Alejandro Colomar
2024-07-08 14:30 ` David Malcolm [this message]
2024-07-08 15:01 ` Alejandro Colomar
2024-07-08 16:05 ` Martin Uecker
2024-07-08 20:17 ` Alejandro Colomar
2024-07-09 5:58 ` Martin Uecker
2024-07-09 9:26 ` Alejandro Colomar
2024-07-08 22:48 ` David Malcolm
2024-07-09 9:07 ` Alejandro Colomar
2024-07-09 9:18 ` Jakub Jelinek
2024-07-09 10:28 ` Alejandro Colomar
2024-07-09 11:28 ` Alejandro Colomar
2024-07-09 22:42 ` n3294 - The restrict function attribute as a replacement of the restrict qualifier Alejandro Colomar
2024-07-26 16:24 ` Joseph Myers
2024-07-26 16:35 ` G. Branden Robinson
2024-07-26 19:53 ` Alejandro Colomar
2024-07-26 18:50 ` Paul Eggert
2024-07-26 20:11 ` Alejandro Colomar
2024-07-26 20:30 ` Joseph Myers
2024-07-26 21:14 ` Alejandro Colomar
2024-07-26 21:22 ` Joseph Myers
2024-07-26 21:49 ` Alejandro Colomar
2024-07-26 22:03 ` Martin Uecker
2024-07-26 22:26 ` Alejandro Colomar
2024-07-26 22:59 ` Martin Uecker
2024-07-27 8:44 ` Alejandro Colomar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e2d7b36d5716f1799ba43d1373a0a2d65cc7cb16.camel@redhat.com \
--to=dmalcolm@redhat.com \
--cc=Richard.Earnshaw@arm.com \
--cc=alx@kernel.org \
--cc=ben.boeckel@kitware.com \
--cc=eggert@cs.ucla.edu \
--cc=gcc@gcc.gnu.org \
--cc=heiko.eissfeldt@siemens.com \
--cc=jakub@redhat.com \
--cc=jwakely.gcc@gmail.com \
--cc=lh_mouse@126.com \
--cc=libc-alpha@sourceware.org \
--cc=linux-man@vger.kernel.org \
--cc=sam@gentoo.org \
--cc=uecker@tugraz.at \
--cc=xry111@xry111.site \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.