Daemonic Dispatches

Musings from Colin Percival

How to zero a buffer

In cryptographic applications, it is often useful to wipe data from memory once it is no longer needed. In a perfect world, this is unnecessary since nobody would gain unauthorized access to that data; but if someone is able to exploit an unrelated problem — a vulnerability which yields remote code execution, or a feature which allows uninitialized memory to be read remotely, for example — then ensuring that sensitive data (e.g., cryptographic keys) is no longer accessible will reduce the impact of the attack. In short, zeroing buffers which contained sensitive information is an exploit mitigation technique.

Alas, this is easier said than done. Consider the most obvious approach:

void
dosomethingsensitive(void)
{
 uint8_t key[32];
 ...
 /* Zero sensitive information. */
 memset(key, 0, sizeof(key));
}
This looks like it should zero the buffer containing the key before returning; but a "sufficiently intelligent" compiler — in this case, most of them — is allowed to recognize that key is not accessible via conforming C code after the function returns, and silently optimize away the call to memset. While this completely subverts our intention, it is perfectly legal: The observable behaviour of the program is unchanged by the optimization.

Now, we don't want to truly change the observable behaviour of our software — but fortunately the C standard has a more liberal concept of "observable" than most people. In particular, the C standard states that the observable behaviour includes accesses to volatile objects. What is a volatile object, you ask? It is an object defined with a volatile type — originally intended for memory-mapped device registers, where the mere act of reading or writing the "memory" location can have side effects. These days, the volatile keyword essentially means "you can't assume that this acts like normal memory".

This brings us to a common attempt at zeroing buffers:

void
dosomethingsensitive(void)
{
 uint8_t key[32];
 ...
 /* Zero sensitive information. */
 memset((volatile void *)key, 0, sizeof(key));
}
On most compilers this is no better: While there is a cast to a volatile type, the pointer is immediately cast back to void * since that is the type of the first parameter to memset. This may produce a warning message, but it won't prevent the optimization: The double cast will be collapsed and the compiler will recognize that it is not handling a volatile object.

A somewhat more nuanced attempt is the following:

static void
secure_memzero(void * p, size_t len)
{
 volatile uint8_t * _p = p;
 while (len--) *_p++ = 0;
}
void
dosomethingsensitive(void)
{
 uint8_t key[32];
 ...
 /* Zero sensitive information. */
 secure_memzero(key, sizeof(key));
}
This does trick a few more compilers, but it isn't guaranteed to work either: The C standard states that accesses to volatile objects are part of the unalterable observable behaviour — but it says nothing about accesses via lvalue expressions with volatile types. Consequently a sufficiently intelligent compiler can still optimize the buffer-zeroing away in this case — it just has to prove that the object being accessed was not originally defined as being volatile.

Some people will try this with secure_memzero in a separate C file. This will trick yet more compilers, but no guarantees — with link-time optimization the compiler may still discover your treachery.

Is it possible to zero a buffer and guarantee that the compiler won't optimize it away? Yes, and here's one way to do it:

static void * (* const volatile memset_ptr)(void *, int, size_t) = memset;
static void
secure_memzero(void * p, size_t len)
{
 (memset_ptr)(p, 0, len);
}
void
dosomethingsensitive(void)
{
 uint8_t key[32];
 ...
 /* Zero sensitive information. */
 secure_memzero(key, sizeof(key));
}
The trick here is the volatile function pointer memset_ptr. While we know that it points to memset and will never change, the compiler doesn't know that — and most importantly, even if it figures out that we will never change the value of the function pointer, the compiler is forbidden from assuming that the function pointer won't change on its own (since that's what volatile objects do). If the function pointer might change, it might point at a function which has side effects; and so the compiler is forced to emit the function call which causes the key buffer to be zeroed.

UPDATE 2014年09月04日: The above code is not guaranteed to work after all.

Now, I'm not the first person to look at this problem, of course, and if you're willing to limit yourself to narrow platforms, you don't need to write secure_memzero yourself: On Windows, you can use the SecureZeroMemory function, and on C11 (are there any fully C11-compliant platforms yet?) you can use the memset_s function. Both of these are guaranteed (or at least specified) to write the provided buffer and to not be optimized away.

There's just one catch: We've been solving the wrong problem.

(part 2)

Posted at 2014年09月04日 19:30 | Permanent link | Comments
View the forum thread. blog comments powered by Disqus

Recent posts

How to support FreeBSD on your cloud
Thoughts on (Amazonian) Leadership
A year of funded FreeBSD
Chunking attacks on Tarsnap (and others)
My re:Invent asks
Generalist AI doesn't scale
Please test: FreeBSD 13.3-RC1
An APPR claim with Air Canada
A Canadian payroll dependency chart
Some late-breaking FreeBSD 14 breakage

Monthly Archives

November 2025
September 2025
June 2025
March 2025
December 2024
April 2024
February 2024
January 2024
December 2023
November 2023
October 2023
October 2022
May 2022
March 2022
August 2021
June 2021
March 2021
September 2020
May 2020
January 2020
November 2019
October 2019
February 2019
December 2018
July 2018
April 2018
February 2018
January 2018
November 2017
October 2017
June 2017
May 2017
February 2017
January 2017
October 2016
April 2016
January 2016
December 2015
November 2015
September 2015
August 2015
April 2015
January 2015
December 2014
November 2014
October 2014
September 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013
September 2013
August 2013
June 2013
January 2013
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
May 2012
March 2012
January 2012
December 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
December 2010
October 2010
September 2010
August 2010
July 2010
June 2010
April 2010
March 2010
December 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
December 2007
November 2007
October 2007
September 2007
August 2007
June 2007
May 2007
March 2007
February 2007
January 2007
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
January 2006
December 2005
November 2005
October 2005
September 2005

Yearly Archives

2025
2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011
2010
2009
2008
2007
2006
2005

RSS

AltStyle によって変換されたページ (->オリジナル) /