Commit message: Check untrusted queue is in outside enclave

PiperOrigin-RevId: 333370935
Change-Id: Ic3f15d5db1302d95c7cb199b44172474fecb81ca
CVE: CVE-2020-8937
CWE: CWE-787
Vulnerability description: An arbitrary memory overwrite vulnerability in Asylo versions up to 0.6.0 allows an attacker to make a host call to enc_untrusted_create_wait_queue that uses a pointer queue that relies on UntrustedLocalMemcpy, which fails to validate where the pointer is located. This allows an attacker to write memory values from within the enclave. We recommend upgrading past commit a37fb6a0e7daf30134dbbf357c9a518a1026aa02

Project: asylo
File: concurrency.cc

void enc_untrusted_enable_waiting(int32_t *const queue) {
  enc_untrusted_wait_queue_set_value(queue, kWaitQueueEnabled);
}

Project: asylo
File: concurrency.cc

int32_t *enc_untrusted_create_wait_queue() {
  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(sizeof(int32_t));
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kLocalLifetimeAllocHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_create_wait_queue",
                           2);
  int32_t *queue = reinterpret_cast<int32_t *>(output.next<uintptr_t>());
  if (!TrustedPrimitives::IsOutsideEnclave(queue, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_create_wait_queue: queue should be in untrusted memory");
  }
  int klinux_errno = output.next<int>();
  if (queue == nullptr) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  enc_untrusted_disable_waiting(queue);
  return queue;
}

Project: asylo
File: concurrency.cc

void enc_untrusted_disable_waiting(int32_t *const queue) {
  enc_untrusted_wait_queue_set_value(queue, kWaitQueueDisabled);
}

Project: asylo
File: concurrency.cc

int enc_untrusted_sys_futex_wake(int32_t *futex, int32_t num) {
  if (!TrustedPrimitives::IsOutsideEnclave(futex, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_sys_futex_wake: futex word should be in untrusted "
        "local memory.");
  }

  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(reinterpret_cast<uint64_t>(futex));
  input.Push<int32_t>(num);
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kSysFutexWakeHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_sys_futex_wake", 2);
  int result = output.next<int>();
  int klinux_errno = output.next<int>();
  if (result == -1) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  return result;
}

Project: asylo
File: concurrency.cc

void enc_untrusted_thread_wait_value(int32_t *const queue, int32_t value,
                                     uint64_t timeout_microsec) {
  enc_untrusted_sys_futex_wait(queue, value, timeout_microsec);
}

Project: asylo
File: concurrency.cc

void enc_untrusted_notify(int32_t *const queue, int32_t num_threads) {
  enc_untrusted_sys_futex_wake(queue, num_threads);
}

Project: asylo
File: concurrency.cc

void enc_untrusted_thread_wait(int32_t *const queue,
                               uint64_t timeout_microsec) {
  enc_untrusted_thread_wait_value(queue, kWaitQueueEnabled, timeout_microsec);
}

Project: asylo
File: concurrency.cc

void enc_untrusted_destroy_wait_queue(int32_t *const queue) {
  // This is a no op on purpose. Wait queue pointers are now
  // registered to be freed on enclave exit.
}

Project: asylo
File: concurrency.cc

int enc_untrusted_sys_futex_wait(int32_t *futex, int32_t expected,
                                 int64_t timeout_microsec) {
  if (!TrustedPrimitives::IsOutsideEnclave(futex, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_sys_futex_wait: futex word should be in untrusted "
        "local memory.");
  }

  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(reinterpret_cast<uint64_t>(futex));
  input.Push<int32_t>(expected);
  input.Push<int64_t>(timeout_microsec);
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kSysFutexWaitHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_sys_futex_wait", 2);
  int result = output.next<int>();
  int klinux_errno = output.next<int>();

  // If FUTEX_WAIT successfully causes the thread to be suspended in the kernel,
  // it returns a zero when the caller is woken up. Otherwise, it returns the
  // appropriate errno.
  if (result != 0) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  return result;
}

Project: asylo
File: concurrency.cc

void enc_untrusted_wait_queue_set_value(int32_t *const queue, int32_t value) {
  TrustedPrimitives::UntrustedLocalMemcpy(queue, &value, sizeof(int32_t));
}