[PATCH] stdmem: free: only call _gcry_secmem_free if needed
Jussi Kivilinna
jussi.kivilinna at iki.fi
Tue Oct 30 17:50:30 CET 2018
Hello,
On 29.10.2018 15.37, Werner Koch wrote:
> Hi!
>
> Thanks for this patch. I would even consider this for backporting.
>
>> - else if (!_gcry_secmem_free (p))
>> +
>> + if (!_gcry_private_is_secure (freep) ||
>> + !_gcry_secmem_free (freep))
>
> Not related to the above change but in general I think it is important
> to add least add more comments to the code to warn about the assumptions
> we make.
Ok, I'll add some comments.
>
> In particular when we allocate a new overflow pool in the secure memory
> we do this (in a locked state):
>
> --8<---------------cut here---------------start------------->8---
> /* Take care: in _gcry_private_is_secure we do not lock and thus
> * we assume that the second assignment below is atomic. */
> pool->next = mainpool.next;
> mainpool.next = pool;
> --8<---------------cut here---------------end--------------->8---
>
> MAINPOOL is never changed and thus as long the last assignment is atomic
> we should always be able to call:
>
> --8<---------------cut here---------------start------------->8---
> /* Return true if P points into the secure memory areas. */
> int
> _gcry_private_is_secure (const void *p)
> {
> pooldesc_t *pool;
>
> /* We do no lock here because once a pool is allocated it will not
> * be removed anymore (except for gcry_secmem_term). Further,
> * adding a new pool to the list should be atomic. */
> for (pool = &mainpool; pool; pool = pool->next)
> if (pool->okay && ptr_into_pool_p (pool, p))
> return 1;
>
> return 0;
> }
> --8<---------------cut here---------------end--------------->8---
>
> Do we need to think about memory visibility here? Are there case where
> we pass a P which has been allocated in secure memory but the test does
> not catch it because the thread in the second function and the one in
> the first have a different view of the memory? Bypassing locking or
> having no guaranteed memory barrier is a difficult thing. Just asking.
>
I do see one problem. The two stores when adding new overflow pool might
get re-ordered:
1> reg = mainpool.next;
2> pool->next = reg;
3> mainpool.next = pool;
to
1> reg = mainpool.next;
3> mainpool.next = pool;
2> pool->next = reg;
Now _gcry_private_is_secure might see load 3 but not 2, and exit loop
too early (pool->next == NULL, should see pool->next == old mainpool.next).
I think adding memory barrier before setting mainpool.next is enough to
prevent this. That ensures that thread in _gcry_private_is_secure sees
new pool structure properly initialized.
1> reg = mainpool.next;
2> pool->next = reg;
memory_barrier ();
3> mainpool.next = pool;
-Jussi
More information about the Gcrypt-devel
mailing list