@@ -1766,47 +1766,85 @@ int ocall_shutdown(int sockfd, int how) {
17661766 return retval ;
17671767}
17681768
1769- int ocall_gettime (uint64_t * microsec_ptr ) {
1769+ int ocall_gettime (uint64_t * microsec_ptr , uint64_t * tsc_ptr ) {
17701770 int retval = 0 ;
1771- struct ocall_gettime * ocall_gettime_args ;
1771+ struct ocall_gettime * ocall_gettime_args = NULL ;
17721772
17731773 void * old_ustack = sgx_prepare_ustack ();
17741774 ocall_gettime_args = sgx_alloc_on_ustack_aligned (sizeof (* ocall_gettime_args ),
17751775 alignof(* ocall_gettime_args ));
17761776 if (!ocall_gettime_args ) {
1777- sgx_reset_ustack ( old_ustack ) ;
1778- return - EPERM ;
1777+ retval = - EPERM ;
1778+ goto out ;
17791779 }
17801780
17811781 /* Last seen time value. This guards against time rewinding. */
1782- static uint64_t last_microsec = 0 ;
1783- uint64_t last_microsec_before_ocall = __atomic_load_n (& last_microsec , __ATOMIC_ACQUIRE );
1782+ struct gettime_guard
1783+ {
1784+ spinlock_t lock ;
1785+ uint64_t microsec ;
1786+ uint64_t tsc ;
1787+ };
1788+ static struct gettime_guard last_value = {
1789+ .lock = INIT_SPINLOCK_UNLOCKED ,
1790+ .microsec = 0 ,
1791+ .tsc = 0 ,
1792+ };
1793+
1794+ spinlock_lock (& last_value .lock );
1795+ uint64_t last_microsec_before_ocall = last_value .microsec ;
1796+ uint64_t last_tsc_before_ocall = last_value .tsc ;
1797+ spinlock_unlock (& last_value .lock );
1798+
1799+ uint64_t tsc_before_ocall = 0 ;
1800+ uint64_t tsc_after_ocall = 0 ;
17841801 do {
1802+ tsc_before_ocall = get_tsc ();
17851803 retval = sgx_exitless_ocall (OCALL_GETTIME , ocall_gettime_args );
1804+ tsc_after_ocall = get_tsc ();
17861805 } while (retval == - EINTR );
17871806
17881807 if (retval < 0 && retval != - EINVAL && retval != - EPERM ) {
17891808 retval = - EPERM ;
17901809 }
1810+ if (retval != 0 ) {
1811+ goto out ;
1812+ }
17911813
1792- if (!retval ) {
1793- uint64_t microsec = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> microsec );
1794- if (microsec < last_microsec_before_ocall ) {
1795- /* Probably a malicious host. */
1796- log_error ("OCALL_GETTIME returned time value smaller than in the previous call" );
1797- _PalProcessExit (1 );
1798- }
1799- /* Update `last_microsec`. */
1800- uint64_t expected_microsec = last_microsec_before_ocall ;
1801- while (expected_microsec < microsec ) {
1802- if (__atomic_compare_exchange_n (& last_microsec , & expected_microsec , microsec ,
1803- /*weak=*/ true, __ATOMIC_RELEASE , __ATOMIC_ACQUIRE )) {
1804- break ;
1805- }
1806- }
1807- * microsec_ptr = MAX (microsec , expected_microsec );
1814+ /* detect malicious host - time and tsc must monotonically increase */
1815+ uint64_t new_microsec = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> microsec );
1816+ uint64_t new_tsc = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> tsc );
1817+ if (new_microsec < last_microsec_before_ocall ) {
1818+ log_error ("OCALL_GETTIME returned time value smaller than in the previous call" );
1819+ _PalProcessExit (1 );
18081820 }
1821+ if (new_tsc <= last_tsc_before_ocall ) {
1822+ log_error ("OCALL_GETTIME returned TSC value smaller than in previous call" );
1823+ _PalProcessExit (1 );
1824+ }
1825+ if (!((tsc_before_ocall < new_tsc ) && (new_tsc < tsc_after_ocall ))) {
1826+ log_error ("OCALL_GETTIME returned TSC value inconsistent with values taken within the enclave" );
1827+ _PalProcessExit (1 );
1828+ }
1829+
1830+ /* Update `last_value` guard. */
1831+ spinlock_lock (& last_value .lock );
1832+ if (last_value .tsc < new_tsc ) {
1833+ last_value .microsec = new_microsec ;
1834+ last_value .tsc = new_tsc ;
1835+ } else {
1836+ /* there was a more recent ocall */
1837+ new_microsec = last_value .microsec ;
1838+ new_tsc = last_value .tsc ;
1839+ }
1840+ spinlock_unlock (& last_value .lock );
18091841
1842+ * microsec_ptr = new_microsec ;
1843+ if (tsc_ptr != NULL ) {
1844+ * tsc_ptr = new_tsc ;
1845+ }
1846+
1847+ out :
18101848 sgx_reset_ustack (old_ustack );
18111849 return retval ;
18121850}
0 commit comments