Path: eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: A Famous Security Bug Date: Thu, 21 Mar 2024 12:42:29 -0700 Organization: None to speak of Lines: 85 Message-ID: <87il1f1ie2.fsf@nosuchdomain.example.com> References: <20240320114218.151@kylheku.com> <20240321211306.779b21d126e122556c34a346@gmail.moc> MIME-Version: 1.0 Content-Type: text/plain Injection-Info: dont-email.me; posting-host="0bf58c5e3e50115de10475b5e7b86fc1"; logging-data="2532317"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19guZPEpbD+tNfuRHCiNdwi" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) Cancel-Lock: sha1:UVJTjP+V4zbs4q2eDVKwKfytHBk= sha1:Qm2e65Z+EfLOLBhy5N59JMALyro= Anton Shepelev writes: > Kaz Kylheku to Stefan Ram: > >> > > A "famous security bug": >> > > >> > > void f( void ) >> > > { char buffer[ MAX ]; >> > > /* . . . */ >> > > memset( buffer, 0, sizeof( buffer )); } >> > > >> > > . Can you see what the bug is? >> >> I don't know about "the bug", but conditions can be >> identified under which that would have a problem >> executing, like MAX being in excess of available automatic >> storage. >> >> If the /*...*/ comment represents the elision of some >> security sensitive code, where the memset is intended to >> obliterate secret information, of course, that >> obliteration is not required to work. >> >> After the memset, the buffer has no next use, so the all >> the assignments performed by memset to the bytes of buffer >> are dead assignments that can be elided. >> >> To securely clear memory, you have to use a function for >> that purpose that is not susceptible to optimization. > > I think this behavior (of a C compiler) rather stupid. In a > low-level imperative language, the compiled program shall > do whatever the programmer commands it to do. If he > commands it to clear the buffer, it shall clear the buffer. > This optimisation is too high-level, too counter-inituitive, > even deceitful. The optimiser is free to perform the task > in the fastest manner possible, but it shall not ignore the > programmer's order to zero-fill the buffer, especially > without emitting a warning about (potentially!) redundant > code, which it is the programmer's reponsibility to confirm > and remove. > > Redundant code shall be dealt with in the source, rather than > in the executable. Then C is not what you call a "low-level imperative language", and none of your "shall"s apply to the language defined by the ISO C standard. C programs define behavior, not generated machine code. If an implementation can implement the behavior of a C program with a memset() call without invoking memset(), it's free to do so. The programmer's intent in the code snippet that started this thread was for the memset call to erase sensitive data in memory that, after the function returns, is not part of any object. C (prior to C23) doesn't provide a way to do that. Any program whose observable behavior differs depending on whether that memory was cleared has undefined behavior. Judicious use of "volatile" should avoid the problem. 5.1.2.3p6: The least requirements on a conforming implementation are: - Volatile accesses to objects are evaluated strictly according to the rules of the abstract machine. - At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced. - The input and output dynamics of interactive devices shall take place as specified in 7.23.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages appear prior to a program waiting for input. This is the *observable behavior* of the program. If you think that's stupid, I won't try to change your mind, but the meaning is clear. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Medtronic void Void(void) { Void(); } /* The recursive call of the void */