@@ -1768,49 +1768,82 @@ int ocall_shutdown(int sockfd, int how) {
17681768
17691769int 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 = { .microsec = 0 , .tsc = 0 , };
1789+ if (FIRST_TIME ()) {
1790+ spinlock_init (& last_value .lock );
1791+ }
1792+
1793+ spinlock_lock (& last_value .lock );
1794+ uint64_t last_microsec_before_ocall = last_value .microsec ;
1795+ uint64_t last_tsc_before_ocall = last_value .tsc ;
1796+ spinlock_unlock (& last_value .lock );
1797+
1798+ uint64_t tsc_before_ocall = 0 ;
1799+ uint64_t tsc_after_ocall = 0 ;
17841800 do {
1801+ tsc_before_ocall = get_tsc ();
17851802 retval = sgx_exitless_ocall (OCALL_GETTIME , ocall_gettime_args );
1803+ tsc_after_ocall = get_tsc ();
17861804 } while (retval == - EINTR );
17871805
17881806 if (retval < 0 && retval != - EINVAL && retval != - EPERM ) {
17891807 retval = - EPERM ;
17901808 }
1791-
17921809 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- }
1810+ goto out ;
1811+ }
18071812
1808- * microsec_ptr = MAX (microsec , expected_microsec );
1809- if (tsc_ptr != NULL ) {
1810- * tsc_ptr = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> tsc );
1811- }
1813+ /* detect malicious host - time and tsc must monotonically increase */
1814+ uint64_t new_microsec = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> microsec );
1815+ uint64_t new_tsc = COPY_UNTRUSTED_VALUE (& ocall_gettime_args -> tsc );
1816+ if (new_microsec < last_microsec_before_ocall ) {
1817+ log_error ("OCALL_GETTIME returned time value smaller than in the previous call" );
1818+ _PalProcessExit (1 );
1819+ }
1820+ if (new_tsc <= last_tsc_before_ocall ) {
1821+ log_error ("OCALL_GETTIME returned TSC value smaller than in previous call" );
1822+ _PalProcessExit (1 );
1823+ }
1824+ if (!((tsc_before_ocall < new_tsc ) && (new_tsc < tsc_after_ocall ))) {
1825+ log_error ("OCALL_GETTIME returned TSC value inconsistent with values taken within the enclave" );
1826+ _PalProcessExit (1 );
18121827 }
18131828
1829+ /* Update `last_value` guard. */
1830+ spinlock_lock (& last_value .lock );
1831+ if (last_value .tsc < new_tsc ) {
1832+ last_value .microsec = new_microsec ;
1833+ last_value .tsc = new_tsc ;
1834+ } else {
1835+ /* there was a more recent ocall */
1836+ new_microsec = last_value .microsec ;
1837+ new_tsc = last_value .tsc ;
1838+ }
1839+ spinlock_unlock (& last_value .lock );
1840+
1841+ * microsec_ptr = new_microsec ;
1842+ if (tsc_ptr != NULL ) {
1843+ * tsc_ptr = new_tsc ;
1844+ }
1845+
1846+ out :
18141847 sgx_reset_ustack (old_ustack );
18151848 return retval ;
18161849}
0 commit comments