Skip to content

Commit

Permalink
Update kraft for EPT.
Browse files Browse the repository at this point in the history
Change the build process for EPT such that IDs are used in cross-compartment calls instead of function pointers.
This is necessary to restrict control flow of cross-compartment calls to actual function boundaries instead of arbitrary pointers.

Also make the maximum number of threads supported by the EPT backend configurable.

Signed-off-by: Sebastian Rauch <[email protected]>
  • Loading branch information
UniformlyR4ndom committed Dec 16, 2021
1 parent 064f674 commit 9970ccc
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 7 deletions.
77 changes: 77 additions & 0 deletions kraft/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@


class Application(Component):
# path of file where the names of functions called via gate are collected
VMEPT_FUNC_LIST_PATH = "/tmp/flexos_vmept_rpc_id_data"

# name and path of auto-generated header file containing the function addresses
VMEPT_ADDR_TABLE_NAME = "vmept_addr_table"

# where to put auto-generated header files (relative to the unikraft build directory)
VMEPT_AUTOGEN_INCLUDE_PATH = "lib/flexos-core/include/flexos/impl"

_type = ComponentType.APP

_config = None
Expand Down Expand Up @@ -605,6 +614,29 @@ def fetch(ctx, self, verbose=False):

return self.make(extra, verbose)

# generate a header containing an array with the addresses of functions called via EPT-gate
def vmept_gen_address_table_header(self, func_names, header_name, addr_map = {}, write_to_file = True):
include_guard = "%s_H" % header_name.upper()
header_code = "#ifndef %s\n" % include_guard
header_code += "#define %s\n\n" % include_guard
header_code += 'static const void* flexos_vmept_addr_table[] __attribute__((section (".rodata"))) = {\n'
num_entries = len(func_names)
i = 0
for fname in func_names:
i += 1
sep = "," if i < num_entries else " "
addr = addr_map[fname] if fname in addr_map else 0
header_code += "\t(void*) 0x%016x%s /* %s */\n" % (addr, sep, fname)
header_code += "};\n\n"
header_code += "#endif /* %s */" % include_guard
if write_to_file:
addr_table_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH)
addr_table_path = os.path.join(addr_table_path, self.VMEPT_ADDR_TABLE_NAME + ".h")
addr_table_file = open(addr_table_path, "w")
addr_table_file.write(header_code)
addr_table_file.close()
return header_code

@click.pass_context
def compartmentalize(ctx, self):
self.find_files()
Expand Down Expand Up @@ -792,6 +824,12 @@ def simple_replace(template_path, path, marker, shstack_enabled=True):
cmd = ["diff", "-urNp", backup_src, filep]
subprocess.call(cmd, stdout=fulldiff, stderr=subprocess.STDOUT)

is_ept = type(self.compartments[0].mechanism.driver) == VMEPTDriver
if is_ept and not os.path.isfile(self.VMEPT_FUNC_LIST_PATH):
# make sure this file exists
rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "w")
rpc_id_file.close()

# now do library-specific rewrites
for lib in self.libraries:
# first add per-library linker scripts
Expand All @@ -810,6 +848,13 @@ def simple_replace(template_path, path, marker, shstack_enabled=True):
str(lib.compartment.number))
gr_rule = ""

ept_rpc_id_prefix = "_RPC_ID_" if is_ept else ""
if (is_ept):
rpc_id_gen_template = get_sec_rule("rpc_id_gen.cocci.in")
rpc_id_gen_template = rpc_id_gen_template.replace("{{ filename }}",
"'" + self.VMEPT_FUNC_LIST_PATH + "'")
gr_rule = rpc_id_gen_template + "\n"

def gr_gen_rule(dest_name, dest_comp):
gr_rule = str(gr_rule_template)
name = dest_name
Expand All @@ -825,6 +870,9 @@ def gr_gen_rule(dest_name, dest_comp):
if dest_comp == lib.compartment:
# FIXME magic value, put somewhere
gr_gate = "flexos_nop_gate"
gr_rule = gr_rule.replace("{{ ept_id_prefix }}", "")
else:
gr_rule = gr_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix)

gr_rule = gr_rule.replace("{{ gate }}", gr_gate)
gr_rule = gr_rule.replace("{{ gate_r }}", gr_gate + "_r")
Expand Down Expand Up @@ -868,6 +916,7 @@ def cb_gen_rule(dest_name, dest_comp):
# with future upstream releases of Coccinelle, talking about it with Julia
whitelisted_libs = ["libvfscore", "libuknetdev", "newlib",
"libuksched", "libuksignal", "libukboot"]

