Skip to content

Commit aa8f0aa

Browse files
committed
[PAL/LinuxSGX] protect from both time and tsc rewind in ocall_gettime
Signed-off-by: Jonathan Shamir <[email protected]>
1 parent e8f263a commit aa8f0aa

File tree

1 file changed

+57
-24
lines changed

1 file changed

+57
-24
lines changed

pal/src/host/linux-sgx/enclave_ocalls.c

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,49 +1768,82 @@ int ocall_shutdown(int sockfd, int how) {
17681768

17691769
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 = { .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

Comments
 (0)