db_mutex.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*
  14. * db_mutex.c
  15. *
  16. * This file contains APIs that provide operations of (futex based) mutexes.
  17. * Based on "Mutexes and Condition Variables using Futexes"
  18. * (http://locklessinc.com/articles/mutex_cv_futex)
  19. */
  20. #include "pal_defs.h"
  21. #include "pal_linux_defs.h"
  22. #include "pal.h"
  23. #include "pal_internal.h"
  24. #include "pal_linux.h"
  25. #include "pal_linux_error.h"
  26. #include "pal_error.h"
  27. #include "pal_debug.h"
  28. #include "api.h"
  29. #include <linux/futex.h>
  30. #include <limits.h>
  31. #include <atomic.h>
  32. #include <linux/time.h>
  33. #include <errno.h>
  34. #include <asm/errno.h>
  35. #define MUTEX_SPINLOCK_TIMES 100
  36. #define MUTEX_UNLOCKED 0
  37. #define MUTEX_LOCKED 1
  38. int
  39. _DkMutexCreate (PAL_HANDLE * handle, int initialCount)
  40. {
  41. PAL_HANDLE mut = malloc(HANDLE_SIZE(mutex));
  42. SET_HANDLE_TYPE(mut, mutex);
  43. atomic_set(&mut->mutex.mut.nwaiters, 0);
  44. mut->mutex.mut.locked = malloc_untrusted(sizeof(int64_t));
  45. if (!mut->mutex.mut.locked) {
  46. free(mut);
  47. return -PAL_ERROR_NOMEM;
  48. }
  49. *(mut->mutex.mut.locked) = initialCount;
  50. *handle = mut;
  51. return 0;
  52. }
  53. int _DkMutexLockTimeout(struct mutex_handle* m, int64_t timeout_us) {
  54. int ret = 0;
  55. if (MUTEX_UNLOCKED == cmpxchg(m->locked, MUTEX_UNLOCKED, MUTEX_LOCKED))
  56. goto success;
  57. if (timeout_us == 0) {
  58. ret = -PAL_ERROR_TRYAGAIN;
  59. goto out;
  60. }
  61. // Bump up the waiters count; we are probably going to block
  62. atomic_inc(&m->nwaiters);
  63. while (MUTEX_LOCKED == cmpxchg(m->locked, MUTEX_UNLOCKED, MUTEX_LOCKED)) {
  64. /*
  65. * Chia-Che 12/7/2017: m->locked points to untrusted memory, so
  66. * can be used for futex. Potentially this design may allow
  67. * attackers to change the mutex value and cause DoS.
  68. */
  69. ret = ocall_futex((int*)m->locked, FUTEX_WAIT, MUTEX_LOCKED, timeout_us);
  70. if (IS_ERR(ret)) {
  71. if (ERRNO(ret) == EWOULDBLOCK) {
  72. ret = -PAL_ERROR_TRYAGAIN;
  73. atomic_dec(&m->nwaiters);
  74. } else {
  75. ret = unix_to_pal_error(ERRNO(ret));
  76. atomic_dec(&m->nwaiters);
  77. }
  78. goto out;
  79. }
  80. }
  81. atomic_dec(&m->nwaiters);
  82. success:
  83. ret = 0;
  84. out:
  85. return ret;
  86. }
  87. int _DkMutexLock(struct mutex_handle* m) {
  88. return _DkMutexLockTimeout(m, -1);
  89. }
  90. int _DkMutexAcquireTimeout(PAL_HANDLE handle, int64_t timeout_us) {
  91. return _DkMutexLockTimeout(&handle->mutex.mut, timeout_us);
  92. }
  93. int _DkMutexUnlock (struct mutex_handle * m)
  94. {
  95. int ret = 0;
  96. int need_wake;
  97. /* Unlock */
  98. *(m->locked) = MUTEX_UNLOCKED; // TODO: this is not atomic!
  99. /* We need to make sure the write to locked is visible to lock-ers
  100. * before we read the waiter count. */
  101. MB();
  102. need_wake= atomic_read(&m->nwaiters);
  103. /* If we need to wake someone up... */
  104. if (need_wake)
  105. ocall_futex((int *) m->locked, FUTEX_WAKE, 1, -1);
  106. return ret;
  107. }
  108. void _DkMutexRelease (PAL_HANDLE handle)
  109. {
  110. struct mutex_handle * mut = &handle->mutex.mut;
  111. int ret = _DkMutexUnlock(mut);
  112. if (ret < 0)
  113. _DkRaiseFailure(ret);
  114. return;
  115. }
  116. static int mutex_wait (PAL_HANDLE handle, int64_t timeout_us) {
  117. return _DkMutexAcquireTimeout(handle, timeout_us);
  118. }
  119. static int mutex_close (PAL_HANDLE handle)
  120. {
  121. free_untrusted((int64_t *) handle->mutex.mut.locked);
  122. return 0;
  123. }
  124. struct handle_ops mutex_ops = {
  125. .wait = &mutex_wait,
  126. .close = &mutex_close,
  127. };