C++ Logo

std-proposals

Advanced search

Re: Another approach to p1315r5 (secure_clear)

From: Thiago Macieira <thiago_at_[hidden]>
Date: Sun, 16 Aug 2020 14:02:21 -0700
On Sunday, 16 August 2020 10:19:02 PDT Marcin Jaczewski via Std-Proposals
wrote:
> One possible way to solve the problem with this is to use `volatile`
> semantic, this means each write/read is guaranteed.
>
> I was thinking about different solution that do the opposite, this
> means you are not guaranteed any writes or reads to memory, this is at
> first sight insane, but if your password was never in that memory you
> do not clean anything, isn't it?
>
> From C++ perspective given memory area was never written to or if it
> was, then it will be overwritten by zeros or random value after work
> is done. If it is kept in the register then no operation is needed
> expert cleaning this register with new value.

We had a keyword to ask the compiler to keep a variable exclusively in
registers. It was "register". It's still reserved, but deprecated in current
C++. Like inline, it did not control what it nominally said it did.

But more importantly, if the variable is not kept in memory, where is it kept?
Before we can even consider this in C++, we need a survey of whether it is
possible in the relevant ABIs and what implications there would be because of
it.

Registers are in limited supply and have limited sizes. On an older
architecture like x86, there are 7 really-general purpose 32-bit registers. If
you wanted to save a password in them, a reasonably secure one today (9 to 12
characters) would require 3 registers. A 128-bit AES key would require 4. A
256-bit key would be impossible to store.

Let's take your example:

> void f(int i)
> {
> __secret auto j = i; //keyword, attribute, or special class?
> foo();
> bar(j);
> }
> ```
> should have same assembly like
> ```
> void f(int i)
> {
> foo();
> bar(i);
> }

Ah, no.

The problem with a variable kept in registers before a function call is that
there are only two types of registers: caller-saved (scratch) and callee-saved
(preserved). In your example, you didn't show f() saving it, so it stands to
reason it was in a callee-saved register. That means foo() may have spilled it
to memory before reloading prior to return.

In order for this to even be possible, the use of such a variable would need
to require that all called functions be inline and inlined. The full code
generation must be visible to the compiler at that point. That could lead to
further problems with cryptographic code, such as side-channel attacks, caused
by the fact that the compiler may try to propagate constants where it
shouldn't.


> void f(int i)
> {
> foo(); //write can be reordered
> int j = i; //in sense it is stack variable, and optializer can't remove
> it bar(j);
> j = 0;
> }
> ```

This example is not useful. If the variable j is exactly identical to i and i
was saved to memory before the call to foo(), what's the point?

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel DPG Cloud Engineering

Received on 2020-08-16 16:05:49