Did you *really* zeroize that key?

Don Davis dtd at world.std.com
Thu Nov 7 23:41:34 EST 2002


At 3:07 PM +1300 11/7/02, Peter Gutmann wrote:
>> [Moderator's note: FYI: no "pragma" is needed.
>> This is what C's "volatile" keyword is for. 
>
> No it isn't.  This was done to death on vuln-dev,
> see the list archives for the discussion.
>
> [Moderator's note: I'd be curious to hear a summary --
> it appears to work fine on the compilers I've tested.
>                                           --Perry]

i include below two parts:  a summary of the vuln-dev
thread, and a compiler jock's explanation of why peter's
#pragma is the _only_ solution that reliably will work.

				- don davis, boston


vuln-dev thread:
   http://online.securityfocus.com/archive/82/298061/2002-10-28/2002-11-03/1
   (thanks to tim fredenburg sending this URL to me.)

summary:  programmers can obstruct dead-code elimination
in various ways:
   - use the volatile attribute (but correctly);
   o introduce dynamic dependency;
   + do the memset with an external call.
punchline:  the subtler or newer the obstruction,
the less likely we are to see that _all_ compilers
treat the obstruction correctly.  the safest route
is to code with obstructions that have long been
known to obstruct dead-code elimination.  hence,
wrapping memset() in an external routine is most
likely to work with various buggy compilers.

synopsis: 
 * peter posted the same message as he posted to
   the cryptography list, appealing for new support
   from the compilers;
   * syzop said, "didn't happen w/ gcc 2.95.4";
   * michael wojcik suggested:
     define an external call that does memset's job,
     so as to defeat dead-code elimination 
   * dan kaminsky suggested: introduce dynamic [runtime]
     dependencies;
   * dom de vitto said, "use the volatile attribute";
     * kaminsky replied:  compilers are more likely
       to reliably respect dynamic dependency, than
       to correctly support the volatile attribute;
     * pavel kankovsky replied, "volatile" is mandatory
       in the standard, so it's ok to trust it;
     * peter also replied to kaminsky:  the dead-code
       elimination problem seems specific to gcc 3.x .
       the underlying problem is unreliable support for
       standard features and for standards compliance.  
     * michael wojcik explains (to peter, pavel, and
       kaminsky) why "volatile" isn't as good as his
       external call:
         - "passing a volatile object to memset
            invokes undefined behavior"
         - "access to volatile objects may be
            significantly slowed" 
         - "volatile seems like the sort of thing
            broken implementations may get wrong"
       michael also argues that more compiler support
       isn't necessary, since the standard provides
       effective features.

<end of synopsis/summary>

------------------------

since i used to build compilers long ago, before i got
into security work, i asked an expert friend (32 yrs of
compiler development) about what he thought of this
problem, and of the proposed solutions.  this guy, btw,
was the lead engineer for digital/compaq's fx32! runtime
binary translator for the alpha workstations, & he knows
a lot about optimizers.  he says that of the four
proposed solutions -

   * #pragma dont_remove_this_code_you_bastard;
   * use the volatile attribute (but correctly);
   * introduce dynamic dependency;
   * do the memset with an external call;

- only peter's pragma can be expected to work reliably:

   * the c99 standard and its predecessors don't
     at all intend "volatile" to mean what we naively
     think it means.  specifically, in the hands of a
     high-end compiler developer, the spec's statement:
        "any expression referring to [a volatile]
         object shall be evaluated strictly according
         to the rules of the abstract machine"
     is really talking about what the compiler can
     infer about the program's intended semantics.
     a c99-compliant compiler _can_ legitimately
     remove a volatile access, as long as the compiler
     can deduce that the removal won't affect the
     "program's result."  here, "the program's result"
     is defined by the compiler's sense of what the
     "abstract machine" is:  the abstract machine
     is mostly defined by the language features, but
     can also take into account whether a debugger
     or specialized hardware are running during
     compilation & or runtime execution.

     for example, such a savvy compiler might leave
     our volatile-memory memset() call in place when
     the debugger is running (knowing that the debug-
     ger might want to view the zeroed key). but then,
     when the debugger is turned off, the same compiler
     could decide to remove the "dead" memset() call,
     because this won't affect the program's results.

   * standards-compliant compilers normally distinguish
     between "conformant" source programs and "noncon-
     formant" source programs.  for example, a noncon-
     formant program might be one that uses a deprecated
     feature.  with nonconformant source programs, the
     compiler can perfectly legitimately bend various
     compilation rules, especially so as to get better
     optimization results.   the idea is that the spec's
     strict rules and semantics only make sense for
     conformant programs.  so, in the case of "volatile,"
     a compiler won't necessarily be bound by the "rules
     of the abstract machine," unless the source program
     strictly conforms to the language spec's "best
     practice" definition of how a C/C++ program ought
     to look. 

   * with the most modern dynamic compilation techniques
     (my friend's specialty), the compiler re-examines
     & re-optimizes the executable program _at_runtime_ ,
     so external references and dynamic dependencies
     aren't intrinsic obstacles to code-motion during
     optimization, anymore.

   * finally, my friend gives the example of a compiler
     that might decide to make a copy of our key buffer
     at runtime, in pursuit of some optimization.  the
     compiler might have the program zeroize one copy of
     the key, but not the other copy.  as long as the
     program's end result turns out to be "correct,"
     such a bizarre trick can still fulfill the language
     spec.

				- don davis, boston








-

---------------------------------------------------------------------
The Cryptography Mailing List
Unsubscribe by sending "unsubscribe cryptography" to majordomo at wasabisystems.com



More information about the cryptography mailing list