Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: sizeof object got by dereferencing uniniitalized pointer Date: Sun, 01 Dec 2019 10:28:06 -0800 Organization: A noiseless patient Spider Lines: 134 Message-ID: <86y2vv6guh.fsf@linuxsc.com> References: <21e377ab-2a49-4f7e-bc18-72f1cee63204@googlegroups.com> <9d5ee6d4-607b-405f-83d9-afbe2b073cac@googlegroups.com> <76a74489-4a1f-4561-a01b-b2962a10bae4@googlegroups.com> <2119c5dc-fc01-4a74-93f4-8027c7cc210e@googlegroups.com> <43093894-ed52-47de-85f4-eadd92a1499e@googlegroups.com> <877e3j52do.fsf@bsb.me.uk> <86eexr8qlh.fsf@linuxsc.com> <87v9r33211.fsf@bsb.me.uk> <86k17i7kuh.fsf@linuxsc.com> <87pnha2vhx.fsf@bsb.me.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="34b550890f9780b4d7a954ae5f416877"; logging-data="28570"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18vx5nKjyvtyXJLhn222Sw8KOdeaWJcUh8=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:fhdOoCat6cMAmy5NXMIfUelWS6k= sha1:7UqeWbvAeYs4VIjv2oVLDTR8dPc= Xref: csiph.com comp.lang.c:148331 Ben Bacarisse writes: > Tim Rentsch writes: > >> Ben Bacarisse writes: >> >>> Tim Rentsch writes: >>> >>>> Ben Bacarisse writes: >>>> >>>>> Thiago Adams writes: >>>>> >>>>> >>>>>> Only when applied to variably types, sizeof is in runtime. >>>>>> >>>>>> int n = 10; >>>>>> >>>>>> int(*buf)[n] = malloc(sizeof(int) * n); >>>>> >>>>> Why would you not write >>>>> >>>>> int (*buf)[n] = malloc(sizof *buf); >>>>> >>>>> ? >>>> >>>> Perhaps because, technically speaking, the right-hand side here >>>> has undefined behavior? >>> >>> Yes, it has. That has always puzzled me. Do you think that this usage >>> was specifically being ruled out, or is it a casualty of having made an >>> overly simple decision[1]? >> >> I have now looked into this further and can offer a more informed >> answer. More below. >> >>> This whole areas makes me uneasy. I've never been able to find a >>> reasonable example where an expression operand of sizeof needs to be >>> evaluated[2] (as the standard mandates) but most compilers evaluate f in >>> >>> sizeof (int[f()]) >>> >>> despite the standard not explaining what it means to evaluate a type >>> (though its not hard to guess what is intended). Do the rules make >>> sense to you? [I note your comment about _Generic_ but taking >>> it out because it doesn't bear on what I have to say.] >> >> The rules now make sense to me. The authors wanted to be sure (or >> so I believe, in case that needs saying) that they didn't miss any >> corner cases, and so used general wording. > > I didn't say what I meant, sorry. I wanted to ask if you think the > rules are the right ones. I feel the sacrifice (to undefined behaviour) > of patterns like > > memset(ptr, 0, sizeof *ptr); > > to be unfortunate. Mind you, VLAs, and the even more useful variably > modified types, have been thrown under the bus anyway. If what you're asking is, do I think it would be an improvement to have a different rule that allows more of the typical cases to be evaluation free and so avoid any possibility of undefined behavior, the answer is Yes. >> They did think about >> needless evaluation in 'sizeof' operands, and the Standard does >> have a provision which talks about that; in fact that provision >> evolved while C99 was being written. > > I would have argued for a limited form of evaluation -- evaluation > sufficient to determine the type. I think the rules would not be too > complicated, and it would be the natural place to specify what > "evaluating" a type name means. I think I would propose a different approach, namely, a rule that is both easy to state and easy to test without any deep thinking, and that covers the usual patterns without any possibility of undefined behavior. Without thinking about it too deeply, I might suggest that operands of sizeof are not evaluated unless the type is a variable length array type and the operand expression has at least one 'type-name' in it that declares a variably modified type (perhaps with a footnote that says variable length array types may involve some run-time computation to produce the value but the expression is not evaluated in the usual C sense unless it satisfies the aforementioned 'type-name' criterion).[*] If this works I think it would be a reasonable candidate, especially if I had a time machine so I could go back and get it into C99, and similarly go back and manage to get VLAs out from under the bus where they were so unceremoniously thrown. [*] Ideally with much better phrasing than that abomination of a sentence. >> (If anyone cares they might >> want to compare n869 to later versions of the C99 standard.) >> >> Now, I hear you ask, _what_ corner cases? Here is one, taken from >> my recent reponse to Keith Thompson: >> >> int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something }; >> >> This declaration is legal C99, provided of course there are >> suitable declarations for the identifiers 'something' and 'ident' >> in scope. The operand is an expression, not a type name, although >> of course the expression does have a type name within it. The >> operand expression needs to be evaluated, at least in part, in >> order to determine the result of the sizeof operator. > > The same effect can be achieved without a compound literal by using a > cast. Is there a subtle point I'm missing that relies on the CL? Not that I know of, I think casts work fine too for this purpose. Using a compound literal is just the first example that popped into my head. >> Rather than >> concoct some carefully chosen language that does some sort of >> partial evalution for just those subexpressions that are needed >> to produce the result, the authors chose the simple rule that >> any VLA operand argument is evaluated. That decision has some >> unfortunate consequences for undefined behavior, but in the 1990s >> there wasn't as much hysteria regarding undefined behavior as >> there is now. > > Maybe " evaluation sufficient to determine the type" is more > complicated than I imagine. I wonder if it was every considered. I think the main thing is this formulation is more complicated than it needs to be. Also it seems prone to interpretation arguments, much like the infamous pre-C11 "the prior value shall be read only to determine the value to be stored." Even if that is unambiguous, it isn't always obvious what is implied. Here I think having the boundaries be immediately evident is a decidely worthwhile property.