Skip to content

Commit 00e1010

Browse files
committed
[LibOS] Make handling of corruption more consistent (WIP)
Signed-off-by: g2flyer <[email protected]>
1 parent b5ea57a commit 00e1010

File tree

5 files changed

+105
-51
lines changed

5 files changed

+105
-51
lines changed

libos/src/fs/libos_fs_encrypted.c

+62-49
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ static LISTP_TYPE(libos_encrypted_files_key) g_keys = LISTP_INIT;
1919

2020
/* Protects the `g_keys` list, but also individual keys, since they can be updated */
2121
static struct libos_lock g_keys_lock;
22-
2322
static LISTP_TYPE(libos_encrypted_volume) g_volumes = LISTP_INIT;
2423

2524
/* Protects the `g_volumes` list. */
@@ -277,69 +276,78 @@ static int encrypted_file_internal_open(struct libos_encrypted_file* enc, PAL_HA
277276
ret = -EACCES;
278277
goto out;
279278
}
279+
libos_encrypted_file_state_t new_state_in_map = PF_FILE_STATE_ACTIVE;
280280
pf_mac_t opening_root_mac;
281281
pf_status_t pfs = pf_open(pal_handle, norm_path, size, PF_FILE_MODE_READ | PF_FILE_MODE_WRITE,
282282
create, &enc->volume->key->pf_key, &opening_root_mac, &pf);
283283
unlock(&g_keys_lock);
284284
if (PF_FAILURE(pfs)) {
285-
log_warning("pf_open failed: %s", pf_strerror(pfs));
286285
ret = -EACCES;
287-
goto out;
286+
if (pfs != PF_STATUS_CORRUPTED) {
287+
log_warning("pf_open failed: %s", pf_strerror(pfs));
288+
goto out;
289+
}
290+
log_error("pf_open of file '%s' encountered corrupted state during open", norm_path);
291+
new_state_in_map = PF_FILE_STATE_ERROR;
288292
}
289293

