Bug 8537 - Optimizer Removes Code Necessary for Security
Summary: Optimizer Removes Code Necessary for Security
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 3.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2002-11-11 19:26 UTC by wagnerjd
Modified: 2019-06-15 00:49 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description wagnerjd 2002-11-11 19:26:01 UTC
When optimizing code for "dead store removal" the optimizing compiler may remove code necessary for security.

For example:

// Beginning of Example Code

#include <string>
using std::string;

#include <memory>

// The specifics of this function are
// not important for demonstrating this bug.
const string getPasswordFromUser() const;

bool isPasswordCorrect() {
	bool isPasswordCorrect = false;
	string Password("password");

	if(Password == getPasswordFromUser()) {
		isPasswordCorrect = true;
	}

	// This line is removed from the optimized code
	// even though it secures the code by wiping
	// the password from memory.
	memset(Password, 0, sizeof(Password));

	return isPasswordCorrect;
}

// End of Example Code

It's considered good practice to remove sensitive data from memory when it's no long needed so that the sensitive data doesn't accidentally end up in the swap file, temp file, memory dump file, etc.

However, in the above example, the optimizing compiler removes the "memset" function as part of "dead store removal" optimization.  The optimizing compiler realizes that "memset" writes to "Password" but "Password" is never again read; hence, it is removed as part of the "dead store removal" optimization.

A programmer could erroneously think that his code is secure, even though the securing code -- "memset" -- is removed from the compiled code.

Release:
GCC-3.2

Environment:
i386
but most likely applicable to all environments

How-To-Repeat:
Any code where "memset" is the last function to affect a sensitive variable/data would be affected by this problem.
Comment 1 wagnerjd 2002-11-11 19:26:01 UTC
Fix:
Two fixes exist from this problem: a workaround and a permanent solution.

WORKAROUND: Read "Password" after it has been wiped from memory.

For example, insert this line after "memset":

// Beginning of Example Code

*(volatile char *)Password = *(volatile char *)Password;

// End of Example Code

This will do nothing, but it will result in reading the "Password" variable, signaling to the optimizing compiler that "memset" should not be "dead store removal" optimized out.

PERMANENT SOLUTION: The WORKAROUND uses both memory and execution time.  A better solution would be to turn off optimization for that part of the code.

However, to the best of my knowledge, GCC does not support altering optimization options on-the-fly though preprocessor statements.  (If it does, will someone please provide me with a link to the appropriate documentation?  I was unable to find any documentation showing that GCC supports this feature.)

For example, altering optimization options on-the-fly through preprocessor statements could be implemented this way:

// Beginning of Example Code

#pragma optimize("-no-dead-code-removal")
memset(Password, 0, sizeof(Password));
#pragma optimize("-dead-code-removal")

// End of Example Code

There's a dozen different ways to accomplish this, but to the best of my understanding and knowledge, the PERMANENT SOLUTION has to be implemented as a new feature (if such a feature doesn't already exist).
Comment 2 Florian Weimer 2002-11-17 06:28:05 UTC
State-Changed-From-To: open->closed
State-Changed-Why: This is not a bug in GCC.  The call to memset() clear hasn't any externally visible effect according to the C language specification, so it can be removed by the optimizer.
    
    See the discussion around http://gcc.gnu.org/ml/gcc/2002-01/msg00518.html for additional information on a very similar topic.
Comment 3 wagnerjd 2002-11-17 08:59:53 UTC
From: "Joseph D. Wagner" <wagnerjd@prodigy.net>
To: <fw@gcc.gnu.org>,
	<gcc-bugs@gcc.gnu.org>,
	<gcc-prs@gcc.gnu.org>,
	<nobody@gcc.gnu.org>,
	<wagnerjd@prodigy.net>,
	<gcc-gnats@gcc.gnu.org>
