misuse of rdtsc and registers in embedded assembly language
openSSL 1.0,0, last modified on 29 March 2010
Spotted in the function RAND_poll() in rand_nw.c
The size of the variable tsc used in a asm parameter (unsigned long) is not
compatible with the edx:eax registers as modified by the rdtsc instruction.
rdtsc is a read timestamp counter, a feature of the IA instruction set. this
instruction write the least significant part of a 64 bit counter into register
eax, and the most significant part into register edx.
There is a lovely potential register mismatch issue, which can be reproduced
easily with the following setup
using gcc 3.3.3 (SuSe linux)
When compiled with gcc –O0 to gcc –O2, register eax is used
When compiled with gcc –O3 and higher, register edx is used
int RAND_poll(void)
{
unsigned long l;
unsigned long tsc; <----------------------- 32 bit
value
int i;
/* There are several options to gather miscellaneous data
* but for now we will loop checking the time stamp counter (rdtsc) and
* the SuperHighResolutionTimer. Each iteration will collect 8 bytes
* of data but it is treated as only 1 byte of entropy. The call to
* ThreadSwitchWithDelay() will introduce additional variability into
* the data returned by rdtsc.
*
* Applications can agument the seed material by adding additional
* stuff with RAND_add() and should probably do so.
*/
l = GetProcessSwitchCount();
RAND_add(&l,sizeof(l),1);
/* need to cast the void* to unsigned long here */
l = (unsigned long)RunningProcess;
RAND_add(&l,sizeof(l),1);
for( i=2; i<ENTROPY_NEEDED; i++)
{
#ifdef __MWERKS__
asm
{
rdtsc
mov tsc, eax
}
#else
asm volatile("rdtsc":"=A" (tsc));
// <--- BUG HERE
#endif
RAND_add(&tsc, sizeof(tsc), 1);
l = GetSuperHighResolutionTimer();
RAND_add(&l, sizeof(l), 0);
}
}
The following code shows the disassembly of the loop, with the tiny funny
detail after rdtsc instruction and register misuse.
gcc –O2
movl $17, %ebx
leal 8(%esp), %esi
.p2align 4,,7
.L6:
#APP
rdtsc
#NO_APP
movl %eax, 8(%esp) <---
EAX: incremented every IA cycle
pushl $1072693248
pushl $0
pushl $4
pushl %esi
call RAND_add
call GetSuperHighResolutionTimer
movl %eax, 28(%esp)
pushl $0
pushl $0
pushl $4
pushl %edi
call RAND_add
addl $32, %esp
decl %ebx
jns .L6
gcc –O3
movl $17, %ebx
leal 8(%esp), %esi
.p2align 4,,7
.L6:
#APP
rdtsc
#NO_APP
movl %edx, 8(%esp) <- EDX: not the same register,
incremented every 2^32 cycles
pushl $1072693248
pushl $0
pushl $4
pushl %esi
call RAND_add
call GetSuperHighResolutionTimer
movl %eax, 28(%esp)
pushl $0
pushl $0
pushl $4
pushl %edi
call RAND_add
addl $32, %esp
decl %ebx
jns .L6
The potential fix is probably one of these
- to declare tsc as "unsigned long long" (enforce a 64 bit value),
- to remove the ambiguous register usage after rdtsc, as follows asm
volatile("rdtsc":"=ra" (tsc)::”edx”); (keep tsc as a 32 bit value)
The impact of the timestamp counter not being incremented at all into this loop
has probably some consequence on the pseudo-randomness generated by this
function.
Regards
______________________________________________________________________
OpenSSL Project http://www.openssl.org
Development Mailing List [email protected]
Automated List Manager [email protected]