for dest_lib in self.libraries:
if (not dest_lib.compartment.default) and (dest_lib.name != lib.name):
# this library is not in the default compartment, add a specific rule
Expand Down Expand Up @@ -920,6 +969,34 @@ def cb_gen_rule(dest_name, dest_comp):

coccinelle_rewrite(lib, rule_file, fulldiff)

# generate a header with macros defining IDs for all
# functions given by function_names
def vmept_gen_rpc_id_macro_header(function_names, header_name):
include_guard = "%s_H" % header_name.upper()
header_code = "#ifndef %s\n" % include_guard
header_code += "#define %s\n\n" % include_guard
rpc_id = 0
for fname in function_names:
header_code += "#define %s%s %d\n" % (ept_rpc_id_prefix, fname, rpc_id)
rpc_id += 1
header_code += "\n#define FLEXOS_VMEPT_RPCID_CNT %d\n\n" % rpc_id
header_code += "#endif /* %s */" % include_guard
return header_code

if (is_ept):
rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "r")
fnames = list(map(str.strip, rpc_id_file.readlines()))
rpc_id_file.close()
id_header_name = "vmept_rpc_id"
addr_table_name = "flexos_%s" % self.VMEPT_ADDR_TABLE_NAME
id_header_code = vmept_gen_rpc_id_macro_header(fnames, id_header_name)
addr_table_code = self.vmept_gen_address_table_header(fnames, addr_table_name, write_to_file = True)
id_header_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH)
id_header_path = os.path.join(id_header_path, id_header_name + ".h")
id_header_file = open(id_header_path, "w")
id_header_file.write(id_header_code)
id_header_file.close()

logger.info("Full diff of rewritings: %s" % fulldifff)
fulldiff.close()

Expand Down
59 changes: 58 additions & 1 deletion kraft/cmd/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ def simple_replace(template_path, marker):
if lib.name.startswith("app"):
appcomp = lib.compartment

# get VMEPT config options
max_threads_shift = 8 # max hreads is (1 << max_threads_shift)
with open(os.path.join(app.localdir, ".config")) as conf:
conf_data = conf.read()
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_256=y' in conf_data:
max_threads_shift = 8
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_128=y' in conf_data:
max_threads_shift = 7
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_64=y' in conf_data:
max_threads_shift = 6
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_32=y' in conf_data:
max_threads_shift = 5
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_16=y' in conf_data:
max_threads_shift = 4
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_8=y' in conf_data:
max_threads_shift = 3
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_4=y' in conf_data:
max_threads_shift = 2

i = 0
logger.info("Building with VM/EPT: " + str(no_ept) + " images to build.")
for comp in app.compartments:
Expand All @@ -92,7 +111,8 @@ def simple_replace(template_path, marker):
# edit build args
extra_args = ["CFLAGS_EXTRA=-DFLEXOS_VMEPT_COMP_ID=" + str(i)
+ " -DFLEXOS_VMEPT_COMP_COUNT=" + str(no_ept)
+ " -DFLEXOS_VMEPT_APPCOMP=" + str(appcomp.number)]
+ " -DFLEXOS_VMEPT_APPCOMP=" + str(appcomp.number)
+ " -DFLEXOS_VMEPT_MAX_THREADS_SHIFT=" + str(max_threads_shift)]
if fast:
extra_args.insert(0, "-j")

Expand All @@ -111,6 +131,43 @@ def simple_replace(template_path, marker):
"of the make command (code " + str(return_code) + ")")
return

for target in app.binaries:
# insert correct addresses
funcname_file = open(Application.VMEPT_FUNC_LIST_PATH, "r")
fnames = list(map(str.strip, funcname_file.readlines()))
funcname_file.close()

addr_map = {}
for fname in fnames:
addr_map[fname] = 0

readelf = subprocess.Popen(['readelf', '-sW', target.binary_debug], stdout = subprocess.PIPE)
output = subprocess.check_output(['awk', '{if ($4 == "FUNC") { print $2,$8 } }'], stdin = readelf.stdout).decode()
readelf.wait()

raw_lines = output.splitlines()

entries = []
for line in raw_lines:
fields = line.split()
address = int(fields[0], 16)
name = fields[1]
if name in addr_map:
addr_map[name] = address

patched_code = app.vmept_gen_address_table_header(fnames,
Application.VMEPT_ADDR_TABLE_NAME, addr_map, write_to_file = True)

# build again
return_code = make_progressbar(app.make_raw(verbose=verbose,
extra=extra_args))

