CVE-2018-20742 (published 2019-01-24T05:29Z)

An issue was discovered in UC Berkeley RISE Opaque before 2018-12-01. There is no boundary check on ocall_malloc. The return value could be a pointer to enclave memory. It could cause an arbitrary enclave memory write.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/ucbrise/opaque
commit_details: repo_url: https://github.com/ucbrise/opaque
author: Ankur Dave
author_date: 2018-12-01 01:11:22-08:00
msg: Check that ecall [user_check] pointers and ocall_malloc result pointer are outside enclave (#67)

This should reduce the enclave's attack surface by preventing an attacker from invoking ecalls on or triggering unexpected writes to arbitrary enclave memory, which could potentially leak information about that memory or lead to incorrect results.

Fixes #36. Fixes #66.
repository: repo_url: https://github.com/ucbrise/opaque
repo_name: mc2-project/opaque-sql
description: An encrypted data analytics platform
repo_language: Scala
owner: ucbrise
file_changes: filename: App.cpp
change_type: ModificationType.MODIFY
diff: @@ -357,7 +357,7 @@ void ocall_print_string(const char *str)
   fflush(stdout);
 }
 
-void ocall_malloc(size_t size, uint8_t **ret) {
+void unsafe_ocall_malloc(size_t size, uint8_t **ret) {
   *ret = static_cast<uint8_t *>(malloc(size));
 }
 

programming_language: C
filename: Enclave.cpp
change_type: ModificationType.MODIFY
diff: @@ -10,6 +10,7 @@
 #include "Project.h"
 #include "Sort.h"
 #include "isv_enclave.h"
+#include "sgx_lfence.h"
 #include "util.h"
 
 // This file contains definitions of the ecalls declared in Enclave.edl. Errors originating within
@@ -19,6 +20,11 @@
 
 void ecall_encrypt(uint8_t *plaintext, uint32_t plaintext_length,
                    uint8_t *ciphertext, uint32_t cipher_length) {
+  // Guard against encrypting or overwriting enclave memory
+  assert(sgx_is_outside_enclave(plaintext, plaintext_length) == 1);
+  assert(sgx_is_outside_enclave(ciphertext, cipher_length) == 1);
+  sgx_lfence();
+
   try {
     // IV (12 bytes) + ciphertext + mac (16 bytes)
     assert(cipher_length >= plaintext_length + SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE);
@@ -33,6 +39,10 @@ void ecall_encrypt(uint8_t *plaintext, uint32_t plaintext_length,
 void ecall_project(uint8_t *condition, size_t condition_length,
                    uint8_t *input_rows, size_t input_rows_length,
                    uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     project(condition, condition_length,
             input_rows, input_rows_length,
@@ -45,6 +55,10 @@ void ecall_project(uint8_t *condition, size_t condition_length,
 void ecall_filter(uint8_t *condition, size_t condition_length,
                   uint8_t *input_rows, size_t input_rows_length,
                   uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     filter(condition, condition_length,
            input_rows, input_rows_length,
@@ -56,6 +70,10 @@ void ecall_filter(uint8_t *condition, size_t condition_length,
 
 void ecall_sample(uint8_t *input_rows, size_t input_rows_length,
                   uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     sample(input_rows, input_rows_length,
            output_rows, output_rows_length);
@@ -68,6 +86,10 @@ void ecall_find_range_bounds(uint8_t *sort_order, size_t sort_order_length,
                              uint32_t num_partitions,
                              uint8_t *input_rows, size_t input_rows_length,
                              uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     find_range_bounds(sort_order, sort_order_length,
                       num_partitions,
@@ -83,6 +105,11 @@ void ecall_partition_for_sort(uint8_t *sort_order, size_t sort_order_length,
                               uint8_t *input_rows, size_t input_rows_length,
                               uint8_t *boundary_rows, size_t boundary_rows_length,
                               uint8_t **output_partitions, size_t *output_partition_lengths) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  assert(sgx_is_outside_enclave(boundary_rows, boundary_rows_length) == 1);
+  sgx_lfence();
+
   try {
     partition_for_sort(sort_order, sort_order_length,
                        num_partitions,
@@ -97,6 +124,10 @@ void ecall_partition_for_sort(uint8_t *sort_order, size_t sort_order_length,
 void ecall_external_sort(uint8_t *sort_order, size_t sort_order_length,
                          uint8_t *input_rows, size_t input_rows_length,
                          uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     external_sort(sort_order, sort_order_length,
                   input_rows, input_rows_length,
@@ -109,6 +140,10 @@ void ecall_external_sort(uint8_t *sort_order, size_t sort_order_length,
 void ecall_scan_collect_last_primary(uint8_t *join_expr, size_t join_expr_length,
                                      uint8_t *input_rows, size_t input_rows_length,
                                      uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     scan_collect_last_primary(join_expr, join_expr_length,
                               input_rows, input_rows_length,
@@ -122,6 +157,11 @@ void ecall_non_oblivious_sort_merge_join(uint8_t *join_expr, size_t join_expr_le
                                          uint8_t *input_rows, size_t input_rows_length,
                                          uint8_t *join_row, size_t join_row_length,
                                          uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  assert(sgx_is_outside_enclave(join_row, join_row_length) == 1);
+  sgx_lfence();
+
   try {
     non_oblivious_sort_merge_join(join_expr, join_expr_length,
                                   input_rows, input_rows_length,
@@ -138,6 +178,10 @@ void ecall_non_oblivious_aggregate_step1(
   uint8_t **first_row, size_t *first_row_length,
   uint8_t **last_group, size_t *last_group_length,
   uint8_t **last_row, size_t *last_row_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  sgx_lfence();
+
   try {
     non_oblivious_aggregate_step1(
       agg_op, agg_op_length,
@@ -157,6 +201,13 @@ void ecall_non_oblivious_aggregate_step2(
   uint8_t *prev_partition_last_group, size_t prev_partition_last_group_length,
   uint8_t *prev_partition_last_row, size_t prev_partition_last_row_length,
   uint8_t **output_rows, size_t *output_rows_length) {
+  // Guard against operating on arbitrary enclave memory
+  assert(sgx_is_outside_enclave(input_rows, input_rows_length) == 1);
+  assert(sgx_is_outside_enclave(next_partition_first_row, next_partition_first_row_length) == 1);
+  assert(sgx_is_outside_enclave(prev_partition_last_group, prev_partition_last_group_length) == 1);
+  assert(sgx_is_outside_enclave(prev_partition_last_row, prev_partition_last_row_length) == 1);
+  sgx_lfence();
+
   try {
     non_oblivious_aggregate_step2(
       agg_op, agg_op_length,

programming_language: C++
filename: Enclave.edl
change_type: ModificationType.MODIFY
diff: @@ -88,7 +88,18 @@ enclave {
 
   untrusted {
     void ocall_print_string([in, string] const char *str);
-    void ocall_malloc(size_t size, [out] uint8_t **ret);
+
+    /**
+     * Allocate memory outside of the enclave and return the pointer in `ret`.
+     *
+     * Before dereferencing the resulting pointer, the caller must check whether it is actually
+     * outside the enclave using `sgx_is_outside_enclave()`. Otherwise, an attacker could cause the
+     * enclave to perform unexpected operations on its own memory. The function `ocall_malloc()`
+     * wraps this function with such a bounds check and most callers should use that function
+     * instead.
+     */
+    void unsafe_ocall_malloc(size_t size, [out] uint8_t **ret);
+
     void ocall_free([user_check] uint8_t *buf);
     void ocall_exit(int exit_code);
     void ocall_throw([in, string] const char *message);

programming_language: C
filename: util.cpp
change_type: ModificationType.MODIFY
diff: @@ -4,6 +4,7 @@
 #include <cstdio>
 
 #include "Enclave_t.h"
+#include "sgx_lfence.h"
 
 int printf(const char *fmt, ...) {
   char buf[BUFSIZ] = {'\0'};
@@ -38,6 +39,14 @@ void exit(int exit_code) {
   ocall_exit(exit_code);
 }
 
+void ocall_malloc(size_t size, uint8_t **ret) {
+  unsafe_ocall_malloc(size, ret);
+
+  // Guard against overwriting enclave memory
+  assert(sgx_is_outside_enclave(*ret, size) == 1);
+  sgx_lfence();
+}
+
 void print_bytes(uint8_t *ptr, uint32_t len) {
   for (uint32_t i = 0; i < len; i++) {
     printf("%u", *(ptr + i));

programming_language: C++
filename: util.h
change_type: ModificationType.MODIFY
diff: @@ -18,6 +18,14 @@ namespace std {
     using ::exit;
 }
 
+/**
+ * Allocate memory outside of the enclave and return the pointer in `ret`.
+ *
+ * This is a checked wrapper around `unsafe_ocall_malloc`. The resulting pointer is safe to write
+ * to.
+ */
+void ocall_malloc(size_t size, uint8_t **ret);
+
 std::string string_format(const std::string &fmt, ...);
 
 void print_bytes(uint8_t *ptr, uint32_t len);

programming_language: C++