Consider this pseudocode:
x = read(memory_location_of_os_where_secret_lies) // will cause exception
y = arr[x *4096] // read some other local memory based on x
Although, the first line is an exception and it should never have read the Operating System (OS) memory. Due to an optimization where instructions are broken in micro operations, the x contains the value of the sacred OS memory for just a small fraction of time before the system finds out that this is an illegal access and purges/deletes/clears the value of x.
Meanwhile, believe me or not, the next line was ready to execute and it also executed before the OS could clear out the value of x and an exception was raised. So, if the value of x was ‘s’, then memory arr[‘s’ * 4096] would be accessed by the CPU.
The value of y will also be cleared very soon after x was cleared, so an attacker can no longer read x or y. To know the value of x we will check the cache lines and somehow guess what x would have been. Subsequent access to arr[‘s’ * 4096] address will activate a particular bit in cache hit. By checking which address was ‘hot’ among all cache address we can find the value of ‘s’ * 4096. Then we do simple math from that and get ‘s’. Next, we do that again and again and get ‘e’, ‘c’,’r’,’e’,’t’.