290294
/* rollback protection */
291-
struct libos_encrypted_volume_state_map* file_state = NULL;
292295
log_debug("file '%s' opened with MAC=" MAC_PRINTF_PATTERN, norm_path,
293296
MAC_PRINTF_ARGS(opening_root_mac)); // TODO (MST): remove me eventually?
297+
struct libos_encrypted_volume_state_map* file_state = NULL;
294298
lock(&(enc->volume->files_state_map_lock));
295299
/* - get current state */
296300
HASH_FIND_STR(enc->volume->files_state_map, norm_path, file_state);
297-
/* - check current state */
298-
if (create) {
299-
if (file_state && (file_state->state != PF_FILE_STATE_DELETED)) {
300-
log_error("newly created file '%s' is in state %s", norm_path,
301-
file_state_to_string(file_state->state));
302-
if (enc->volume->protection_mode != PF_ENCLAVE_LIFE_RB_PROTECTION_NONE) {
303-
pf_set_corrupted(pf);
304-
ret = -EEXIST;
305-
goto out_unlock_map;
306-
}
307-
}
308-
} else {
309-
if (file_state) {
310-
if ((file_state->state == PF_FILE_STATE_ERROR) ||
311-
(file_state->state == PF_FILE_STATE_DELETED)) {
312-
log_error("file '%s' was seen before but in %s state", norm_path,
301+
if (new_state_in_map != PF_FILE_STATE_ERROR) {
302+
/* - check current state */
303+
if (create) {
304+
if (file_state && (file_state->state != PF_FILE_STATE_DELETED)) {
305+
// Note: with create=true we want to open without overwriting, so only valid state
306+
// for an existing map entry is if the file was known to be deleted.
307+
log_error("newly created file '%s' is in state %s", norm_path,
313308
file_state_to_string(file_state->state));
314309
if (enc->volume->protection_mode != PF_ENCLAVE_LIFE_RB_PROTECTION_NONE) {
315-
pf_set_corrupted(pf);
316-
ret = -EACCES;
317-
goto out_unlock_map;
318-
}
319-
}
320-
if (memcmp(file_state->last_seen_root_mac, opening_root_mac, sizeof(pf_mac_t)) != 0) {
321-
log_error(
322-
"file '%s' was seen before but in different inconsistent (rolled-back?) "
323-
"state, expected MAC=" MAC_PRINTF_PATTERN
324-
" but file had "
325-
"MAC=" MAC_PRINTF_PATTERN,
326-
norm_path, MAC_PRINTF_ARGS(file_state->last_seen_root_mac),
327-
MAC_PRINTF_ARGS(opening_root_mac));
328-
if (enc->volume->protection_mode != PF_ENCLAVE_LIFE_RB_PROTECTION_NONE) {
329-
pf_set_corrupted(pf);
330-
ret = -EACCES;
331-
goto out_unlock_map;
310+
pf_close(pf, NULL);
311+
ret = -EEXIST;
312+
new_state_in_map = PF_FILE_STATE_ERROR;
332313
}
333314
}
334315
} else {
335-
if (enc->volume->protection_mode == PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT) {
336-
log_error(
337-
"file '%s' was not seen before which is not allowed with strict rollback "
338-
"protection mode",
339-
norm_path);
340-
pf_set_corrupted(pf);
341-
ret = -EACCES;
342-
goto out_unlock_map;
316+
if (file_state) {
317+
if ((file_state->state == PF_FILE_STATE_ERROR) ||
318+
(file_state->state == PF_FILE_STATE_DELETED)) {
319+
log_error("file '%s' was seen before but in %s state", norm_path,
320+
file_state_to_string(file_state->state));
321+
if (enc->volume->protection_mode != PF_ENCLAVE_LIFE_RB_PROTECTION_NONE) {
322+
pf_close(pf, NULL);
323+
ret = -EACCES;
324+
new_state_in_map = PF_FILE_STATE_ERROR;
325+
}
326+
} else if (memcmp(file_state->last_seen_root_mac, opening_root_mac,
327+
sizeof(pf_mac_t)) != 0) {
328+
log_error(
329+
"file '%s' was seen before but in different inconsistent (rolled-back?) "
330+
"state, expected MAC=" MAC_PRINTF_PATTERN
331+
" but file had "
332+
"MAC=" MAC_PRINTF_PATTERN,
333+
norm_path, MAC_PRINTF_ARGS(file_state->last_seen_root_mac),
334+
MAC_PRINTF_ARGS(opening_root_mac));
335+
if (enc->volume->protection_mode != PF_ENCLAVE_LIFE_RB_PROTECTION_NONE) {
336+
pf_close(pf, NULL);
337+
ret = -EACCES;
338+
new_state_in_map = PF_FILE_STATE_ERROR;
339+
}
340+
}
341+
} else {
342+
if (enc->volume->protection_mode == PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT) {
343+
log_error(
344+
"file '%s' was not seen before which is not allowed with strict rollback "
345+
"protection mode",
346+
norm_path);
347+
pf_close(pf, NULL);
348+
ret = -EACCES;
349+
new_state_in_map = PF_FILE_STATE_ERROR;
350+
}
343351
}
344352
}
345353
}
@@ -354,15 +362,20 @@ static int encrypted_file_internal_open(struct libos_encrypted_file* enc, PAL_HA
354362
norm_path = NULL; /* to prevent freeing it */
355363
HASH_ADD_KEYPTR(hh, enc->volume->files_state_map, file_state->norm_path,
356364
strlen(file_state->norm_path), file_state);
365+
log_debug(
366+
"updated file protection map with file '%s', state '%s' and MAC=" MAC_PRINTF_PATTERN,
367+
file_state->norm_path, file_state_to_string(file_state->state),
368+
MAC_PRINTF_ARGS(file_state->last_seen_root_mac));
357369
}
358370
/* we do below unconditionally as we might recreate a deleted file or overwrite an existing
359371
* one */
360372
memcpy(file_state->last_seen_root_mac, opening_root_mac, sizeof(pf_mac_t));
361-
file_state->state = PF_FILE_STATE_ACTIVE;
373+
file_state->state = new_state_in_map;
362374

363-
enc->pf = pf;
364-
enc->pal_handle = pal_handle;
365-
ret = 0;
375+
if (ret == 0) {
376+
enc->pf = pf;
377+
enc->pal_handle = pal_handle;
378+
}
366379

367380
out_unlock_map:
368381
unlock(&(enc->volume->files_state_map_lock));

libos/test/fs/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ tests = {
4343
'open_close': {},
4444
'open_flags': {},
4545
'pf_rollback': {},
46+
'pf_tamper': {},
4647
'read_write': {},
4748
'read_write_mmap': {},
4849
'seek_tell': {},
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
loader.entrypoint = "file:{{ gramine.libos }}"
2+
loader.log_level ="trace" # DEBUG
3+
libos.entrypoint = "{{ entrypoint }}"
4+
5+
loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"
6+
loader.insecure__use_cmdline_argv = true
7+
8+
fs.mounts = [
9+
{ path = "/lib", uri = "file:{{ gramine.runtimedir(libc) }}" },
10+
{ path = "/{{ entrypoint }}", uri = "file:{{ binary_dir }}/{{ entrypoint }}" },
11+
{ path = "/bin", uri = "file:/bin" },
12+
13+
{ type = "encrypted", protection_mode = "non-strict", path = "/tmp/enc_output", uri = "file:tmp/enc_output" },
14+
]
15+
16+
sgx.max_threads = {{ '1' if env.get('EDMM', '0') == '1' else '16' }}
17+
sgx.debug = true
18+
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
19+
20+
21+
sgx.trusted_files = [
22+
"file:{{ gramine.libos }}",
23+
"file:{{ gramine.runtimedir(libc) }}/",
24+
"file:{{ binary_dir }}/{{ entrypoint }}",
25+
]
26+
27+
# See the `keys.c` test.
28+
fs.insecure__keys.default = "ffeeddccbbaa99887766554433221100"

libos/test/fs/test_enc.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,24 @@ def test_500_invalid(self):
200200
# decryption of invalid file must fail with -1 (wrapped to 255)
201201
self.assertEqual(exc.returncode, 255)
202202
else:
203-
print('[!] Fail: successfully decrypted file: ' + name)
203+
print('[!] Fail: successfully decrypted file with cipher utility: ' + name)
204204
failed = True
205+
206+
# test decryption as part of reading file in program running with gramine
207+
stdout, stderr = self.run_binary(['pf_tamper', input_path])
208+
try:
209+
self.assertIn('ERROR: ', stdout)
210+
# TODO: check also that we updated map in trace/stderr?
211+
# DEBUG: self.assertIn('truncate(' + path_1 + ') to ' + str(size_out) + ' OK', stdout)
212+
except:
213+
print('[!] Fail: successfully decrypted file with gramine: ' + name)
214+
failed = True
215+
205216
if failed:
206217
self.fail()
207218

219+
# checks rollback protection
208220
def test_600_gdb_pf_rollback(self):
209-
# This test checks rollback protection.
210221
# To run this test manually, encrypt a <input_file> (contained in <work_dir>) with the
211222
# default key from manifest and use:
212223
# GDB=1 GDB_TTY=1 GDB_SCRIPT=pf_rollback.gdb gramine-[sgx|direct] pf_rollback <work_dir> <input_file>

libos/test/fs/tests.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ manifests = [
1414
"open_close",
1515
"open_flags",
1616
"pf_rollback",
17+
"pf_tamper",
1718
"read_write",
1819
"read_write_mmap",
1920
"seek_tell",

0 commit comments

Comments
 (0)