Skip to content

Commit d07769b

Browse files
committed
- _handle_sgx_get_report will now write to the supplied argument only if it lies in host memory. This fixes data corruption scenario where the untrusted host could supply address of an enclave memory location to this function and corrupt the contents of that location. - Check for missing null terminator in oeedger8r generated code. oeedger8r now emits null terminator checks for string and wstring in and in-out parameters. Attempts by an untrusted host to manually construct a marshalling struct with non null-terminated strings will be detected and the edge function will report an error.
1 parent a63542c commit d07769b

File tree

9 files changed

+423
-21
lines changed

9 files changed

+423
-21
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4141
`OE_ENCLAVE_TYPE_AUTO` to have the enclave appropriate to your built environment
4242
be chosen automatically. For instance, building Intel binaries will select SGX
4343
automatically, where on ARM it will pick TrustZone.
44+
- Fix CVE-2019-0876
45+
- `_handle_sgx_get_report` will now write to the supplied argument if it les in host memory.
46+
- Added check for missing null terminator in oeedger8r generated code.
4447

4548
### Changed
4649

enclave/core/sgx/calls.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,16 @@ static oe_result_t _handle_call_enclave_function(uint64_t arg_in)
220220
args = *args_ptr;
221221

222222
// Ensure that input buffer is valid.
223-
if (args.input_buffer == NULL || args.input_buffer_size == 0 ||
223+
// Input buffer must be able to hold atleast an oe_result_t.
224+
if (args.input_buffer == NULL ||
225+
args.input_buffer_size < sizeof(oe_result_t) ||
224226
!oe_is_outside_enclave(args.input_buffer, args.input_buffer_size))
225227
OE_RAISE(OE_INVALID_PARAMETER);
226228

227229
// Ensure that output buffer is valid.
228-
if (args.output_buffer == NULL || args.output_buffer_size == 0 ||
230+
// Output buffer must be able to hold atleast an oe_result_t.
231+
if (args.output_buffer == NULL ||
232+
args.output_buffer_size < sizeof(oe_result_t) ||
229233
!oe_is_outside_enclave(args.output_buffer, args.output_buffer_size))
230234
OE_RAISE(OE_INVALID_PARAMETER);
231235

@@ -271,13 +275,20 @@ static oe_result_t _handle_call_enclave_function(uint64_t arg_in)
271275
args.output_buffer_size,
272276
&output_bytes_written);
273277

274-
// Copy outputs to host memory.
275-
memcpy(args.output_buffer, output_buffer, output_bytes_written);
278+
// The output_buffer is expected to point to a marshaling struct,
279+
// whose first field is an oe_result_t. The function is expected
280+
// to fill this field with the status of the ecall.
281+
result = *(oe_result_t*)output_buffer;
276282

277-
// The ecall succeeded.
278-
args_ptr->output_bytes_written = output_bytes_written;
279-
args_ptr->result = OE_OK;
280-
result = OE_OK;
283+
if (result == OE_OK)
284+
{
285+
// Copy outputs to host memory.
286+
memcpy(args.output_buffer, output_buffer, output_bytes_written);
287+
288+
// The ecall succeeded.
289+
args_ptr->output_bytes_written = output_bytes_written;
290+
args_ptr->result = OE_OK;
291+
}
281292

282293
done:
283294
if (buffer)

enclave/core/sgx/report.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,10 +400,11 @@ oe_result_t _handle_get_sgx_report(uint64_t arg_in)
400400
oe_get_sgx_report_args_t enc_arg;
401401
size_t report_buffer_size = sizeof(sgx_report_t);
402402

403-
if (host_arg == NULL)
403+
if (!oe_is_outside_enclave(host_arg, sizeof(*host_arg)))
404404
OE_RAISE(OE_INVALID_PARAMETER);
405405

406406
// Validate and copy args to prevent TOCTOU issues.
407+
// oe_get_sgx_report_args_t is a flat structure with no nested pointers.
407408
enc_arg = *host_arg;
408409

409410
// Host is not allowed to pass report data. Otherwise, the host can use the
@@ -420,9 +421,8 @@ oe_result_t _handle_get_sgx_report(uint64_t arg_in)
420421

421422
*host_arg = enc_arg;
422423
result = OE_OK;
423-
424+
host_arg->result = result;
424425
done:
425-
if (host_arg)
426-
host_arg->result = result;
426+
427427
return result;
428428
}

enclave/core/sgx/tracee.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <openenclave/bits/types.h>
77
#include <openenclave/corelibc/stdarg.h>
88
#include <openenclave/corelibc/stdio.h>
9+
#include <openenclave/corelibc/stdlib.h>
910
#include <openenclave/corelibc/string.h>
1011
#include <openenclave/enclave.h>
1112
#include <openenclave/internal/calls.h>
@@ -73,6 +74,7 @@ bool is_enclave_debug_allowed()
7374
oe_result_t _handle_oelog_init(uint64_t arg)
7475
{
7576
oe_result_t result = OE_FAILURE;
77+
char* path = NULL;
7678
const char* filename = NULL;
7779
oe_log_filter_t* filter = (oe_log_filter_t*)arg;
7880
oe_log_filter_t local;
@@ -98,6 +100,17 @@ oe_result_t _handle_oelog_init(uint64_t arg)
98100
goto done;
99101
}
100102

