Sujet : Re: A Famous Security Bug
De : Keith.S.Thompson+u (at) *nospam* gmail.com (Keith Thompson)
Groupes : comp.lang.cDate : 21. Mar 2024, 20:42:29
Autres entêtes
Organisation : None to speak of
Message-ID : <87il1f1ie2.fsf@nosuchdomain.example.com>
References : 1 2 3
User-Agent : Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux)
Anton Shepelev <
anton.txt@gmail.moc> 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.comWorking, but not speaking, for Medtronicvoid Void(void) { Void(); } /* The recursive call of the void */