if (return_code > 0):
# there was probably an error
logger.error("Aborting build due to probable error during execution "
"of the make command (code " + str(return_code) + ")")
return

# secure image (so that it doesn't get overwritten)
for target in app.binaries:
os.rename(target.binary, target.binary + ".comp" + str(i))
Expand Down
4 changes: 3 additions & 1 deletion kraft/sec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def coccinelle_rewrite(lib, rule_file, fulldiff):

with open(log, 'wb') as logf:
try:
cmd1 = ["spatch", "-j", "6", "-in_place", "-sp_file", rule_file, file]
# we can't use parallel jobs when building for EPT since this leads to
# @finalize scripts being called too early
cmd1 = ["spatch", "-in_place", "-sp_file", rule_file, file]
cmd2 = ["diff", "-urNp", backup_src, file]
logf.write(bytes("$ " + " ".join(cmd1) + "\n", 'utf-8'))
logf.flush()
Expand Down
2 changes: 1 addition & 1 deletion kraft/sec/replacements/ukboot_init_sections.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
(void *) __uk_image_symbol(_ecomp{{ comp_nr }}));
PROTECT_SECTION("bss_comp{{ comp_nr }}", {{ comp_nr }}, (void *) __uk_image_symbol(_bss_comp{{ comp_nr }}),
(void *) __uk_image_symbol(_ebss_comp{{ comp_nr }}));
ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, 1000 /* size */, flexos_comp{{ comp_nr }}_alloc);
ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, CONFIG_LIBFLEXOS_COMP_HEAP_SIZE /* size */, flexos_comp{{ comp_nr }}_alloc);
60 changes: 56 additions & 4 deletions kraft/sec/rules/gatereplacer.cocci.in
Original file line number Diff line number Diff line change
@@ -1,13 +1,65 @@
@gatereplacer_return0_{{ rule_nr }}@
identifier func;
expression ret, lname;
fresh identifier func_id = "{{ ept_id_prefix }}" ## func;
@@
- flexos_gate_r({{ lib_dest_name }}, ret, func);
+ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id);

@script:python@
func_name << gatereplacer_return0_{{ rule_nr }}.func;
@@
fname = str(func_name)
if fname not in entries:
entries[fname] = entry_cnt
entry_cnt += 1

@gatereplacer_return{{ rule_nr }}@
identifier func;
expression list EL;
expression ret, lname;
fresh identifier func_id = "{{ ept_id_prefix }}" ## func;
@@
- flexos_gate_r({{ lib_dest_name }}, ret, func, EL);
+ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id, EL);

@script:python@
func_name << gatereplacer_return{{ rule_nr }}.func;
@@
- flexos_gate_r({{ lib_dest_name }}, ret, EL);
+ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, EL);
fname = str(func_name)
if fname not in entries:
entries[fname] = entry_cnt
entry_cnt += 1

@gatereplacer_noreturn0_{{ rule_nr }}@
identifier func;
expression lname;
fresh identifier func_id = "{{ ept_id_prefix }}" ## func;
@@
- flexos_gate({{ lib_dest_name }}, func);
+ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id);

@script:python@
func_name << gatereplacer_noreturn0_{{ rule_nr }}.func;
@@
fname = str(func_name)
if fname not in entries:
entries[fname] = entry_cnt
entry_cnt += 1

@gatereplacer_noreturn{{ rule_nr }}@
identifier func;
expression list EL;
expression lname;
fresh identifier func_id = "{{ ept_id_prefix }}" ## func;
@@
- flexos_gate({{ lib_dest_name }}, func, EL);
+ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id, EL);

@script:python@
func_name << gatereplacer_noreturn{{ rule_nr }}.func;
@@
- flexos_gate({{ lib_dest_name }}, EL);
+ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, EL);
fname = str(func_name)
if fname not in entries:
entries[fname] = entry_cnt
entry_cnt += 1
21 changes: 21 additions & 0 deletions kraft/sec/rules/rpc_id_gen.cocci.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@initialize:python@ @@
import os
import operator

filename = {{ filename }}
file = open(filename, "r")
lines = file.readlines()
entries = {}
entry_cnt = 0
for l in lines:
entries[l.strip()] = entry_cnt
entry_cnt += 1
file.close()

@finalize:python@ @@
filename = {{ filename }}
file = open(filename, "w")
sorted_entries = sorted(entries.items(), key=operator.itemgetter(1))
for e in sorted_entries:
file.write(e[0] + "\n")
file.close()

0 comments on commit 9970ccc

Please sign in to comment.