103+
/* Copy path to enclave memory and add a null-terminator */
104+
path = (char*)oe_calloc(1, local.path_len + 1);
105+
if (path == NULL)
106+
{
107+
result = OE_OUT_OF_MEMORY;
108+
goto done;
109+
}
110+
oe_secure_memcpy(path, local.path, local.path_len);
111+
path[local.path_len] = '\0';
112+
local.path = path;
113+
101114
_active_log_level = local.level;
102115
filename = get_filename_from_path(local.path, local.path_len);
103116
if (filename)
@@ -112,6 +125,9 @@ oe_result_t _handle_oelog_init(uint64_t arg)
112125
_debug_allowed_enclave = is_enclave_debug_allowed();
113126
result = OE_OK;
114127
done:
128+
if (path)
129+
oe_free(path);
130+
115131
return result;
116132
}
117133

include/openenclave/edger8r/common.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,34 @@ OE_INLINE oe_result_t oe_add_size(size_t* total, size_t size)
158158

159159
#define OE_READ_IN_OUT_PARAM OE_READ_OUT_PARAM
160160

161+
/**
162+
* Check that a string is null terminated.
163+
*/
164+
#define OE_CHECK_NULL_TERMINATOR(str, size) \
165+
{ \
166+
const char* _str = (const char*)(str); \
167+
size_t _size = (size_t)(size); \
168+
if (_str && (_size == 0 || _str[_size - 1] != '\0')) \
169+
{ \
170+
_result = OE_INVALID_PARAMETER; \
171+
goto done; \
172+
} \
173+
}
174+
175+
/**
176+
* Check that a wstring is null terminated.
177+
*/
178+
#define OE_CHECK_NULL_TERMINATOR_WIDE(str, size) \
179+
{ \
180+
const wchar_t* _str = (const wchar_t*)(str); \
181+
size_t _size = (size_t)(size); \
182+
if (_str && (_size == 0 || _str[_size - 1] != L'\0')) \
183+
{ \
184+
_result = OE_INVALID_PARAMETER; \
185+
goto done; \
186+
} \
187+
}
188+
161189
OE_EXTERNC_END
162190

163191
#endif // _OE_EDGER8R_COMMON_H

tests/oeedger8r/edl/string.edl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ enclave {
3838
public void ecall_wstring_fun7([wstring, in] wchar_t* s1, [wstring, in] wchar_t* s2);
3939

4040
public void test_wstring_edl_ocalls();
41+
42+
// Negative tests for non null-terminated strings
43+
public void ecall_string_no_null_terminator(
44+
[string, in] char* s1, [string, in, out] char* s2);
45+
public void ecall_wstring_no_null_terminator(
46+
[wstring, in] wchar_t* s1, [wstring, in, out] wchar_t* s2);
47+
4148
};
4249

4350
untrusted {
@@ -74,6 +81,10 @@ enclave {
7481

7582
// Multiple string parameters
7683
void ocall_wstring_fun7([wstring, in] wchar_t* s1, [wstring, in] wchar_t* s2);
77-
};
84+
85+
// Scenario where host does not null terminate in-out strings.
86+
void ocall_string_no_null_terminator(bool erasenull, [string, in, out] char* s);
87+
void ocall_wstring_no_null_terminator(bool erasenull, [wstring, in, out] wchar_t* s);
88+
};
7889
};
7990

tests/oeedger8r/enc/teststring.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ void test_string_edl_ocalls()
4040
// Multiple string params. One null.
4141
OE_TEST(ocall_string_fun7(str, NULL) == OE_OK);
4242

43+
// Test scenario where host does not null-terminate an
44+
// in-out string. The first call preserves the null-terminator.
45+
// The second call does not preserve the null terminator.
46+
{
47+
char str1[] = "Hello";
48+
OE_TEST(ocall_string_no_null_terminator(false, str1) == OE_OK);
49+
OE_TEST(
50+
ocall_string_no_null_terminator(true, str1) ==
51+
OE_INVALID_PARAMETER);
52+
}
53+
4354
printf("=== test_string_edl_ocalls passed\n");
4455
}
4556

@@ -129,6 +140,18 @@ void ecall_string_fun7(char* s1, char* s2)
129140
OE_TEST(s2 == NULL);
130141
}
131142

143+
void ecall_string_no_null_terminator(char* s1, char* s2)
144+
{
145+
OE_UNUSED(s1);
146+
OE_UNUSED(s2);
147+
}
148+
149+
void ecall_wstring_no_null_terminator(wchar_t* s1, wchar_t* s2)
150+
{
151+
OE_UNUSED(s1);
152+
OE_UNUSED(s2);
153+
}
154+
132155
void test_wstring_edl_ocalls()
133156
{
134157
const wchar_t* str_value = L"Hello, World\n";
@@ -163,6 +186,17 @@ void test_wstring_edl_ocalls()
163186
// Multiple string params. One null.
164187
OE_TEST(ocall_wstring_fun7(str, NULL) == OE_OK);
165188

189+
// Test scenario where host does not null-terminate an
190+
// in-out string. The first call preserves the null-terminator.
191+
// The second call does not preserve the null terminator.
192+
{
193+
wchar_t str1[] = L"Hello";
194+
OE_TEST(ocall_wstring_no_null_terminator(false, str1) == OE_OK);
195+
OE_TEST(
196+
ocall_wstring_no_null_terminator(true, str1) ==
197+
OE_INVALID_PARAMETER);
198+
}
199+
166200
printf("=== test_wstring_edl_ocalls passed\n");
167201
}
168202

0 commit comments

Comments
 (0)