Cc:  
Subject: RE: optimization/8537: Optimizer Removes Code Necessary for Security
Date: Sun, 17 Nov 2002 08:59:53 -0600

 Direct quote from:
 http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Bug-Criteria.html
 
 "If the compiler produces valid assembly code that does not correctly
 execute the input source code, that is a compiler bug."
 
 So to all you naysayers out there who claim this is a programming error
 or poor coding, YES, IT IS A BUG!
 
 From:
 http://gcc.gnu.org/ml/gcc/2002-01/msg00518.html
 
 > The problem is the standard gives wide latitude in what the optimizer
 can optimize
 
 Isn't this also the solution?  Can't the optimizer check to see if the
 function is memset(), and if so check to see if the value is 0 or NULL,
 and if so leave it in?
 
 The optimizer could check if memset() is zeroing out memory by checking
 if the fill value is 0 or NULL.  If the fill value is 0 or NULL, the
 optimizer can reasonably presume that this instance of memset() is for
 security and not optimize it out.
 
 Joseph Wagner
 
 -----Original Message-----
 From: fw@gcc.gnu.org [mailto:fw@gcc.gnu.org] 
 Sent: Sunday, November 17, 2002 8:28 AM
 To: gcc-bugs@gcc.gnu.org; gcc-prs@gcc.gnu.org; nobody@gcc.gnu.org;
 wagnerjd@prodigy.net
 Subject: Re: optimization/8537: Optimizer Removes Code Necessary for
 Security
 
 Synopsis: Optimizer Removes Code Necessary for Security
 
 State-Changed-From-To: open->closed
 State-Changed-By: fw
 State-Changed-When: Sun Nov 17 06:28:05 2002
 State-Changed-Why:
     This is not a bug in GCC.  The call to memset() clear hasn't any
 externally visible effect according to the C language specification, so
 it can be removed by the optimizer.
     
     See the discussion around
 http://gcc.gnu.org/ml/gcc/2002-01/msg00518.html for additional
 information on a very similar topic.
     
 
 http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=g
 cc&pr=8537
 

Comment 4 Florian Weimer 2002-11-17 16:27:12 UTC
From: Florian Weimer <fw@deneb.enyo.de>
To: "Joseph D. Wagner" <wagnerjd@prodigy.net>
Cc: <gcc-bugs@gcc.gnu.org>, <gcc-gnats@gcc.gnu.org>
Subject: Re: optimization/8537: Optimizer Removes Code Necessary for
 Security
Date: Sun, 17 Nov 2002 16:27:12 +0100

 "Joseph D. Wagner" <wagnerjd@prodigy.net> writes:
 
 > Direct quote from:
 > http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Bug-Criteria.html
 >
 > "If the compiler produces valid assembly code that does not correctly
 > execute the input source code, that is a compiler bug."
 
 In this case, "correctly" means "correctly according to ISO 9899 and
 the GCC documentation", not just "as expected".
 
 > So to all you naysayers out there who claim this is a programming error
 > or poor coding, YES, IT IS A BUG!
 
 It would be a bug if GCC would implement Joseph D. Wagner's
 Imaginative Version Of C, but the GNU C compiler implements a
 different programming language, I'm afraid.
 
 Just because it's unexpected to you and a few others, it's not a bug
 automatically.
 
 >> The problem is the standard gives wide latitude in what the optimizer
 >> can optimize
 >
 > Isn't this also the solution?
 
 Solution to which problem?  Of course you can special-case this
 particular instance in the optimizer, but this isn't a good idea.
 There's already enough bloat in GCC.
 
 >  Can't the optimizer check to see if the function is memset(), and
 > if so check to see if the value is 0 or NULL, and if so leave it in?
 
 This only solves one particular incarnation of the more general
 problem.  Currently, when you have scrubbing requirements, you have to
 inspect the object code anyway, even if any of the changes to GCC
 suggested so far were made.  There is no way to tell the compiler,
 "this data is critical, don't make any copies of it".
 
 Anyway, correct scrubbing is only a very weak form of protection and
 prone to race conditions in multi-tasking environments.  Although one
 of the most widely used operating systems doesn't do any scrubbing on
 the operating system level, this is hardly a problem we want to and
 can fix in GCC.
Comment 5 Florian Weimer 2015-08-26 19:07:12 UTC
Setting the correct resolution.