Files
intel-linux-sgx/sdk/edger8r/linux/CodeGen.ml
Li, Xun 60d36e0de7 Linux 2.10 Open Source Gold Release
Provided a reproducible SDK.
Supported new OS: RHEL 8.1, CentOS 8.1 and Fedora 31.
Supported user to specify platform id in PCK Cert ID Retrieval Tool’s command line option.
Added ability to execute Platform Cert ID Retrieval Tool on multi-package platforms without
  loading enclaves. PCCS now supports this functionality. The platform still needs to support
  SGX.
Updated Platform Cert ID Retrieval Tool and Multi-package registration tool to align with BIOS
  platform manifest changes.
Added .deb and .rpm installers for Platform Cert ID Retrieval Tool and Multi-package
  Registration Agent.
Fixed bugs.

Signed-off-by: Li, Xun <xun.li@intel.com>
2020-07-07 08:42:08 +08:00

2522 lines
112 KiB
OCaml

(*
* Copyright (C) 2011-2020 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*)
open Printf
open Util (* for failwithf *)
(* --------------------------------------------------------------------
* We first introduce a `parse_enclave_ast' function (see below) to
* parse a value of type `Ast.enclave' into a `enclave_content' record.
* --------------------------------------------------------------------
*)
(* This record type is used to better organize a value of Ast.enclave *)
type enclave_content = Ast.enclave_content = {
file_shortnm : string; (* the short name of original EDL file *)
enclave_name : string; (* the normalized C identifier *)
include_list : string list; (* include another .h *)
import_exprs : Ast.import_decl list; (* always empty list after finished reduce_import. *)
comp_defs : Ast.composite_type list;
tfunc_decls : Ast.trusted_func list;
ufunc_decls : Ast.untrusted_func list;
}
(* The generated code of ECalls and OCalls depends on some SGX utility
* functions, each of which has two versions. The version to be used for an
* ECall/OCall is determined by whether the ECAll/OCall is switchless or not.
* For example, switchless ECalls use sgx_ecall_switchless() while ordinary ECalls use
* sgx_ecall(). *)
type sgx_fn_id = SGX_ECALL | SGX_OCALL | SGX_OCALLOC | SGX_OCFREE
let get_sgx_fname fn_id is_switchless =
let switchless_str = if is_switchless then "_switchless" else "" in
match fn_id with
SGX_ECALL -> "sgx_ecall" ^ switchless_str
| SGX_OCALL -> "sgx_ocall" ^ switchless_str
| SGX_OCALLOC -> "sgx_ocalloc"
| SGX_OCFREE -> "sgx_ocfree"
(* Whether to prefix untrusted proxy with Enclave name *)
let g_use_prefix = ref false
let g_untrusted_dir = ref "."
let g_trusted_dir = ref "."
let empty_ec =
{ file_shortnm = "";
enclave_name = "";
include_list = [];
import_exprs = [];
comp_defs = [];
tfunc_decls = [];
ufunc_decls = []; }
let get_tf_fname (tf: Ast.trusted_func) =
tf.Ast.tf_fdecl.Ast.fname
let is_priv_ecall (tf: Ast.trusted_func) =
tf.Ast.tf_is_priv
let is_switchless_ecall (tf: Ast.trusted_func) =
tf.Ast.tf_is_switchless
let get_uf_fname (uf: Ast.untrusted_func) =
uf.Ast.uf_fdecl.Ast.fname
let get_trusted_func_names (ec: enclave_content) =
List.map get_tf_fname ec.tfunc_decls
let get_untrusted_func_names (ec: enclave_content) =
List.map get_uf_fname ec.ufunc_decls
let tf_list_to_fd_list (tfs: Ast.trusted_func list) =
List.map (fun (tf: Ast.trusted_func) -> tf.Ast.tf_fdecl) tfs
let tf_list_to_priv_list (tfs: Ast.trusted_func list) =
List.map is_priv_ecall tfs
(* Get a list of names of all private ECALLs *)
let get_priv_ecall_names (tfs: Ast.trusted_func list) =
List.filter is_priv_ecall tfs |> List.map get_tf_fname
let uf_list_to_fd_list (ufs: Ast.untrusted_func list) =
List.map (fun (uf: Ast.untrusted_func) -> uf.Ast.uf_fdecl) ufs
(* Get a list of names of all allowed ECALLs from `allow(...)' *)
let get_allowed_names (ufs: Ast.untrusted_func list) =
let allow_lists =
List.map (fun (uf: Ast.untrusted_func) -> uf.Ast.uf_allow_list) ufs
in
List.flatten allow_lists |> dedup_list
(* With `parse_enclave_ast', each enclave AST is traversed only once. *)
let parse_enclave_ast (e: Ast.enclave) =
let ac_include_list = ref [] in
let ac_import_exprs = ref [] in
let ac_comp_defs = ref [] in
let ac_tfunc_decls = ref [] in
let ac_ufunc_decls = ref [] in
List.iter (fun ex ->
match ex with
Ast.Composite x -> ac_comp_defs := x :: !ac_comp_defs
| Ast.Include x -> ac_include_list := x :: !ac_include_list
| Ast.Importing x -> ac_import_exprs := x :: !ac_import_exprs
| Ast.Interface xs ->
List.iter (fun ef ->
match ef with
Ast.Trusted f ->
ac_tfunc_decls := f :: !ac_tfunc_decls
| Ast.Untrusted f ->
ac_ufunc_decls := f :: !ac_ufunc_decls) xs
) e.Ast.eexpr;
{ file_shortnm = e.Ast.ename;
enclave_name = Util.to_c_identifier e.Ast.ename;
include_list = List.rev !ac_include_list;
import_exprs = List.rev !ac_import_exprs;
comp_defs = List.rev !ac_comp_defs;
tfunc_decls = List.rev !ac_tfunc_decls;
ufunc_decls = List.rev !ac_ufunc_decls; }
let is_foreign_array (pt: Ast.parameter_type) =
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr(t, a) ->
match t with
Ast.Foreign _ -> a.Ast.pa_isary
| _ -> false
(* A naked function has neither parameters nor return value. *)
let is_naked_func (fd: Ast.func_decl) =
fd.Ast.rtype = Ast.Void && fd.Ast.plist = []
(*
* If user only defined a trusted function w/o neither parameter nor
* return value, the generated trusted bridge will not call any tRTS
* routines. If the real trusted function doesn't call tRTS function
* either (highly possible), then the MSVC linker will not link tRTS
* into the result enclave.
*)
let tbridge_gen_dummy_variable (ec: enclave_content) =
let _dummy_variable =
sprintf "\n#ifdef _MSC_VER\n\
\t/* In case enclave `%s' doesn't call any tRTS function. */\n\
\tvolatile int force_link_trts = sgx_is_within_enclave(NULL, 0);\n\
\t(void) force_link_trts; /* avoid compiler warning */\n\
#endif\n\n" ec.enclave_name
in
if ec.ufunc_decls <> [] then ""
else
if List.for_all (fun tfd -> is_naked_func tfd.Ast.tf_fdecl) ec.tfunc_decls
then _dummy_variable
else ""
(* This function is used to convert Array form into Pointer form.
* e.g.: int array[10][20] => [count = 200] int* array
*
* This function is called when generating proxy/bridge code and
* the marshaling structure.
*)
let conv_array_to_ptr (pd: Ast.pdecl): Ast.pdecl =
let (pt, declr) = pd in
let get_count_attr ilist =
(* XXX: assume the size of each dimension will be > 0. *)
Ast.ANumber (List.fold_left (fun acc i -> acc*i) 1 ilist)
in
match pt with
Ast.PTVal _ -> (pt, declr)
| Ast.PTPtr(aty, pa) ->
if Ast.is_array declr then
let tmp_declr = { declr with Ast.array_dims = [] } in
let tmp_aty = Ast.Ptr aty in
let tmp_cnt = get_count_attr declr.Ast.array_dims in
let tmp_pa = { pa with Ast.pa_size = { Ast.empty_ptr_size with Ast.ps_count = Some tmp_cnt } }
in (Ast.PTPtr(tmp_aty, tmp_pa), tmp_declr)
else (pt, declr)
(* ------------------------------------------------------------------
* Code generation for edge-routines.
* ------------------------------------------------------------------
*)
(* Little functions for naming of a struct and its members etc *)
let retval_name = "retval"
let retval_declr = { Ast.identifier = retval_name; Ast.array_dims = []; }
let eid_name = "eid"
let ms_ptr_name = "pms"
let ms_struct_val = "ms"
let mk_ms_member_name (pname: string) = "ms_" ^ pname
let mk_ms_struct_name (fname: string) = "ms_" ^ fname ^ "_t"
let ms_retval_name = mk_ms_member_name retval_name
let mk_tbridge_name (fname: string) = "sgx_" ^ fname
let mk_parm_accessor name = sprintf "%s->%s" ms_struct_val (mk_ms_member_name name)
let mk_tmp_var name = "_tmp_" ^ name
let mk_tmp_var2 name1 name2 = "_tmp_" ^ name1 ^ "_" ^ name2
let mk_len_var name = "_len_" ^ name
let mk_len_var2 name1 name2 = "_len_" ^ name1 ^ "_" ^ name2
let mk_in_var name = "_in_" ^ name
let mk_in_var2 name1 name2 = "_in_" ^ name1 ^ "_" ^ name2
let mk_ocall_table_name enclave_name = "ocall_table_" ^ enclave_name
(* Un-trusted bridge name is prefixed with enclave file short name. *)
let mk_ubridge_name (enclave_name: string) (funcname: string) =
sprintf "%s_%s" enclave_name funcname
let mk_ubridge_proto (enclave_name: string) (funcname: string) =
sprintf "static sgx_status_t SGX_CDECL %s(void* %s)"
(mk_ubridge_name enclave_name funcname) ms_ptr_name
(* Common macro definitions. *)
let common_macros = "#include <stdlib.h> /* for size_t */\n\n\
#define SGX_CAST(type, item) ((type)(item))\n\n\
#ifdef __cplusplus\n\
extern \"C\" {\n\
#endif\n"
(* Header footer *)
let header_footer = "\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif\n"
(* Little functions for generating file names. *)
let get_uheader_short_name (file_shortnm: string) = file_shortnm ^ "_u.h"
let get_uheader_name (file_shortnm: string) =
!g_untrusted_dir ^ separator_str ^ (get_uheader_short_name file_shortnm)
let get_usource_name (file_shortnm: string) =
!g_untrusted_dir ^ separator_str ^ file_shortnm ^ "_u.c"
let get_theader_short_name (file_shortnm: string) = file_shortnm ^ "_t.h"
let get_theader_name (file_shortnm: string) =
!g_trusted_dir ^ separator_str ^ (get_theader_short_name file_shortnm)
let get_tsource_name (file_shortnm: string) =
!g_trusted_dir ^ separator_str ^ file_shortnm ^ "_t.c"
(* Construct the string of structure definition *)
let mk_struct_decl (fs: string) (name: string) =
sprintf "typedef struct %s {\n%s} %s;\n" name fs name
(* Construct the string of structure definition with #ifndef surrounded *)
let mk_struct_decl_with_macro (fs: string) (name: string) =
sprintf "#ifndef _%s\n#define _%s\ntypedef struct %s {\n%s} %s;\n#endif\n" name name name fs name
(* Construct the string of union definition with #ifndef surrounded *)
let mk_union_decl_with_macro (fs: string) (name: string) =
sprintf "#ifndef _%s\n#define _%s\ntypedef union %s {\n%s} %s;\n#endif\n" name name name fs name
(* Generate a definition of enum with #ifndef surrounded *)
let mk_enum_def (e: Ast.enum_def) =
let gen_enum_ele_str (ele: Ast.enum_ele) =
let k, v = ele in
match v with
Ast.EnumValNone -> k
| Ast.EnumVal ev -> sprintf "%s = %s" k (Ast.attr_value_to_string ev)
in
let enname = e.Ast.enname in
let enbody = e.Ast.enbody in
let enbody_str =
if enbody = [] then ""
else List.fold_left (fun acc ele ->
acc ^ "\t" ^ gen_enum_ele_str ele ^ ",\n") "" enbody
in
if enname = "" then sprintf "enum {\n%s};\n" enbody_str
else sprintf "typedef enum %s {\n%s} %s;\n" enname enbody_str enname
let get_array_dims (ns: int list) =
(* Get the array declaration from a list of array dimensions.
* Empty `ns' indicates the corresponding declarator is a simple identifier.
* Element of value -1 means that user does not specify the dimension size.
*)
let get_dim n = if n = -1 then "[]" else sprintf "[%d]" n
in
if ns = [] then ""
else List.fold_left (fun acc n -> acc ^ get_dim n) "" ns
let get_typed_declr_str (ty: Ast.atype) (declr: Ast.declarator) =
let tystr = Ast.get_tystr ty in
let dmstr = get_array_dims declr.Ast.array_dims in
sprintf "%s %s%s" tystr declr.Ast.identifier dmstr
(* Construct a member declaration string *)
let mk_member_decl (ty: Ast.atype) (declr: Ast.declarator) =
sprintf "\t%s;\n" (get_typed_declr_str ty declr)
(* Check whether given parameter is `const' specified. *)
let is_const_ptr (pt: Ast.parameter_type) =
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr(aty, pa) ->
if not pa.Ast.pa_rdonly then false
else
match aty with
Ast.Foreign _ -> false
| _ -> true
(* Note that, for a foreign array type `foo_array_t' we will generate
* foo_array_t* ms_field;
* in the marshaling data structure to keep the pass-by-address scheme
* as in the C programming language.
*)
let mk_ms_member_decl (pt: Ast.parameter_type) (declr: Ast.declarator) (isecall: bool) =
let qual = if is_const_ptr pt then "const " else "" in
let aty = Ast.get_param_atype pt in
let tystr = Ast.get_tystr aty in
let ptr = if is_foreign_array pt then "* " else "" in
let field = mk_ms_member_name declr.Ast.identifier in
(* String attribute is available for in/inout both ecall and ocall.
* For ocall ,strlen is called in trusted proxy ocde, so no need to defense it.
*)
let need_str_len_var (pt: Ast.parameter_type) =
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr(_, pa) ->
if pa.Ast.pa_isstr || pa.Ast.pa_iswstr then
match pa.Ast.pa_direction with
Ast.PtrInOut | Ast.PtrIn -> if isecall then true else false
| _ -> false
else false
in
let str_len = if need_str_len_var pt then sprintf "\tsize_t %s_len;\n" field else ""
in
let dmstr = get_array_dims declr.Ast.array_dims in
sprintf "\t%s%s%s %s%s;\n%s" qual tystr ptr field dmstr str_len
(* It is used to save the structures defined in EDLs. *)
let defined_structure = ref []
let is_structure_defined s = List.exists (fun (( i, _ ): (Ast.struct_def * bool)) -> i.Ast.sname = s) !defined_structure
let get_struct_def s = List.find (fun (( i, _ ): (Ast.struct_def * bool)) -> i.Ast.sname = s) !defined_structure
let is_structure_deep_copy (s:Ast.struct_def) =
let is_deep_copy (pd: Ast.pdecl) =
let (pt, _) = pd in
match pt with
| Ast.PTVal _ -> false
| Ast.PTPtr (_, attr) -> if attr.Ast.pa_size = Ast.empty_ptr_size then false else true
in
List.exists is_deep_copy s.Ast.smlist
let is_foreign_a_structure (pt: Ast.parameter_type) =
let rec is_foreign_atype (atype: Ast.atype) =
match atype with
| Ast.Ptr(ptr) -> is_foreign_atype ptr
| Ast.Foreign(name) -> (is_structure_defined name, name)
| _ -> (false, "")
in
match pt with
| Ast.PTVal(atype)
| Ast.PTPtr(atype, _) -> is_foreign_atype atype
| _ -> (false, "")
(* Check duplicated structure definition and illegal usage.
*)
let check_structure (ec: enclave_content) =
let trusted_fds = tf_list_to_fd_list ec.tfunc_decls in
let untrusted_fds = uf_list_to_fd_list ec.ufunc_decls in
List.iter(fun (st: Ast.composite_type) ->
match st with
Ast.StructDef s ->
if is_structure_defined s.Ast.sname then
failwithf "duplicated structure definition `%s'" s.Ast.sname
else
defined_structure := (s, is_structure_deep_copy s) :: !defined_structure;
| _ -> ()
) ec.comp_defs;
List.iter (fun (fd: Ast.func_decl) ->
List.iter (fun (pd: Ast.pdecl) ->
let (pt, _)= pd in
match pt with
| Ast.PTVal (Ast.Struct(s)) ->
if is_structure_defined s then
let (struct_def, deep_copy) = get_struct_def s
in
if deep_copy then
failwithf "the structure declaration \"%s\" specifies a deep copy is expected. Referenced by value in function \"%s\" detected."s fd.Ast.fname
else
if List.exists (fun (pt, _) ->
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr _ -> true ) struct_def.Ast.smlist
then (eprintf "warning: the structure \"%s\" is referenced by value in function \"%s\". Part of the data may not be copied.\n"s fd.Ast.fname)
else ()
else ()
| Ast.PTPtr (Ast.Ptr(Ast.Struct(s)), attr) ->
if is_structure_defined s then
let (_, deep_copy) = get_struct_def s
in
if deep_copy && attr.Ast.pa_direction = Ast.PtrOut then
failwithf "the structure declaration \"%s\" specifies a deep copy, should not be used with an `out' attribute in function \"%s\"."s fd.Ast.fname
else ()
else ()
| a -> let (found, name) = is_foreign_a_structure a
in
if found then
let (_, deep_copy) = get_struct_def name
in
if deep_copy then
failwithf "`%s' in function `%s' is a structure and it specifies a deep copy. Use `struct %s' instead." name fd.Ast.fname name
else
(eprintf "warning: `%s' in function `%s' is a structure. Use `struct %s' instead.\n" name fd.Ast.fname name)
else ()
) fd.Ast.plist
) (trusted_fds @ untrusted_fds)
(* Generate code using given function if a parameter is structure pointer. *)
let invoke_if_struct (ty: Ast.atype) (param_direction: Ast.ptr_direction) (var_name: string)
(pre:string -> string -> string)
(generator: Ast.ptr_direction -> string -> string -> Ast.atype -> Ast.ptr_attr -> Ast.declarator -> string )
(post:string)=
match ty with
Ast.Ptr(Ast.Struct(struct_type)) ->
if is_structure_defined struct_type then
let (struct_def, deep_copy)= get_struct_def struct_type
in
if deep_copy then
let body =
List.fold_left (
fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr (member_ty, member_attr) ->
if member_attr.Ast.pa_size <> Ast.empty_ptr_size then
acc ^ generator param_direction struct_type var_name member_ty member_attr declr
else acc
) "" struct_def.Ast.smlist
in
if body = "" then ""
else (pre struct_type var_name) ^ body ^ post
else ""
else ""
| _ -> ""
(* Generate data structure definition *)
let gen_comp_def (st: Ast.composite_type) =
let gen_union_member_list mlist =
List.fold_left (fun acc (ty, pdeclr) ->
acc ^ mk_member_decl ty pdeclr) "" mlist
in
let gen_struct_member_list mlist =
List.fold_left (fun acc (pt, declr) ->
acc ^ mk_member_decl (Ast.get_param_atype pt) declr) "" mlist
in
match st with
Ast.StructDef s -> mk_struct_decl_with_macro (gen_struct_member_list s.Ast.smlist) s.Ast.sname
| Ast.UnionDef u -> mk_union_decl_with_macro (gen_union_member_list u.Ast.umlist) u.Ast.uname
| Ast.EnumDef e -> mk_enum_def e
(* Generate a list of '#include' *)
let gen_include_list (xs: string list) =
List.fold_left (fun acc s -> acc ^ sprintf "#include \"%s\"\n" s) "" xs
(* Get the type string from 'parameter_type' *)
let get_param_tystr (pt: Ast.parameter_type) =
Ast.get_tystr (Ast.get_param_atype pt)
(* Generate marshaling structure definition *)
let gen_marshal_struct (fd: Ast.func_decl) (errno: string) (isecall: bool) =
let member_list_str = errno ^
let new_param_list = List.map conv_array_to_ptr fd.Ast.plist in
List.fold_left (fun acc (pt, declr) ->
acc ^ mk_ms_member_decl pt declr isecall) "" new_param_list in
let struct_name = mk_ms_struct_name fd.Ast.fname in
match fd.Ast.rtype with
(* A function w/o return value and parameters doesn't need
a marshaling struct. *)
Ast.Void -> if fd.Ast.plist = [] && errno = "" then ""
else mk_struct_decl member_list_str struct_name
| _ -> let rv_str = mk_ms_member_decl (Ast.PTVal fd.Ast.rtype) retval_declr isecall
in mk_struct_decl (rv_str ^ member_list_str) struct_name
let gen_ecall_marshal_struct (tf: Ast.trusted_func) =
gen_marshal_struct tf.Ast.tf_fdecl "" true
let gen_ocall_marshal_struct (uf: Ast.untrusted_func) =
let errno_decl = if uf.Ast.uf_propagate_errno then "\tint ocall_errno;\n" else "" in
gen_marshal_struct uf.Ast.uf_fdecl errno_decl false
(* Generate parameter representation. *)
let gen_parm_str (p: Ast.pdecl) =
let (pt, (declr : Ast.declarator)) = p in
let aty = Ast.get_param_atype pt in
let str = get_typed_declr_str aty declr in
if is_const_ptr pt then "const " ^ str else str
(* Generate parameter representation of return value. *)
let gen_parm_retval (rt: Ast.atype) =
if rt = Ast.Void then ""
else Ast.get_tystr rt ^ "* " ^ retval_name
(* ---------------------------------------------------------------------- *)
(* `gen_ecall_table' is used to generate ECALL table with the following form:
SGX_EXTERNC const struct {
size_t nr_ecall; /* number of ECALLs */
struct {
void *ecall_addr;
uint8_t is_priv;
uint8_t is_switchless;
} ecall_table [nr_ecall];
} g_ecall_table = {
2, { {sgx_foo, 1, 0}, {sgx_bar, 0, 1} }
};
*)
let gen_ecall_table (tfs: Ast.trusted_func list) =
let ecall_table_name = "g_ecall_table" in
let ecall_table_size = List.length tfs in
let trusted_fds = tf_list_to_fd_list tfs in
let tbridge_names = List.map (fun (fd: Ast.func_decl) ->
mk_tbridge_name fd.Ast.fname) trusted_fds in
let priv_switchless_bits = List.map (fun (tf: Ast.trusted_func ) ->
(is_priv_ecall tf, is_switchless_ecall tf) ) tfs in
let ecall_table =
let bool_to_int b = if b then 1 else 0 in
let inner_table =
List.fold_left2 (fun acc s (b1, b2) ->
sprintf "%s\t\t{(void*)(uintptr_t)%s, %d, %d},\n" acc s (bool_to_int b1) (bool_to_int b2)) "" tbridge_names priv_switchless_bits
in "\t{\n" ^ inner_table ^ "\t}\n"
in
sprintf "SGX_EXTERNC const struct {\n\
\tsize_t nr_ecall;\n\
\tstruct {void* ecall_addr; uint8_t is_priv; uint8_t is_switchless;} ecall_table[%d];\n\
} %s = {\n\
\t%d,\n\
%s};\n" ecall_table_size
ecall_table_name
ecall_table_size
(if ecall_table_size = 0 then "" else ecall_table)
(* `gen_entry_table' is used to generate Dynamic Entry Table with the form:
SGX_EXTERNC const struct {
/* number of OCALLs (number of ECALLs can be found in ECALL table) */
size_t nr_ocall;
/* entry_table[m][n] = 1 iff. ECALL n is allowed in the OCALL m. */
uint8_t entry_table[NR_OCALL][NR_ECALL];
} g_dyn_entry_table = {
3, {{0, 0}, {0, 1}, {1, 0}}
};
*)
let gen_entry_table (ec: enclave_content) =
let dyn_entry_table_name = "g_dyn_entry_table" in
let ocall_table_size = List.length ec.ufunc_decls in
let trusted_func_names = get_trusted_func_names ec in
let ecall_table_size = List.length trusted_func_names in
let get_entry_array (allowed_ecalls: string list) =
List.fold_left (fun acc name ->
acc ^ (if List.exists (fun x -> x=name) allowed_ecalls
then "1"
else "0") ^ ", ") "" trusted_func_names in
let entry_table =
let inner_table =
List.fold_left (fun acc (uf: Ast.untrusted_func) ->
let entry_array = get_entry_array uf.Ast.uf_allow_list
in acc ^ "\t\t{" ^ entry_array ^ "},\n") "" ec.ufunc_decls
in
"\t{\n" ^ inner_table ^ "\t}\n"
in
(* Generate dynamic entry table iff. both sgx_ecall/ocall_table_size > 0 *)
let gen_table_p = (ecall_table_size > 0) && (ocall_table_size > 0) in
(* When NR_ECALL is 0, or NR_OCALL is 0, there will be no entry table field. *)
let entry_table_field =
if gen_table_p then
sprintf "\tuint8_t entry_table[%d][%d];\n" ocall_table_size ecall_table_size
else
""
in
sprintf "SGX_EXTERNC const struct {\n\
\tsize_t nr_ocall;\n%s\
} %s = {\n\
\t%d,\n\
%s};\n" entry_table_field
dyn_entry_table_name
ocall_table_size
(if gen_table_p then entry_table else "")
(* ---------------------------------------------------------------------- *)
(* Generate the function prototype for untrusted proxy in COM style.
* For example, un-trusted functions
* int foo(double d);
* void bar(float f);
*
* will have an untrusted proxy like below:
* sgx_status_t foo(int* retval, double d);
* sgx_status_t bar(float f);
*)
let gen_tproxy_proto (fd: Ast.func_decl) =
let parm_list =
match fd.Ast.plist with
[] -> ""
| x :: xs ->
List.fold_left (fun acc pd ->
acc ^ ", " ^ gen_parm_str pd) (gen_parm_str x) xs
in
let retval_parm_str = gen_parm_retval fd.Ast.rtype in
if fd.Ast.plist = [] && fd.Ast.rtype = Ast.Void then
sprintf "sgx_status_t SGX_CDECL %s(void)" fd.Ast.fname
else if fd.Ast.plist = [] then
sprintf "sgx_status_t SGX_CDECL %s(%s)" fd.Ast.fname retval_parm_str
else if fd.Ast.rtype = Ast.Void then
sprintf "sgx_status_t SGX_CDECL %s(%s)" fd.Ast.fname parm_list
else
sprintf "sgx_status_t SGX_CDECL %s(%s, %s)" fd.Ast.fname retval_parm_str parm_list
(* Generate the function prototype for untrusted proxy in COM style.
* For example, trusted functions
* int foo(double d);
* void bar(float f);
*
* will have an untrusted proxy like below:
* sgx_status_t foo(sgx_enclave_id_t eid, int* retval, double d);
* sgx_status_t foo(sgx_enclave_id_t eid, float f);
*
* When `g_use_prefix' is true, the untrusted proxy name is prefixed
* with the `prefix' parameter.
*
*)
let gen_uproxy_com_proto (fd: Ast.func_decl) (prefix: string) =
let retval_parm_str = gen_parm_retval fd.Ast.rtype in
let eid_parm_str =
if fd.Ast.rtype = Ast.Void then sprintf "(sgx_enclave_id_t %s" eid_name
else sprintf "(sgx_enclave_id_t %s, " eid_name in
let parm_list =
List.fold_left (fun acc pd -> acc ^ ", " ^ gen_parm_str pd)
retval_parm_str fd.Ast.plist in
let fname =
if !g_use_prefix then sprintf "%s_%s" prefix fd.Ast.fname
else fd.Ast.fname
in "sgx_status_t " ^ fname ^ eid_parm_str ^ parm_list ^ ")"
let get_ret_tystr (fd: Ast.func_decl) = Ast.get_tystr fd.Ast.rtype
let get_plist_str (fd: Ast.func_decl) =
if fd.Ast.plist = [] then "void"
else List.fold_left (fun acc pd -> acc ^ ", " ^ gen_parm_str pd)
(gen_parm_str (List.hd fd.Ast.plist))
(List.tl fd.Ast.plist)
(* Generate the function prototype as is. *)
let gen_func_proto (fd: Ast.func_decl) =
let ret_tystr = get_ret_tystr fd in
let plist_str = get_plist_str fd in
sprintf "%s %s(%s)" ret_tystr fd.Ast.fname plist_str
(* Generate prototypes for untrusted function. *)
let gen_ufunc_proto (uf: Ast.untrusted_func) =
let dllimport = if uf.Ast.uf_fattr.Ast.fa_dllimport then "SGX_DLLIMPORT " else "" in
let ret_tystr = get_ret_tystr uf.Ast.uf_fdecl in
let cconv_str = "SGX_" ^ Ast.get_call_conv_str uf.Ast.uf_fattr.Ast.fa_convention in
let func_name = uf.Ast.uf_fdecl.Ast.fname in
let plist_str = get_plist_str uf.Ast.uf_fdecl in
let func_guard = sprintf "%s_DEFINED__" (String.uppercase func_name) in
sprintf "#ifndef %s\n#define %s\n%s%s SGX_UBRIDGE(%s, %s, (%s));\n#endif"
func_guard func_guard dllimport ret_tystr cconv_str func_name plist_str
(* The preemble contains common include expressions. *)
let gen_uheader_preemble (guard: string) (inclist: string)=
let grd_hdr = sprintf "#ifndef %s\n#define %s\n\n" guard guard in
let inc_exp = "#include <stdint.h>\n\
#include <wchar.h>\n\
#include <stddef.h>\n\
#include <string.h>\n\
#include \"sgx_edger8r.h\" /* for sgx_status_t etc. */\n" in
grd_hdr ^ inc_exp ^ "\n" ^ inclist ^ "\n" ^ common_macros
let ms_writer out_chan ec =
let ms_struct_ecall = List.map gen_ecall_marshal_struct ec.tfunc_decls in
let ms_struct_ocall = List.map gen_ocall_marshal_struct ec.ufunc_decls in
let output_struct s =
match s with
"" -> s
| _ -> sprintf "%s\n" s
in
List.iter (fun s -> output_string out_chan (output_struct s)) ms_struct_ecall;
List.iter (fun s -> output_string out_chan (output_struct s)) ms_struct_ocall
(* Generate untrusted header for enclave *)
let gen_untrusted_header (ec: enclave_content) =
let header_fname = get_uheader_name ec.file_shortnm in
let guard_macro = sprintf "%s_U_H__" (String.uppercase ec.enclave_name) in
let preemble_code =
let include_list = gen_include_list (ec.include_list @ !untrusted_headers) in
gen_uheader_preemble guard_macro include_list
in
let comp_def_list = List.map gen_comp_def ec.comp_defs in
let func_proto_ufunc = List.map gen_ufunc_proto ec.ufunc_decls in
let uproxy_com_proto =
List.map (fun (tf: Ast.trusted_func) ->
gen_uproxy_com_proto tf.Ast.tf_fdecl ec.enclave_name)
ec.tfunc_decls
in
let out_chan = open_out header_fname in
output_string out_chan (preemble_code ^ "\n");
List.iter (fun s -> output_string out_chan (s ^ "\n")) comp_def_list;
List.iter (fun s -> output_string out_chan (s ^ "\n")) func_proto_ufunc;
output_string out_chan "\n";
List.iter (fun s -> output_string out_chan (s ^ ";\n")) uproxy_com_proto;
output_string out_chan header_footer;
close_out out_chan
(* It generates preemble for trusted header file. *)
let gen_theader_preemble (guard: string) (inclist: string) =
let grd_hdr = sprintf "#ifndef %s\n#define %s\n\n" guard guard in
let inc_exp = "#include <stdint.h>\n\
#include <wchar.h>\n\
#include <stddef.h>\n\
#include \"sgx_edger8r.h\" /* for sgx_ocall etc. */\n\n" in
grd_hdr ^ inc_exp ^ inclist ^ "\n" ^ common_macros
(* Generate trusted header for enclave *)
let gen_trusted_header (ec: enclave_content) =
let header_fname = get_theader_name ec.file_shortnm in
let guard_macro = sprintf "%s_T_H__" (String.uppercase ec.enclave_name) in
let guard_code =
let include_list = gen_include_list (ec.include_list @ !trusted_headers) in
gen_theader_preemble guard_macro include_list in
let comp_def_list = List.map gen_comp_def ec.comp_defs in
let func_proto_list = List.map gen_func_proto (tf_list_to_fd_list ec.tfunc_decls) in
let func_tproxy_list= List.map gen_tproxy_proto (uf_list_to_fd_list ec.ufunc_decls) in
let out_chan = open_out header_fname in
output_string out_chan (guard_code ^ "\n");
List.iter (fun s -> output_string out_chan (s ^ "\n")) comp_def_list;
List.iter (fun s -> output_string out_chan (s ^ ";\n")) func_proto_list;
output_string out_chan "\n";
List.iter (fun s -> output_string out_chan (s ^ ";\n")) func_tproxy_list;
output_string out_chan header_footer;
close_out out_chan
(* It generates function invocation expression. *)
let mk_parm_name_raw (pt: Ast.parameter_type) (declr: Ast.declarator) =
let cast_expr =
let tystr = get_param_tystr pt in
if Ast.is_array declr && List.length declr.Ast.array_dims > 1
then
let dims = get_array_dims (List.tl declr.Ast.array_dims) in
sprintf "(%s (*)%s)" tystr dims
else ""
in
cast_expr ^ mk_parm_accessor declr.Ast.identifier
(* We passed foreign array `foo_array_t foo' as `&foo[0]', thus we
* need to get back `foo' by '* array_ptr' where
* array_ptr = &foo[0]
*)
let add_foreign_array_ptrref
(f: Ast.parameter_type -> Ast.declarator -> string)
(pt: Ast.parameter_type)
(declr: Ast.declarator) =
let arg = f pt declr in
if is_foreign_array pt
then sprintf "(%s != NULL) ? (*%s) : NULL" arg arg
else arg
let mk_parm_name_ubridge (pt: Ast.parameter_type) (declr: Ast.declarator) =
add_foreign_array_ptrref mk_parm_name_raw pt declr
let mk_parm_name_ext (pt: Ast.parameter_type) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
match pt with
Ast.PTVal _ -> mk_parm_name_raw pt declr
| Ast.PTPtr (_, attr) ->
match attr.Ast.pa_direction with
| Ast.PtrNoDirection -> mk_parm_name_raw pt declr
| _ -> mk_in_var name
let gen_func_invoking (fd: Ast.func_decl)
(mk_parm_name: Ast.parameter_type -> Ast.declarator -> string) =
match fd.Ast.plist with
[] -> sprintf "%s();" fd.Ast.fname
| (pt, (declr : Ast.declarator)) :: ps ->
sprintf "%s(%s);"
fd.Ast.fname
(let p0 = mk_parm_name pt declr in
List.fold_left (fun acc (pty, dlr) ->
acc ^ ", " ^ mk_parm_name pty dlr) p0 ps)
(* Generate untrusted bridge code for a given untrusted function. *)
let gen_func_ubridge (enclave_name: string) (ufunc: Ast.untrusted_func) =
let fd = ufunc.Ast.uf_fdecl in
let propagate_errno = ufunc.Ast.uf_propagate_errno in
let func_open = sprintf "%s\n{\n" (mk_ubridge_proto enclave_name fd.Ast.fname) in
let func_close = "\treturn SGX_SUCCESS;\n}\n" in
let set_errno = if propagate_errno then "\tms->ocall_errno = errno;" else "" in
let ms_struct_name = mk_ms_struct_name fd.Ast.fname in
let declare_ms_ptr = sprintf "%s* %s = SGX_CAST(%s*, %s);"
ms_struct_name
ms_struct_val
ms_struct_name
ms_ptr_name in
let call_with_pms =
let invoke_func = gen_func_invoking fd mk_parm_name_ubridge in
if fd.Ast.rtype = Ast.Void then invoke_func
else sprintf "%s = %s" (mk_parm_accessor retval_name) invoke_func
in
if (is_naked_func fd) && (propagate_errno = false) then
let check_pms =
sprintf "if (%s != NULL) return SGX_ERROR_INVALID_PARAMETER;" ms_ptr_name
in
sprintf "%s\t%s\n\t%s\n%s" func_open check_pms call_with_pms func_close
else
sprintf "%s\t%s\n\t%s\n%s\n%s" func_open declare_ms_ptr call_with_pms set_errno func_close
let fill_ms_field (isptr: bool) (pd: Ast.pdecl) =
let accessor = if isptr then "->" else "." in
let (pt, declr) = pd in
let param_name = declr.Ast.identifier in
let ms_member_name = mk_ms_member_name param_name in
let assignment_str (aty: Ast.atype) =
sprintf "%s%s%s = %s;" ms_struct_val accessor ms_member_name param_name
in
let gen_setup_foreign_array aty =
sprintf "%s%s%s = (%s *)&%s[0];"
ms_struct_val accessor ms_member_name (Ast.get_tystr aty) param_name
in
let gen_setup_foreign_str aty =
sprintf "%s%s%s_len = %s ? strlen(%s) + 1 : 0;"
ms_struct_val accessor ms_member_name param_name param_name
in
let gen_setup_foreign_wstr aty =
sprintf "%s%s%s_len = %s ? (wcslen(%s) + 1) * sizeof(wchar_t) : 0;"
ms_struct_val accessor ms_member_name param_name param_name
in
if declr.Ast.array_dims = [] then
match pt with
Ast.PTVal(aty) -> assignment_str aty
| Ast.PTPtr(aty, pattr) ->
if pattr.Ast.pa_isary
then gen_setup_foreign_array aty
else if pattr.Ast.pa_isstr
then assignment_str aty ^ "\n\t" ^ gen_setup_foreign_str aty
else if pattr.Ast.pa_iswstr
then assignment_str aty ^ "\n\t" ^ gen_setup_foreign_wstr aty
else assignment_str aty
else
(* Arrays are passed by address. *)
let tystr = Ast.get_tystr (Ast.Ptr (Ast.get_param_atype pt)) in
sprintf "%s%s%s = (%s)%s;" ms_struct_val accessor ms_member_name tystr param_name
(* Generate untrusted proxy code for a given trusted function. *)
let gen_func_uproxy (tf: Ast.trusted_func) (idx: int) (ec: enclave_content) =
let fd = tf.Ast.tf_fdecl in
let func_open =
gen_uproxy_com_proto fd ec.enclave_name ^
"\n{\n\tsgx_status_t status;\n"
in
let func_close = "\treturn status;\n}\n" in
let ocall_table_name = mk_ocall_table_name ec.enclave_name in
let ms_struct_name = mk_ms_struct_name fd.Ast.fname in
let declare_ms_expr = sprintf "%s %s;" ms_struct_name ms_struct_val in
let ocall_table_ptr =
sprintf "&%s" ocall_table_name in
let sgx_ecall_fn = get_sgx_fname SGX_ECALL tf.Ast.tf_is_switchless in
(* Normal case - do ECALL with marshaling structure*)
let ecall_with_ms = sprintf "status = %s(%s, %d, %s, &%s);"
sgx_ecall_fn eid_name idx ocall_table_ptr ms_struct_val in
(* Rare case - the trusted function doesn't have parameter nor return value.
* In this situation, no marshaling structure is required - passing in NULL.
*)
let ecall_null = sprintf "status = %s(%s, %d, %s, NULL);"
sgx_ecall_fn eid_name idx ocall_table_ptr
in
let update_retval = sprintf "if (status == SGX_SUCCESS && %s) *%s = %s.%s;"
retval_name retval_name ms_struct_val ms_retval_name in
let func_body = ref [] in
if is_naked_func fd then
sprintf "%s\t%s\n%s" func_open ecall_null func_close
else
begin
func_body := declare_ms_expr :: !func_body;
List.iter (fun pd -> func_body := fill_ms_field false pd :: !func_body) fd.Ast.plist;
func_body := ecall_with_ms :: !func_body;
if fd.Ast.rtype <> Ast.Void then func_body := update_retval :: !func_body;
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") func_open (List.rev !func_body) ^ func_close
end
(* Generate an expression to check the pointers. *)
let mk_check_ptr (name: string) (lenvar: string) =
let checker = "CHECK_UNIQUE_POINTER"
in sprintf "\t%s(%s, %s);\n" checker name lenvar
(* Pointer to marshaling structure should never be NULL. *)
let mk_check_pms (fname: string) =
let lenvar = sprintf "sizeof(%s)" (mk_ms_struct_name fname)
in sprintf "\t%s(%s, %s);%s" "CHECK_REF_POINTER" ms_ptr_name lenvar
"\n\t//\n\t// fence after pointer checks\n\t//\n\tsgx_lfence();\n"
(* Generate code to get the size of the pointer. *)
let gen_ptr_size (ty: Ast.atype) (pattr: Ast.ptr_attr) (name: string) (get_parm: string -> string) =
let len_var = mk_len_var name in
let parm_name = get_parm name in
let mk_len_size v =
match v with
Ast.AString s -> get_parm s
| Ast.ANumber n -> sprintf "%d" n in
let mk_len_count v size_str =
match v with
Ast.AString s -> sprintf "%s * %s" (get_parm s) size_str
| Ast.ANumber n -> sprintf "%d * %s" n size_str in
(* size_str:
[size = n] -> n
int ptr[] -> sizeof(int)
int* ptr -> sizeof(int)
int **ptr -> sizeof(int* )
Mystruct struct -> sizeof(Mystruct)
pMystruct ptr -> sizeof( *ptr)
*)
let do_ps_attribute (sattr: Ast.ptr_size) =
let size_str =
match sattr.Ast.ps_size with
Some a -> mk_len_size a
| None ->
match ty with
Ast.Ptr ty ->
sprintf "sizeof(%s)" (Ast.get_tystr ty)
| _ ->
if pattr.Ast.pa_isptr then
sprintf "sizeof(*%s)" parm_name
else
sprintf "sizeof(%s)" (Ast.get_tystr ty)
in
match sattr.Ast.ps_count with
None -> size_str
| Some a -> mk_len_count a size_str
in
sprintf "size_t %s = %s;\n"
len_var
(if pattr.Ast.pa_isary then
sprintf "sizeof(%s)" (Ast.get_tystr ty)
else
(* genrerate ms_parm_len only for ecall with string/wstring in _t.c.*)
if (pattr.Ast.pa_isstr || pattr.Ast.pa_iswstr) && parm_name <> name then
sprintf "%s_len " (mk_parm_accessor name)
else
(* genrerate strlen(param)/wcslen(param) only for ocall with string/wstring in _t.c.*)
if pattr.Ast.pa_isstr then
sprintf "%s ? strlen(%s) + 1 : 0" parm_name parm_name
else
if pattr.Ast.pa_iswstr then
sprintf "%s ? (wcslen(%s) + 1) * sizeof(wchar_t) : 0" parm_name parm_name
else do_ps_attribute pattr.Ast.pa_size)
(* Find the data type of a parameter. *)
let find_param_type (name: string) (plist: Ast.pdecl list) =
try
let (pt, _) = List.find (fun (pd: Ast.pdecl) ->
let (pt, declr) = pd
in declr.Ast.identifier = name) plist
in get_param_tystr pt
with
Not_found -> failwithf "parameter `%s' not found." name
(* Generate code to check the length of buffers. *)
let gen_check_tbridge_length_overflow (plist: Ast.pdecl list) =
let gen_check_length (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let tmp_ptr_name= mk_tmp_var name in
let mk_len_size v =
match v with
Ast.AString s -> mk_tmp_var s
| Ast.ANumber n -> sprintf "%d" n in
let gen_check_overflow cnt size_str =
let if_statement =
match cnt with
Ast.AString s -> sprintf "\tif (%s != 0 &&\n\t\t(size_t)%s > (SIZE_MAX / %s)) {\n" size_str (mk_tmp_var s) size_str
| Ast.ANumber n -> sprintf "\tif (%s != 0 &&\n\t\t%d > (SIZE_MAX / %s)) {\n" size_str n size_str
in
sprintf "%s\t\treturn SGX_ERROR_INVALID_PARAMETER;\n\t}" if_statement
in
let size_str =
match attr.Ast.pa_size.Ast.ps_size with
Some a -> mk_len_size a
| None -> sprintf "sizeof(*%s)" tmp_ptr_name
in
match attr.Ast.pa_size.Ast.ps_count with
None -> ""
| Some a -> sprintf "%s\n\n" (gen_check_overflow a size_str)
in
List.fold_left
(fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr(ty, attr) -> acc ^ gen_check_length ty attr declr) "" plist
(* Generate code to check all function parameters which are pointers. *)
let gen_check_tbridge_ptr_parms (plist: Ast.pdecl list) =
let gen_check_ptr (ty: Ast.atype) (pattr: Ast.ptr_attr) (declr: Ast.declarator) =
if not pattr.Ast.pa_chkptr then ""
else
let name = declr.Ast.identifier in
let len_var = mk_len_var name in
let parm_name = mk_tmp_var name in
if pattr.Ast.pa_chkptr
then mk_check_ptr parm_name len_var
else ""
in
let new_param_list = List.map conv_array_to_ptr plist
in
let pointer_checkings =
List.fold_left
(fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr(ty, attr) -> acc ^ gen_check_ptr ty attr declr) "" new_param_list
in
if pointer_checkings = "" then ""
else pointer_checkings ^ "\n\t//\n\t// fence after pointer checks\n\t//\n\tsgx_lfence();\n"
(* If a foreign type is a readonly pointer, we cast it to 'void*' for memcpy() and free() *)
let mk_in_ptr_dst_name (ty: Ast.atype) (attr: Ast.ptr_attr) (ptr_name: string) =
let rdonly =
match ty with
Ast.Foreign _ -> attr.Ast.pa_rdonly
| _ -> false
in
if rdonly then "(void*)" ^ ptr_name
else ptr_name
let gen_struct_ptr_size (ty: Ast.atype) (pattr: Ast.ptr_attr) (name: string) (get_parm: string -> string) =
let mk_len_size v =
match v with
Ast.AString s -> get_parm s
| Ast.ANumber n -> sprintf "%d" n in
let mk_len_count v size_str =
match v with
Ast.AString s -> sprintf "%s * %s" (get_parm s) size_str
| Ast.ANumber n -> sprintf "%d * %s" n size_str in
let do_ps_attribute (sattr: Ast.ptr_size) =
let size_str =
match sattr.Ast.ps_size with
Some a -> mk_len_size a
| None ->
match ty with
Ast.Ptr ty ->
sprintf "sizeof(%s)" (Ast.get_tystr ty)
| _ ->
if pattr.Ast.pa_isptr then
sprintf "sizeof(*%s)" (get_parm name)
else
sprintf "sizeof(%s)" (Ast.get_tystr ty)
in
match sattr.Ast.ps_count with
None -> size_str
| Some a -> mk_len_count a size_str
in
sprintf "%s" (do_ps_attribute pattr.Ast.pa_size)
(* Generate code to check the length of buffers in structure. *)
let gen_check_member_length (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) (get_parm: string -> string) (indent: string) (break_out: string list) =
let name = declr.Ast.identifier in
let mk_len_size v =
match v with
Ast.AString s -> get_parm s
| Ast.ANumber n -> sprintf "%d" n in
let gen_check_overflow cnt size_str =
let if_statement =
match cnt with
Ast.AString s -> sprintf "if (%s != 0 &&\n%s\t\t(size_t)%s > (SIZE_MAX / %s)) {\n" size_str indent (get_parm s) size_str
| Ast.ANumber n -> sprintf "if (%s != 0 &&\n%s\t\t%d > (SIZE_MAX / %s)) {\n" size_str indent n size_str
in
(List.fold_left (fun acc s -> acc ^ indent ^ s ^ "\n") if_statement break_out) ^ indent ^ "}"
in
let size_str =
match attr.Ast.pa_size.Ast.ps_size with
Some a -> mk_len_size a
| None ->
match ty with
Ast.Ptr ty ->
sprintf "sizeof(%s)" (Ast.get_tystr ty)
| _ ->
if attr.Ast.pa_isptr then
sprintf "sizeof(*%s)" (get_parm name)
else
sprintf "sizeof(%s)" (Ast.get_tystr ty)
in
match attr.Ast.pa_size.Ast.ps_count with
None -> ""
| Some a -> (gen_check_overflow a size_str)
(* Generate the code to handle structure pointer deep copy,
* which is to be inserted before actually calling the trusted function.
*)
let gen_struct_ptr_direction_pre_calculate (param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let in_struct_name = mk_in_var struct_name in
let in_ptr_name = mk_in_var2 struct_name name in
let len_var = mk_len_var2 struct_name name in
let in_struct = sprintf "(%s + i)->%s" in_struct_name in
let in_struct_member = sprintf "%s" (in_struct name) in
let gen_tmp_var_for_out =
match param_direction with
Ast.PtrInOut ->
[
sprintf "if (ADD_ASSIGN_OVERFLOW(_%s_malloc_size, sizeof(void*) + sizeof(size_t))) {" struct_name;
"\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\tgoto err;";
"}";
]
| _ -> []
in
let check_size =
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) | Ast.Ptr(Ast.Struct(_)) -> []
| _ ->
[
sprintf "\tif (%s %% sizeof(*%s) != 0) {" len_var in_ptr_name; (* "size x count" is a multiple of sizeof type *)
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tgoto err;";
"\t}";
]
in
let code_template =
[
gen_check_member_length ty attr declr in_struct "\t\t\t" ["\tstatus = SGX_ERROR_INVALID_PARAMETER;";"\tgoto err;"];
sprintf "if (%s != NULL && (%s = %s) != 0) {" in_struct_member len_var (gen_struct_ptr_size ty attr name in_struct);
sprintf "\tif (!sgx_is_outside_enclave(%s, %s)) {" in_struct_member len_var;
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tgoto err;";
"\t}\n";
]
@ check_size @
[
sprintf "\tif (ADD_ASSIGN_OVERFLOW(_%s_malloc_size, %s)) {" struct_name len_var;
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tgoto err;";
"\t}";
"}";
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "" (gen_tmp_var_for_out @ code_template)
(* Generate the code to handle structure pointer deep copy,
* which is to be inserted before actually calling the trusted function.
*)
let gen_struct_ptr_direction_pre_copy (param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let in_struct_name = mk_in_var struct_name in
let in_ptr_name = mk_in_var2 struct_name name in
let len_var = mk_len_var2 struct_name name in
let in_ptr_dst_name = mk_in_ptr_dst_name ty attr in_ptr_name in
let in_struct = sprintf "(%s + i)->%s" in_struct_name in
let in_struct_member = sprintf "%s" (in_struct name) in
let code_template =
let gen_tmp_var_for_out =
match param_direction with
Ast.PtrInOut ->
[
sprintf "*(%s*)__tmp_%s = %s;" (Ast.get_tystr ty) in_struct_name in_ptr_name ;
sprintf "__tmp_%s = (void *)((size_t)__tmp_%s + sizeof(void*));" in_struct_name in_struct_name;
sprintf "*(size_t*)__tmp_%s = %s;" in_struct_name len_var;
sprintf "__tmp_%s= (void *)((size_t)__tmp_%s + sizeof(size_t));" in_struct_name in_struct_name;
]
| _ -> []
in
let check_size =
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) | Ast.Ptr(Ast.Struct(_)) -> []
| _ ->
[
sprintf "\tif (%s %% sizeof(*%s) != 0) {" len_var in_struct_member; (* "size x count" is a multiple of sizeof type *)
"\t\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\t\tgoto err;";
"\t}";
]
in
[
sprintf "%s = %s;" in_ptr_name in_struct_member;
sprintf "%s = %s;" len_var (gen_struct_ptr_size ty attr name in_struct);
] @ gen_tmp_var_for_out @
[
sprintf "if (%s != NULL && %s != 0) {" in_ptr_dst_name len_var;
] @ check_size @
[
sprintf "\tif (memcpy_s(__tmp_%s, %s, %s, %s)) {" in_struct_name len_var in_struct_member len_var;
"\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tgoto err;";
sprintf "\t}";
sprintf "\t%s = __tmp_%s;" in_struct_member in_struct_name;
sprintf "\t__tmp_%s = (void *)((size_t)__tmp_%s + %s);" in_struct_name in_struct_name len_var ;
"}";
"else";
sprintf "\t%s = NULL;" in_struct_member;
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "" code_template
(* Generate the code to handle function pointer parameter direction,
* which is to be inserted before actually calling the trusted function.
*)
let gen_parm_ptr_direction_pre (plist: Ast.pdecl list) =
let clone_in_ptr (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let is_ary = (Ast.is_array declr || attr.Ast.pa_isary) in
let in_ptr_name = mk_in_var name in
let in_ptr_type = sprintf "%s%s" (Ast.get_tystr ty) (if is_ary then "*" else "") in
let len_var = mk_len_var name in
let in_ptr_dst_name = mk_in_ptr_dst_name ty attr in_ptr_name in
let tmp_ptr_name= mk_tmp_var name in
let malloc_and_copy pre_indent =
let check_size =
if attr.Ast.pa_isstr then [] else
if attr.Ast.pa_iswstr then
[
sprintf "\tif (%s %% sizeof(wchar_t) != 0)" len_var;
"\t{";
"\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tgoto err;";
"\t}";
]
else
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) -> []
| Ast.Ptr(Ast.Struct(struct_type)) ->
if is_structure_defined struct_type then
let (struct_def, deep_copy)= get_struct_def struct_type
in
if deep_copy then
[
sprintf "\tif ( %s %% sizeof(*%s) != 0)" len_var tmp_ptr_name;
"\t{";
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tgoto err;";
"\t}";
]
else []
else []
| _ ->
[
sprintf "\tif ( %s %% sizeof(*%s) != 0)" len_var tmp_ptr_name;
"\t{";
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tgoto err;";
"\t}";
]
in
match attr.Ast.pa_direction with
Ast.PtrIn | Ast.PtrInOut ->
let struct_deep_copy_pre =
let struct_calculate =
(invoke_if_struct ty attr.Ast.pa_direction name
(fun struct_type name ->
sprintf "\t\tfor (i = 0; i < %s / sizeof(struct %s); i++){\n" (mk_len_var name) struct_type
)
gen_struct_ptr_direction_pre_calculate "\t\t}\n")
in
let struct_malloc =
let code_template = [
sprintf "\t__tmp_%s = malloc(_%s_malloc_size);"(mk_in_var name) name;
sprintf "\tif (__tmp_%s == NULL) {" (mk_in_var name);
"\t\tstatus = SGX_ERROR_OUT_OF_MEMORY;";
"\t\tgoto err;";
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
in
let struct_copy =
(invoke_if_struct ty attr.Ast.pa_direction name
(fun struct_type name ->
sprintf "\t\t_in_member_%s = __tmp_%s;\n\t\tfor (i = 0; i < %s / sizeof(struct %s); i++){\n" name (mk_in_var name) (mk_len_var name) struct_type
) gen_struct_ptr_direction_pre_copy "\t\t}\n")
in
struct_calculate ^ (if struct_calculate = "" then "" else struct_malloc) ^ struct_copy
in
let code_template =
[
sprintf "if (%s != NULL && %s != 0) {" tmp_ptr_name len_var;
]
@ check_size @
[
sprintf "\t%s = (%s)malloc(%s);" in_ptr_name in_ptr_type len_var;
sprintf "\tif (%s == NULL) {" in_ptr_name;
"\t\tstatus = SGX_ERROR_OUT_OF_MEMORY;";
"\t\tgoto err;";
"\t}\n";
sprintf "\tif (memcpy_s(%s, %s, %s, %s)) {" in_ptr_dst_name len_var tmp_ptr_name len_var;
"\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tgoto err;";
sprintf "\t}\n%s" struct_deep_copy_pre;
]
in
let s1 = List.fold_left (fun acc s -> acc ^ pre_indent ^ s ^ "\n") "" code_template in
let s2 =
if attr.Ast.pa_isstr then
let code_template2 = [
sprintf "\t%s[%s - 1] = '\\0';" in_ptr_name len_var;
sprintf "\tif (%s != strlen(%s) + 1)" len_var in_ptr_name;
"\t{";
"\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tgoto err;";
"\t}";
]
in
s1 ^ List.fold_left (fun acc s -> acc ^ pre_indent ^ s ^ "\n") "" code_template2
else if attr.Ast.pa_iswstr then
let code_template3 = [
sprintf "\t%s[(%s - sizeof(wchar_t))/sizeof(wchar_t)] = (wchar_t)0;" in_ptr_name len_var;
sprintf "\tif ( %s / sizeof(wchar_t) != wcslen(%s) + 1)" len_var in_ptr_name;
"\t{";
"\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tgoto err;";
"\t}";
]
in
s1 ^ List.fold_left (fun acc s -> acc ^ pre_indent ^ s ^ "\n") "" code_template3
else s1 in
sprintf "%s\t}\n" s2
| Ast.PtrOut ->
let code_template =
[
sprintf "if (%s != NULL && %s != 0) {" tmp_ptr_name len_var;
]
@ check_size @
[
sprintf "\tif ((%s = (%s)malloc(%s)) == NULL) {" in_ptr_name in_ptr_type len_var;
"\t\tstatus = SGX_ERROR_OUT_OF_MEMORY;";
"\t\tgoto err;";
"\t}\n";
sprintf "\tmemset((void*)%s, 0, %s);" in_ptr_name len_var;
"}"]
in
List.fold_left (fun acc s -> acc ^ pre_indent ^ s ^ "\n") "" code_template
| _ -> ""
in
malloc_and_copy "\t"
in List.fold_left
(fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr (ty, attr) -> acc ^ clone_in_ptr ty attr declr) "" plist
(* Generate the code to handle tructure pointer deep copy,
* which is to be inserted after finishing calling the trusted function.
*)
let gen_struct_ptr_direction_post (param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let in_struct_name = mk_in_var struct_name in
let in_ptr_name = mk_in_var2 struct_name name in
let in_struct = sprintf "(%s + i)->%s" in_struct_name in
let in_struct_member = sprintf "%s" (in_struct name) in
let in_len_ptr_var = mk_len_var2 struct_name name in
let out_len_ptr_var = mk_len_var2 ("out_" ^ struct_name) name in
match param_direction with
Ast.PtrIn -> ""
| Ast.PtrInOut ->
let check_size =
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) | Ast.Ptr(Ast.Struct(_)) -> ""
| _ -> sprintf " || (%s %% sizeof(*%s) != 0)" in_len_ptr_var in_struct_member (* "size x count" is a multiple of sizeof type *)
in
let code_template = [
sprintf "%s = *(%s*)__tmp_%s;" in_ptr_name (Ast.get_tystr ty) in_struct_name;
sprintf "__tmp_%s = (void *)((size_t)__tmp_%s + sizeof(void*));" in_struct_name in_struct_name;
sprintf "%s = *(size_t*)__tmp_%s;" in_len_ptr_var in_struct_name;
sprintf "__tmp_%s = (void *)((size_t)__tmp_%s + sizeof(size_t));" in_struct_name in_struct_name;
gen_check_member_length ty attr declr in_struct "\t\t\t" ["\tstatus = SGX_ERROR_INVALID_PARAMETER;";"\tbreak;"];
sprintf "size_t %s = %s;" out_len_ptr_var (gen_struct_ptr_size ty attr name in_struct);
sprintf "if(%s!= NULL &&" in_struct_member;
sprintf "\t\t%s != 0) {" out_len_ptr_var;
sprintf "\tif (%s > %s%s) {" out_len_ptr_var in_len_ptr_var check_size;
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tbreak;";
"\t}";
sprintf "\tif (!sgx_is_within_enclave(%s, %s) ||" in_struct_member out_len_ptr_var;
sprintf "\t\t\t!sgx_is_outside_enclave(%s, %s)) {" in_ptr_name in_len_ptr_var;
"\t\tstatus = SGX_ERROR_INVALID_PARAMETER;";
"\t\tbreak;";
"\t}";
sprintf "\tif (memcpy_s(%s, %s, %s, %s)) {" in_ptr_name in_len_ptr_var in_struct_member out_len_ptr_var;
sprintf "\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\tbreak;";
"\t}";
"}";
sprintf "%s = %s;" in_struct_member in_ptr_name;
sprintf "__tmp_%s = (void *)((size_t)__tmp_%s + %s);" in_struct_name in_struct_name in_len_ptr_var;
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "" code_template
| _ -> ""
(* Generate the code to handle function pointer parameter direction,
* which is to be inserted after finishing calling the trusted function.
*)
let gen_parm_ptr_direction_post (plist: Ast.pdecl list) =
let copy_and_free (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let in_ptr_name = mk_in_var name in
let len_var = mk_len_var name in
let struct_deep_copy_post =
let pre struct_type name =
let code_template = [
sprintf "__tmp_%s = _in_member_%s;" (mk_in_var name) name;
sprintf "for (i = 0; i < %s / sizeof(struct %s); i++){" (mk_len_var name) struct_type;
"\tif (status != SGX_SUCCESS)";
"\t\tbreak;";
]
in
List.fold_left (fun acc s -> acc ^ "\t\t" ^ s ^ "\n") "" code_template
in
invoke_if_struct ty attr.Ast.pa_direction name pre gen_struct_ptr_direction_post "\t\t}\n"
in
match attr.Ast.pa_direction with
Ast.PtrIn -> ""
| Ast.PtrInOut | Ast.PtrOut ->
if attr.Ast.pa_isstr then
let code_template = [
sprintf "\tif (%s)" in_ptr_name;
"\t{";
sprintf "\t\t%s[%s - 1] = '\\0';" in_ptr_name len_var;
sprintf "\t\t%s = strlen(%s) + 1;" len_var in_ptr_name;
sprintf "\t\tif (memcpy_s((void*)%s, %s, %s, %s)) {" (mk_tmp_var name) len_var in_ptr_name len_var;
"\t\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\t\tgoto err;";
"\t\t}";
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ s ^ "\n") "" code_template
else if attr.Ast.pa_iswstr then
let code_template = [
sprintf "\tif (%s)" in_ptr_name;
"\t{";
sprintf "\t\t%s[(%s - sizeof(wchar_t))/sizeof(wchar_t)] = (wchar_t)0;" in_ptr_name len_var;
sprintf "\t\t%s = (wcslen(%s) + 1) * sizeof(wchar_t);" len_var in_ptr_name;
sprintf "\t\tif (memcpy_s((void*)%s, %s, %s, %s)) {" (mk_tmp_var name) len_var in_ptr_name len_var;
"\t\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\t\tgoto err;";
"\t\t}";
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ s ^ "\n") "" code_template
else
let code_template = [
sprintf "\tif (%s) {" in_ptr_name;
sprintf "%s\t\tif (memcpy_s(%s, %s, %s, %s)) {" struct_deep_copy_post (mk_tmp_var name) len_var in_ptr_name len_var;
"\t\t\tstatus = SGX_ERROR_UNEXPECTED;";
"\t\t\tgoto err;";
"\t\t}";
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ s ^ "\n") "" code_template
| _ -> ""
in
List.fold_left
(fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr (ty, attr) -> acc ^ copy_and_free ty attr declr) "" plist
(* Generate an "err:" goto mark if necessary. *)
let gen_err_mark (plist: Ast.pdecl list) =
let has_inout_p (attr: Ast.ptr_attr): bool =
attr.Ast.pa_direction <> Ast.PtrNoDirection
in
if List.exists (fun (pt, name) ->
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr(_, attr) -> has_inout_p attr) plist
then "err:"
else ""
(* Generate the code to handle function pointer parameter direction,
* which is to be inserted after finishing calling the trusted function.
*)
let gen_parm_ptr_free_post (plist: Ast.pdecl list) =
let copy_and_free (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let in_ptr_name = mk_in_var name in
let in_ptr_dst_name = mk_in_ptr_dst_name ty attr in_ptr_name in
match attr.Ast.pa_direction with
Ast.PtrIn ->
let struct_free =
match ty with
Ast.Ptr(Ast.Struct(struct_type)) ->
if is_structure_defined struct_type then
let (_, deep_copy)= get_struct_def struct_type
in
if deep_copy then
sprintf "\tif (_in_member_%s) free(_in_member_%s);\n" name name
else ""
else ""
| _ -> ""
in
sprintf "\tif (%s) free(%s);\n%s" in_ptr_name in_ptr_dst_name struct_free
| Ast.PtrInOut | Ast.PtrOut ->
sprintf "\tif (%s) free(%s);\n" in_ptr_name in_ptr_name
| _ -> ""
in
List.fold_left
(fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr (ty, attr) -> acc ^ copy_and_free ty attr declr) "" plist
(* It is used to save the parameters used as the value of size/count attribute. *)
let param_cache = Hashtbl.create 1
let is_in_param_cache s = Hashtbl.mem param_cache s
(* Try to generate a temporary value to save the size of the buffer. *)
let gen_tmp_size (pattr: Ast.ptr_attr) (plist: Ast.pdecl list) =
let do_gen_temp_var (s: string) =
if is_in_param_cache s then ""
else
let param_tystr = find_param_type s plist in
let tmp_var = mk_tmp_var s in
let parm_str = mk_parm_accessor s in
Hashtbl.add param_cache s true;
sprintf "\t%s %s = %s;\n" param_tystr tmp_var parm_str
in
let gen_temp_var (v: Ast.attr_value) =
match v with
Ast.ANumber _ -> ""
| Ast.AString s -> do_gen_temp_var s
in
let tmp_size_str =
match pattr.Ast.pa_size.Ast.ps_size with
Some v -> gen_temp_var v
| None -> ""
in
let tmp_count_str =
match pattr.Ast.pa_size.Ast.ps_count with
Some v -> gen_temp_var v
| None -> ""
in
sprintf "%s%s" tmp_size_str tmp_count_str
let is_ptr (pt: Ast.parameter_type) =
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr _ -> true
let is_ptr_type (aty: Ast.atype) =
match aty with
Ast.Ptr _ -> true
| _ -> false
let ptr_has_direction (pt: Ast.parameter_type) =
match pt with
Ast.PTVal _ -> false
| Ast.PTPtr(_, a) -> a.Ast.pa_direction <> Ast.PtrNoDirection
let tbridge_mk_parm_name_ext (pt: Ast.parameter_type) (declr: Ast.declarator) =
let cast_expr =
let tystr = get_param_tystr pt in
if Ast.is_array declr && List.length declr.Ast.array_dims > 1
then
let dims = get_array_dims (List.tl declr.Ast.array_dims) in
sprintf "(%s (*)%s)" tystr dims
else if is_const_ptr pt then
sprintf "(const %s)" tystr
else ""
in
if is_in_param_cache declr.Ast.identifier || (is_ptr pt && (not (is_foreign_array pt)))
then
if ptr_has_direction pt
then cast_expr ^ mk_in_var declr.Ast.identifier
else cast_expr ^ mk_tmp_var declr.Ast.identifier
else mk_parm_name_ext pt declr
let mk_parm_name_tbridge (pt: Ast.parameter_type) (declr: Ast.declarator) =
add_foreign_array_ptrref tbridge_mk_parm_name_ext pt declr
(* Generate local variables required for the trusted bridge. *)
let gen_tbridge_local_vars (plist: Ast.pdecl list) =
let status_var = "\tsgx_status_t status = SGX_SUCCESS;\n" in
let do_gen_local_var (pt: Ast.parameter_type) (attr: Ast.ptr_attr) (name: string) =
let qual = if is_const_ptr pt then "const " else "" in
let ty = Ast.get_param_atype pt in
let tmp_var =
(* Save a copy of pointer in case it might be modified in the marshaling structure. *)
sprintf "\t%s%s %s = %s;\n" qual (Ast.get_tystr ty) (mk_tmp_var name) (mk_parm_accessor name)
in
let len_var =
if not attr.Ast.pa_chkptr then ""
else gen_tmp_size attr plist ^ "\t" ^ gen_ptr_size ty attr name mk_tmp_var in
let in_ptr =
match attr.Ast.pa_direction with
Ast.PtrNoDirection -> ""
| _ -> sprintf "\t%s %s = NULL;\n" (Ast.get_tystr ty) (mk_in_var name)
in
let in_ptr_struct_var =
if not attr.Ast.pa_chkptr then ""
else
let gen_struct_local_var (param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator)=
let in_ptr_name = mk_in_var2 struct_name declr.Ast.identifier in
let in_len_ptr_name = mk_len_var2 struct_name declr.Ast.identifier in
sprintf "\t%s %s = NULL;\n\tsize_t %s = 0;\n" (Ast.get_tystr ty) in_ptr_name in_len_ptr_name
in
invoke_if_struct ty attr.Ast.pa_direction name (fun _ name -> sprintf "\tvoid* __tmp_%s = NULL;\n\tsize_t _%s_malloc_size = 0;\n\tvoid* _in_member_%s = NULL;\n" (mk_in_var name) name name) gen_struct_local_var ""
in
(tmp_var ^ len_var ^ in_ptr ^ in_ptr_struct_var, if in_ptr_struct_var <> "" then true else false)
in
let gen_local_var_for_foreign_array (ty: Ast.atype) (attr: Ast.ptr_attr) (name: string) =
let tystr = Ast.get_tystr ty in
let tmp_var =
sprintf "\t%s* %s = %s;\n" tystr (mk_tmp_var name) (mk_parm_accessor name)
in
let len_var = sprintf "\tsize_t %s = sizeof(%s);\n" (mk_len_var name) tystr
in
let in_ptr = sprintf "\t%s* %s = NULL;\n" tystr (mk_in_var name)
in
match attr.Ast.pa_direction with
Ast.PtrNoDirection -> ""
| _ -> tmp_var ^ len_var ^ in_ptr
in
let gen_local_var (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ("", false)
| Ast.PTPtr (ty, attr) ->
if is_foreign_array pty
then (gen_local_var_for_foreign_array ty attr declr.Ast.identifier, false)
else do_gen_local_var pty attr declr.Ast.identifier
in
let new_param_list = List.map conv_array_to_ptr plist
in
let (str, deep_copy) =
Hashtbl.clear param_cache;
List.fold_left (fun acc pd -> let (str, deep_copy) = (gen_local_var pd) in (fst acc ^ str, snd acc || deep_copy)) (status_var, false) new_param_list
in
str ^ if deep_copy then "\tsize_t i = 0;\n" else ""
(* It generates trusted bridge code for a trusted function. *)
let gen_func_tbridge (fd: Ast.func_decl) (dummy_var: string) =
let func_open = sprintf "static sgx_status_t SGX_CDECL %s(void* %s)\n{\n"
(mk_tbridge_name fd.Ast.fname)
ms_ptr_name in
let local_vars = gen_tbridge_local_vars fd.Ast.plist in
let func_close = "\treturn status;\n}\n" in
let ms_struct_name = mk_ms_struct_name fd.Ast.fname in
let declare_ms_ptr = sprintf "%s* %s = SGX_CAST(%s*, %s);"
ms_struct_name
ms_struct_val
ms_struct_name
ms_ptr_name in
let invoke_func = gen_func_invoking fd mk_parm_name_tbridge in
let update_retval = sprintf "%s = %s"
(mk_parm_accessor retval_name)
invoke_func in
if is_naked_func fd then
let check_pms =
sprintf "if (%s != NULL) return SGX_ERROR_INVALID_PARAMETER;" ms_ptr_name
in
sprintf "%s%s%s\t%s\n\t%s\n%s" func_open local_vars dummy_var check_pms invoke_func func_close
else
sprintf "%s%s\t%s\n%s\n%s%s\n%s\n\t%s\n%s\n%s\n%s%s"
func_open
(mk_check_pms fd.Ast.fname)
declare_ms_ptr
local_vars
(gen_check_tbridge_length_overflow fd.Ast.plist)
(gen_check_tbridge_ptr_parms fd.Ast.plist)
(gen_parm_ptr_direction_pre fd.Ast.plist)
(if fd.Ast.rtype <> Ast.Void then update_retval else invoke_func)
(gen_parm_ptr_direction_post fd.Ast.plist)
(gen_err_mark fd.Ast.plist)
(gen_parm_ptr_free_post fd.Ast.plist)
func_close
let tproxy_fill_ms_field (pd: Ast.pdecl) (is_ocall_switchless: bool) =
let (pt, declr) = pd in
let name = declr.Ast.identifier in
let len_var = mk_len_var name in
let parm_accessor = mk_parm_accessor name in
let sgx_ocfree_fn = get_sgx_fname SGX_OCFREE is_ocall_switchless in
match pt with
Ast.PTVal _ -> fill_ms_field true pd
| Ast.PTPtr(ty, attr) ->
let is_ary = (Ast.is_array declr || attr.Ast.pa_isary) in
let tystr = sprintf "%s%s%s" (if is_const_ptr pt then "const " else"")(get_param_tystr pt) (if is_ary then "*" else "") in
if not attr.Ast.pa_chkptr then (* [user_check] specified *)
if is_ary then sprintf "%s = SGX_CAST(%s, %s);" parm_accessor tystr name
else sprintf "%s = %s;" parm_accessor name
else
let check_size =
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) -> []
| Ast.Ptr(Ast.Struct(struct_type)) ->
if is_structure_defined struct_type then
let (_, deep_copy)= get_struct_def struct_type
in
if deep_copy then
[
sprintf "\tif (%s %% sizeof(*%s) != 0) {" len_var name;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_INVALID_PARAMETER;";
"\t}";
]
else []
else []
| _ ->
[
sprintf "\tif (%s %% sizeof(*%s) != 0) {" len_var name;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_INVALID_PARAMETER;";
"\t}";
]
in
let copy_out =
let deep_copy_out =
let pre struct_type name =
let code_template =[
sprintf "for (i = 0; i < %s / sizeof(struct %s); i++){" (mk_len_var name) struct_type;
sprintf "\t\tif (memcpy_s(&__local_%s, sizeof(__local_%s), %s + i, sizeof(struct %s))) {" name name name struct_type;
sprintf "\t\t\t%s();" sgx_ocfree_fn;
"\t\t\treturn SGX_ERROR_UNEXPECTED;";
"\t\t}";
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
in
let clear_member(param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let member_name = declr.Ast.identifier in
sprintf "\t\t\t__local_%s.%s = NULL;\n" struct_name member_name
in
let post =
let code_template =[
sprintf "\tif (memcpy_s((void *)((size_t)__tmp + sizeof(__local_%s) * i), sizeof(__local_%s), &__local_%s, sizeof(__local_%s))) {" name name name name;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;";
"\t}";
"}";
sprintf "memset(&__local_%s, 0, sizeof(__local_%s));" name name;
]
in
List.fold_left (fun acc s -> acc ^ "\t\t" ^ s ^ "\n") "" code_template
in
invoke_if_struct ty attr.Ast.pa_direction name pre clear_member post
in
let non_deep_copy_out =
let code_template =
[
sprintf "if (memcpy_s(__tmp, ocalloc_size, %s, %s)) {" name len_var;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;";
"\t}";
]
in List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
in
if deep_copy_out = "" then non_deep_copy_out else deep_copy_out
in
match attr.Ast.pa_direction with
Ast.PtrOut ->
let code_template =
[sprintf "if (%s != NULL) {" name;
sprintf "\t%s = (%s)__tmp;" parm_accessor tystr;
sprintf "\t__tmp_%s = __tmp;" name;
]
@ check_size @
[
sprintf "\tmemset(__tmp_%s, 0, %s);" name len_var;
sprintf "\t__tmp = (void *)((size_t)__tmp + %s);" len_var;
sprintf "\tocalloc_size -= %s;" len_var;
"} else {";
sprintf "\t%s = NULL;" parm_accessor;
"}"
]
in List.fold_left (fun acc s -> acc ^ s ^ "\n\t") "" code_template
| Ast.PtrInOut ->
let code_template =
[sprintf "if (%s != NULL) {" name;
sprintf "\t%s = (%s)__tmp;" parm_accessor tystr;
sprintf "\t__tmp_%s = __tmp;" name;
]
@ check_size @
[
sprintf "%s\t\t__tmp = (void *)((size_t)__tmp + %s);"copy_out len_var;
sprintf "\tocalloc_size -= %s;" len_var;
"} else {";
sprintf "\t%s = NULL;" parm_accessor;
"}"
]
in List.fold_left (fun acc s -> acc ^ s ^ "\n\t") "" code_template
| _ ->
let code_template =
[sprintf "if (%s != NULL) {" name;
sprintf "\t%s = (%s)__tmp;" parm_accessor tystr;
]
@ check_size @
[
sprintf "%s\t\t__tmp = (void *)((size_t)__tmp + %s);" copy_out len_var;
sprintf "\tocalloc_size -= %s;" len_var;
"} else {";
sprintf "\t%s = NULL;" parm_accessor;
"}"
]
in List.fold_left (fun acc s -> acc ^ s ^ "\n\t") "" code_template
(* Attach data pointed by structure member pointer at the end of ms. *)
let tproxy_fill_structure(pd: Ast.pdecl) (is_ocall_switchless: bool)=
let (pt, declr) = pd in
let name = declr.Ast.identifier in
let parm_accessor = mk_parm_accessor name in
let sgx_ocfree_fn = get_sgx_fname SGX_OCFREE is_ocall_switchless in
let fill_structure(param_direction: Ast.ptr_direction) (struct_type: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let member_name = declr.Ast.identifier in
let len_member_name = mk_len_var2 struct_name member_name in
let in_struct = sprintf "(%s + i)->%s" parm_accessor in
let in_struct_member = sprintf "%s" (in_struct member_name) in
let para_struct = sprintf "(%s + i)->%s" name in
let para_struct_member = sprintf "%s" (para_struct member_name) in
match param_direction with
| Ast.PtrInOut | Ast.PtrIn ->
let code_template =
[
sprintf "%s = %s;" len_member_name (gen_struct_ptr_size ty attr name para_struct);
sprintf "\tif (%s != NULL && %s != 0) {" para_struct_member len_member_name;
sprintf "\t\tif (memcpy_s(__tmp, %s, %s, %s)) {" len_member_name para_struct_member len_member_name;
sprintf "\t\t\t%s();" sgx_ocfree_fn;
"\t\t\treturn SGX_ERROR_UNEXPECTED;";
"\t\t}";
sprintf "\t\t%s = (%s)__tmp;" in_struct_member (Ast.get_tystr ty);
sprintf "\t\t__tmp = (void *)((size_t)__tmp + %s);" len_member_name;
sprintf "\t\tocalloc_size -= %s;" len_member_name;
"\t} else {";
sprintf "\t\t%s = NULL;" in_struct_member;
"\t}"
]
in List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "" code_template
| _ -> ""
in
match pt with
Ast.PTPtr(ty, attr) ->
let pre struct_type name =
let code_template =
if attr.Ast.pa_direction = Ast.PtrInOut then
[sprintf "if (%s != NULL && %s != 0 ) {" name (mk_len_var name);
sprintf "\t__tmp_member_%s = __tmp;" name;
sprintf "\tfor (i = 0; i < %s / sizeof(struct %s); i++){" (mk_len_var name) struct_type
]
else
[sprintf "if (%s != NULL && %s != 0 ) {" name (mk_len_var name);
sprintf "\tfor (i = 0; i < %s / sizeof(struct %s); i++){" (mk_len_var name) struct_type
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
in
invoke_if_struct ty attr.Ast.pa_direction name pre fill_structure "\t\t}\n\t}\n"
| _ -> ""
(* Generate local variables required for the trusted proxy, inclidng variables required by structure deep copy. *)
let gen_tproxy_local_vars (plist: Ast.pdecl list) =
let status_var = "sgx_status_t status = SGX_SUCCESS;\n" in
let do_gen_local_vars (ty: Ast.atype) (attr: Ast.ptr_attr) (name: string) =
let do_gen_local_var =
if not attr.Ast.pa_chkptr then ""
else "\t" ^ gen_ptr_size ty attr name (fun x -> x)
in
let in_ptr_struct_var =
if not attr.Ast.pa_chkptr then ""
else
let gen_in_ptr_struct_var (_: Ast.ptr_direction) (_: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let member_name = declr.Ast.identifier in
let in_len_ptr_name = mk_len_var2 struct_name member_name in
sprintf "\tsize_t %s = 0;\n" in_len_ptr_name
in
invoke_if_struct ty attr.Ast.pa_direction name (fun struct_type name ->
sprintf "\tstruct %s __local_%s;\n" struct_type name ^
if attr.Ast.pa_direction = Ast.PtrInOut then
sprintf"\tvoid* __tmp_member_%s = NULL;\n" name else
""
) gen_in_ptr_struct_var ""
in
(do_gen_local_var ^ in_ptr_struct_var, if in_ptr_struct_var <> "" then true else false)
in
let gen_local_var (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ("", false)
| Ast.PTPtr (ty, attr) -> do_gen_local_vars ty attr declr.Ast.identifier
in
let new_param_list = List.map conv_array_to_ptr plist
in
let (str, deep_copy) =
List.fold_left (fun acc pd -> let (str, deep_copy) = (gen_local_var pd) in (fst acc ^ str, snd acc || deep_copy)) (status_var, false) new_param_list
in
str ^ if deep_copy then "\tsize_t i = 0;\n" else ""
(* Generate only one ocalloc block required for the trusted proxy. *)
let gen_ocalloc_block (fname: string) (plist: Ast.pdecl list) (is_switchless: bool) =
let ms_struct_name = mk_ms_struct_name fname in
let new_param_list = List.map conv_array_to_ptr plist in
let local_vars_block = sprintf "%s* %s = NULL;\n\tsize_t ocalloc_size = sizeof(%s);\n\tvoid *__tmp = NULL;\n\n" ms_struct_name ms_struct_val ms_struct_name in
let local_var (ty: Ast.atype) (attr: Ast.ptr_attr) (name: string) =
if not attr.Ast.pa_chkptr then ""
else
match attr.Ast.pa_direction with
Ast.PtrOut | Ast.PtrInOut -> sprintf "\tvoid *__tmp_%s = NULL;\n" name
| _ -> ""
in
let do_local_var (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ""
| Ast.PTPtr (ty, attr) -> local_var ty attr declr.Ast.identifier
in
let mk_check_enclave_ptr (name: string) (lenvar: string) =
let checker = "CHECK_ENCLAVE_POINTER" in
sprintf "%s(%s, %s)" checker name lenvar
in
let check_enclave_ptr_block =
let check_enclave_ptr (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ""
| Ast.PTPtr (ty, attr) ->
if not attr.Ast.pa_chkptr then ""
else "\t" ^ mk_check_enclave_ptr declr.Ast.identifier (mk_len_var declr.Ast.identifier) ^ ";\n"
in
let do_check_enclave_ptr = List.fold_left (fun acc pd -> acc ^ check_enclave_ptr pd) "" new_param_list in
let break_line = if do_check_enclave_ptr = "" then "" else "\n" in
break_line ^ do_check_enclave_ptr ^ break_line
in
let count_ocalloc_size (ty: Ast.atype) (attr: Ast.ptr_attr) (name: string) =
if not attr.Ast.pa_chkptr then ""
else sprintf "\tif (ADD_ASSIGN_OVERFLOW(ocalloc_size, (%s != NULL) ? %s : 0))\n\t\treturn SGX_ERROR_INVALID_PARAMETER;\n" name (mk_len_var name)
in
let do_count_ocalloc_size (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ""
| Ast.PTPtr (ty, attr) -> count_ocalloc_size ty attr declr.Ast.identifier
in
let sgx_ocalloc_fn = get_sgx_fname SGX_OCALLOC is_switchless in
let sgx_ocfree_fn = get_sgx_fname SGX_OCFREE is_switchless in
let do_gen_ocalloc_block = [
sprintf "\n\t__tmp = %s(ocalloc_size);\n" sgx_ocalloc_fn;
"\tif (__tmp == NULL) {\n";
sprintf "\t\t%s();\n" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;\n";
"\t}\n";
sprintf "\t%s = (%s*)__tmp;\n" ms_struct_val ms_struct_name;
sprintf "\t__tmp = (void *)((size_t)__tmp + sizeof(%s));\n" ms_struct_name;
sprintf "\tocalloc_size -= sizeof(%s);\n" ms_struct_name;
]
in
let s1 = List.fold_left (fun acc pd -> acc ^ do_local_var pd) local_vars_block new_param_list in
let s2 = List.fold_left (fun acc pd -> acc ^ do_count_ocalloc_size pd) (s1 ^ check_enclave_ptr_block) new_param_list in
List.fold_left (fun acc s -> acc ^ s) s2 do_gen_ocalloc_block
(* Generate only one ocalloc block required for the trusted proxy. *)
let gen_ocalloc_block_struct_deep_copy (fname: string) (plist: Ast.pdecl list) (is_ocall_switchless: bool)=
let new_param_list = List.map conv_array_to_ptr plist in
let sgx_ocalloc_fn = get_sgx_fname SGX_OCALLOC is_ocall_switchless in
let sgx_ocfree_fn = get_sgx_fname SGX_OCFREE is_ocall_switchless in
let count_ocalloc_size (ty: Ast.atype) (attr: Ast.ptr_attr) (name: string) =
if not attr.Ast.pa_chkptr then ""
else
let count_struct_ocalloc_size =
let gen_member_size (_: Ast.ptr_direction) (_: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let in_len_ptr_var = mk_len_var2 struct_name declr.Ast.identifier in
let para_struct = sprintf "(%s + i)->%s" struct_name in
let check_size =
match ty with
Ast.Ptr(Ast.Void) | Ast.Ptr(Ast.Foreign(_)) | Ast.Foreign(_) | Ast.Ptr(Ast.Struct(_)) -> []
| _ ->
[
sprintf "if (%s %% sizeof(*%s) != 0) {" in_len_ptr_var (para_struct declr.Ast.identifier); (* "size x count" is a multiple of sizeof type *)
sprintf "\t%s();" sgx_ocfree_fn;
"\treturn SGX_ERROR_INVALID_PARAMETER;";
"}";
]
in
let code_template =
[
gen_check_member_length ty attr declr para_struct "\t\t\t" [(sprintf "\t%s();" sgx_ocfree_fn);"\treturn SGX_ERROR_INVALID_PARAMETER;"];
sprintf "%s = %s;" in_len_ptr_var (gen_struct_ptr_size ty attr struct_name para_struct);
]
@ check_size @
[
sprintf "if (%s && ! sgx_is_within_enclave(%s, %s)) {" (para_struct declr.Ast.identifier) (para_struct declr.Ast.identifier) in_len_ptr_var;
sprintf "\t%s();" sgx_ocfree_fn;
"\treturn SGX_ERROR_INVALID_PARAMETER;";
"}";
sprintf "\tif (ADD_ASSIGN_OVERFLOW(ocalloc_size, (%s != NULL) ? %s : 0)) {" (para_struct declr.Ast.identifier) in_len_ptr_var;
sprintf "\t%s();" sgx_ocfree_fn;
"\treturn SGX_ERROR_INVALID_PARAMETER;";
"}";
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "" code_template
in
invoke_if_struct ty attr.Ast.pa_direction name (fun struct_type name -> sprintf "\t\tfor (i = 0; i < %s / sizeof(struct %s); i++){\n" (mk_len_var name) struct_type) gen_member_size "\t\t}\n"
in
if count_struct_ocalloc_size <> "" then
sprintf "\tif (%s != NULL && %s != 0){\n" name (mk_len_var name) ^
sprintf "%s" count_struct_ocalloc_size ^
"\t}\n"
else ""
in
let do_count_ocalloc_size (pd: Ast.pdecl) =
let (pty, declr) = pd in
match pty with
Ast.PTVal _ -> ""
| Ast.PTPtr (ty, attr) -> count_ocalloc_size ty attr declr.Ast.identifier
in
let do_gen_ocalloc_block = [
sprintf "\n\t__tmp = %s(ocalloc_size);\n" sgx_ocalloc_fn;
"\tif (__tmp == NULL) {\n";
sprintf "\t\t%s();\n" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;\n";
"\t}\n";
]
in
let s2 = List.fold_left (fun acc pd -> acc ^ do_count_ocalloc_size pd) "" new_param_list in
if s2 = "" then ""
else
List.fold_left (fun acc s -> acc ^ s) s2 do_gen_ocalloc_block
(* Generate trusted proxy code for a given untrusted function. *)
let gen_func_tproxy (ufunc: Ast.untrusted_func) (idx: int) =
let fd = ufunc.Ast.uf_fdecl in
let propagate_errno = ufunc.Ast.uf_propagate_errno in
let func_open = sprintf "%s\n{\n" (gen_tproxy_proto fd) in
let local_vars = gen_tproxy_local_vars fd.Ast.plist in
let ocalloc_ms_struct = gen_ocalloc_block fd.Ast.fname fd.Ast.plist ufunc.Ast.uf_is_switchless in
let ocalloc_struct_deep_copy = gen_ocalloc_block_struct_deep_copy fd.Ast.fname fd.Ast.plist in
let sgx_ocfree_fn = get_sgx_fname SGX_OCFREE ufunc.Ast.uf_is_switchless in
let gen_ocfree rtype plist =
if rtype = Ast.Void && plist = [] && propagate_errno = false then "" else sprintf "\t%s();\n" sgx_ocfree_fn
in
let handle_out_ptr plist =
let copy_memory (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
match attr.Ast.pa_direction with
Ast.PtrInOut | Ast.PtrOut ->
if attr.Ast.pa_isstr then
let code_template = [
sprintf "\tif (%s) {" name;
sprintf "\t\tsize_t __tmp%s;" (mk_len_var name);
sprintf "\t\tif (memcpy_s((void*)%s, %s, __tmp_%s, %s)) {" name (mk_len_var name) name (mk_len_var name);
sprintf "\t\t\t%s();" sgx_ocfree_fn;
"\t\t\treturn SGX_ERROR_UNEXPECTED;";
"\t\t}";
sprintf "\t\t((char*)%s)[%s - 1] = '\\0';" name (mk_len_var name);
sprintf "\t\t__tmp%s = strlen(%s) + 1;" (mk_len_var name) name;
sprintf "\t\tmemset(%s +__tmp%s - 1, 0, %s -__tmp%s);" name (mk_len_var name) (mk_len_var name) (mk_len_var name);
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
else if attr.Ast.pa_iswstr then
let code_template = [
sprintf "\tif (%s) {" name;
sprintf "\t\tsize_t __tmp%s;" (mk_len_var name);
sprintf "\t\tif (memcpy_s((void*)%s, %s, __tmp_%s, %s)) {" name (mk_len_var name) name (mk_len_var name);
sprintf "\t\t\t%s();" sgx_ocfree_fn;
"\t\t\treturn SGX_ERROR_UNEXPECTED;";
"\t\t}";
sprintf "\t\t((wchar_t*)%s)[(%s - sizeof(wchar_t))/sizeof(wchar_t)] = (wchar_t)0;" name (mk_len_var name);
sprintf "\t\t__tmp%s = (wcslen(%s) + 1) * sizeof(wchar_t);" (mk_len_var name) name;
sprintf "\t\tmemset(((uint8_t*)%s) + __tmp%s - sizeof(wchar_t), 0, %s -__tmp%s);" name (mk_len_var name) (mk_len_var name) (mk_len_var name);
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
else
let struct_deep_copy_post =
let copy_and_free_struct_member (_: Ast.ptr_direction) (_: string) (struct_name: string) (ty: Ast.atype) (attr: Ast.ptr_attr) (declr: Ast.declarator) =
let name = declr.Ast.identifier in
let para_struct = sprintf "(%s + i)->%s" struct_name in
let para_struct_member = sprintf "%s" (para_struct name) in
let local_struct = sprintf "__local_%s.%s" struct_name in
let in_len_ptr_var = mk_len_var2 struct_name name in
let out_len_ptr_var = mk_len_var2 ("out_" ^ struct_name) name in
let check_size =
match ty with
Ast.Ptr(Ast.Void) -> ""
| _ -> sprintf " || (%s %% sizeof(*%s) != 0)" out_len_ptr_var para_struct_member (* "size x count" is a multiple of sizeof type *)
in
let code_template = [
sprintf "size_t %s = 0;" out_len_ptr_var;
gen_check_member_length ty attr declr local_struct "\t\t\t\t" [(sprintf "\t%s();" sgx_ocfree_fn);"\treturn SGX_ERROR_INVALID_PARAMETER;"];
sprintf "%s = %s;" in_len_ptr_var (gen_struct_ptr_size ty attr name para_struct);
sprintf "if(%s!= NULL &&" para_struct_member;
sprintf "\t\t(%s = %s) != 0) {" out_len_ptr_var (gen_struct_ptr_size ty attr name local_struct);
sprintf "\tif (%s != __tmp_member_%s ||" (local_struct name) struct_name;(*pointer is not changed by untrusted code *)
sprintf "\t\t\t%s > %s%s) {" out_len_ptr_var in_len_ptr_var check_size;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_INVALID_PARAMETER;";
"\t}";
sprintf "\tif (memcpy_s(%s, %s, __tmp_member_%s, %s)) {" para_struct_member in_len_ptr_var struct_name out_len_ptr_var;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;";
"\t}";
"}";
sprintf "%s = %s;" (local_struct name) para_struct_member;
sprintf "__tmp_member_%s = (void *)((size_t)__tmp_member_%s + (%s != NULL? %s : 0));" struct_name struct_name para_struct_member in_len_ptr_var;
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t\t" ^ s ^ "\n") "" code_template
in
let pre (struct_type: string) (name: string) =
let code_template2 = [
sprintf "for (i = 0; i < %s / sizeof(struct %s); i++){" (mk_len_var name) struct_type;
sprintf "\tif (memcpy_s(&__local_%s, sizeof(%s), ((%s*)__tmp_%s + i), sizeof(%s))) {"name struct_type struct_type name struct_type;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;";
"\t}";
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "\n" code_template2
in
let post =
let code_template3 = [
sprintf "\tif (memcpy_s((void*)(%s + i), sizeof(__local_%s), &__local_%s, sizeof(__local_%s))) {" name name name name;
sprintf "\t\t%s();" sgx_ocfree_fn;
"\t\treturn SGX_ERROR_UNEXPECTED;";
"\t}";
"}";
]
in
List.fold_left (fun acc s -> acc ^ "\t\t\t" ^ s ^ "\n") "\n" code_template3
in
invoke_if_struct ty attr.Ast.pa_direction name pre copy_and_free_struct_member post
in
let code_template =
if struct_deep_copy_post = "" then
[
sprintf "\tif (%s) {" name;
sprintf "\t\tif (memcpy_s((void*)%s, %s, __tmp_%s, %s)) {" name (mk_len_var name) name (mk_len_var name);
sprintf "\t\t\t%s();" sgx_ocfree_fn;
"\t\t\treturn SGX_ERROR_UNEXPECTED;";
"\t\t}";
"\t}" ;
]
else
[
sprintf "\tif (%s) {%s" name struct_deep_copy_post;
"\t}" ;
]
in
List.fold_left (fun acc s -> acc ^ "\t" ^ s ^ "\n") "" code_template
| _ -> ""
in
List.fold_left (fun acc (pty, declr) ->
match pty with
Ast.PTVal _ -> acc
| Ast.PTPtr(ty, attr) -> acc ^ copy_memory ty attr declr) "" plist in
let set_errno = if propagate_errno then "\t\terrno = ms->ocall_errno;\n" else "" in
let func_close = sprintf "%s%s%s\n%s%s\n"
(handle_out_ptr fd.Ast.plist)
set_errno
"\t}"
(gen_ocfree fd.Ast.rtype fd.Ast.plist)
"\treturn status;\n}" in
let sgx_ocall_fn = get_sgx_fname SGX_OCALL ufunc.Ast.uf_is_switchless in
let ocall_null = sprintf "status = %s(%d, NULL);\n" sgx_ocall_fn idx in
let ocall_with_ms = sprintf "status = %s(%d, %s);\n" sgx_ocall_fn idx ms_struct_val in
let update_retval = sprintf "\tif (%s) *%s = %s;"
retval_name retval_name (mk_parm_accessor retval_name) in
let func_body = ref [] in
if (is_naked_func fd) && (propagate_errno = false) then
sprintf "%s\t%s\t%s%s" func_open local_vars ocall_null "\n\treturn status;\n}"
else
begin
func_body := local_vars :: !func_body;
func_body := ocalloc_ms_struct:: !func_body;
List.iter (fun pd -> func_body := tproxy_fill_ms_field pd ufunc.Ast.uf_is_switchless :: !func_body ) fd.Ast.plist;
func_body := ocalloc_struct_deep_copy ufunc.Ast.uf_is_switchless :: !func_body;
List.iter (fun pd -> func_body := tproxy_fill_structure pd ufunc.Ast.uf_is_switchless:: !func_body) fd.Ast.plist;
func_body := ocall_with_ms :: !func_body;
func_body := "if (status == SGX_SUCCESS) {" :: !func_body;
if fd.Ast.rtype <> Ast.Void then func_body := update_retval :: !func_body;
List.fold_left (fun acc s -> if s = "" then acc else acc ^ "\t" ^ s ^ "\n") func_open (List.rev !func_body) ^ func_close
end
(* It generates OCALL table and the untrusted proxy to setup OCALL table. *)
let gen_ocall_table (ec: enclave_content) =
let func_proto_ubridge = List.map (fun (uf: Ast.untrusted_func) ->
let fd : Ast.func_decl = uf.Ast.uf_fdecl in
mk_ubridge_name ec.enclave_name fd.Ast.fname)
ec.ufunc_decls in
let nr_ocall = List.length ec.ufunc_decls in
let ocall_table_name = mk_ocall_table_name ec.enclave_name in
let ocall_table =
let ocall_members =
List.fold_left
(fun acc proto -> acc ^ "\t\t(void*)" ^ proto ^ ",\n") "" func_proto_ubridge
in "\t{\n" ^ ocall_members ^ "\t}\n"
in
sprintf "static const struct {\n\
\tsize_t nr_ocall;\n\
\tvoid * table[%d];\n\
} %s = {\n\
\t%d,\n\
%s};\n" (max nr_ocall 1)
ocall_table_name
nr_ocall
(if nr_ocall <> 0 then ocall_table else "\t{ NULL },\n")
(* It generates untrusted code to be saved in a `.c' file. *)
let gen_untrusted_source (ec: enclave_content) =
let code_fname = get_usource_name ec.file_shortnm in
let include_hd = "#include \"" ^ get_uheader_short_name ec.file_shortnm ^ "\"\n" in
let include_errno = "#include <errno.h>\n" in
let uproxy_list =
List.map2 (fun tf ecall_idx -> gen_func_uproxy tf ecall_idx ec)
ec.tfunc_decls
(Util.mk_seq 0 (List.length ec.tfunc_decls - 1))
in
let ubridge_list =
List.map (fun fd -> gen_func_ubridge ec.enclave_name fd)
(ec.ufunc_decls) in
let out_chan = open_out code_fname in
output_string out_chan (include_hd ^ include_errno ^ "\n");
ms_writer out_chan ec;
List.iter (fun s -> output_string out_chan (s ^ "\n")) ubridge_list;
output_string out_chan (gen_ocall_table ec);
List.iter (fun s -> output_string out_chan (s ^ "\n")) uproxy_list;
close_out out_chan
(* It generates trusted code to be saved in a `.c' file. *)
let gen_trusted_source (ec: enclave_content) =
let code_fname = get_tsource_name ec.file_shortnm in
let include_hd = "#include \"" ^ get_theader_short_name ec.file_shortnm ^ "\"\n\n\
#include \"sgx_trts.h\" /* for sgx_ocalloc, sgx_is_outside_enclave */\n\
#include \"sgx_lfence.h\" /* for sgx_lfence */\n\n\
#include <errno.h>\n\
#include <mbusafecrt.h> /* for memcpy_s etc */\n\
#include <stdlib.h> /* for malloc/free etc */\n\
\n\
#define CHECK_REF_POINTER(ptr, siz) do {\t\\\n\
\tif (!(ptr) || ! sgx_is_outside_enclave((ptr), (siz)))\t\\\n\
\t\treturn SGX_ERROR_INVALID_PARAMETER;\\\n\
} while (0)\n\
\n\
#define CHECK_UNIQUE_POINTER(ptr, siz) do {\t\\\n\
\tif ((ptr) && ! sgx_is_outside_enclave((ptr), (siz)))\t\\\n\
\t\treturn SGX_ERROR_INVALID_PARAMETER;\\\n\
} while (0)\n\
\n\
#define CHECK_ENCLAVE_POINTER(ptr, siz) do {\t\\\n\
\tif ((ptr) && ! sgx_is_within_enclave((ptr), (siz)))\t\\\n\
\t\treturn SGX_ERROR_INVALID_PARAMETER;\\\n\
} while (0)\n\
\n\
#define ADD_ASSIGN_OVERFLOW(a, b) (\t\\\n\
\t((a) += (b)) < (b)\t\\\n\
)\n\
\n"
in
let trusted_fds = tf_list_to_fd_list ec.tfunc_decls in
let tbridge_list =
let dummy_var = tbridge_gen_dummy_variable ec in
List.map (fun tfd -> gen_func_tbridge tfd dummy_var) trusted_fds in
let ecall_table = gen_ecall_table ec.tfunc_decls in
let entry_table = gen_entry_table ec in
let tproxy_list = List.map2
(fun fd idx -> gen_func_tproxy fd idx)
(ec.ufunc_decls)
(Util.mk_seq 0 (List.length ec.ufunc_decls - 1)) in
let out_chan = open_out code_fname in
output_string out_chan (include_hd ^ "\n");
ms_writer out_chan ec;
List.iter (fun s -> output_string out_chan (s ^ "\n")) tbridge_list;
output_string out_chan (ecall_table ^ "\n");
output_string out_chan (entry_table ^ "\n");
output_string out_chan "\n";
List.iter (fun s -> output_string out_chan (s ^ "\n")) tproxy_list;
close_out out_chan
(* We use a stack to keep record of imported files.
*
* A file will be pushed to the stack before we parsing it,
* and we will pop the stack after each `parse_import_file'.
*)
let already_read = SimpleStack.create ()
let save_file fullpath =
if SimpleStack.mem fullpath already_read
then failwithf "detected circled import for `%s'" fullpath
else SimpleStack.push fullpath already_read
(* The entry point of the Edger8r parser front-end.
* ------------------------------------------------
*)
let start_parsing (fname: string) : Ast.enclave =
let set_initial_pos lexbuf filename =
lexbuf.Lexing.lex_curr_p <- {
lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = fname;
}
in
try
let fullpath = Util.get_file_path fname in
let preprocessed =
save_file fullpath; Preprocessor.processor_macro(fullpath) in
let lexbuf =
match preprocessed with
| None ->
let chan = open_in fullpath in
Lexing.from_channel chan
| Some(preprocessed_string) -> Lexing.from_string preprocessed_string
in
try
set_initial_pos lexbuf fname;
let e : Ast.enclave = Parser.start_parsing Lexer.tokenize lexbuf in
let short_name = Util.get_short_name fname in
if short_name = ""
then (eprintf "error: %s: file short name is empty\n" fname; exit 1;)
else
let res = { e with Ast.ename = short_name } in
if Util.is_c_identifier short_name then res
else (eprintf "warning: %s: file short name `%s' is not a valid C identifier\n" fname short_name; res)
with exn ->
begin match exn with
| Parsing.Parse_error ->
let curr = lexbuf.Lexing.lex_curr_p in
let line = curr.Lexing.pos_lnum in
let cnum = curr.Lexing.pos_cnum - curr.Lexing.pos_bol in
let tok = Lexing.lexeme lexbuf in
failwithf "%s:%d:%d: unexpected token: %s\n" fname line cnum tok
| _ -> raise exn
end
with Sys_error s -> failwithf "%s\n" s
(* Check duplicated ECALL/OCALL names.
*
* This is a pretty simple implementation - to improve it, the
* location information of each token should be carried to AST.
*)
let check_duplication (ec: enclave_content) =
let dict = Hashtbl.create 10 in
let trusted_fds = tf_list_to_fd_list ec.tfunc_decls in
let untrusted_fds = uf_list_to_fd_list ec.ufunc_decls in
let check_and_add fname =
if Hashtbl.mem dict fname then
failwithf "Multiple definition of function \"%s\" detected." fname
else
Hashtbl.add dict fname true
in
List.iter (fun (fd: Ast.func_decl) ->
check_and_add fd.Ast.fname) (trusted_fds @ untrusted_fds)
(* For each untrusted functions, check that allowed ECALL does exist. *)
let check_allow_list (ec: enclave_content) =
let trusted_func_names = get_trusted_func_names ec in
let do_check_allow_list fname allowed_ecalls =
List.iter (fun trusted_func ->
if List.exists (fun x -> x = trusted_func) trusted_func_names
then ()
else
failwithf "\"%s\" declared to allow unknown function \"%s\"."
fname trusted_func) allowed_ecalls
in
List.iter (fun (uf: Ast.untrusted_func) ->
let fd = uf.Ast.uf_fdecl in
let allowed_ecalls = uf.Ast.uf_allow_list in
do_check_allow_list fd.Ast.fname allowed_ecalls) ec.ufunc_decls
(* Report private ECALL not used in any "allow(...)" expression. *)
let report_orphaned_priv_ecall (ec: enclave_content) =
let priv_ecall_names = get_priv_ecall_names ec.tfunc_decls in
let allowed_names = get_allowed_names ec.ufunc_decls in
let check_ecall n = if List.mem n allowed_names then ()
else eprintf "warning: private ECALL `%s' is not used by any OCALL\n" n
in
List.iter check_ecall priv_ecall_names
(* Check that there is at least one public ECALL function. *)
let check_priv_funcs (ec: enclave_content) =
let priv_bits = tf_list_to_priv_list ec.tfunc_decls in
if List.for_all (fun is_priv -> is_priv) priv_bits
then failwithf "the enclave `%s' contains no public root ECALL.\n" ec.file_shortnm
else report_orphaned_priv_ecall ec
(* When generating edge-routines, it need first to check whether there
* are `import' expressions inside EDL. If so, it will parse the given
* importing file to get an `enclave_content' record, recursively.
*
* `ec' is the toplevel `enclave_content' record.
* Here, a tree reduce algorithm is used. `ec' is the root-node, each
* `import' expression is considered as a children.
*)
let reduce_import (ec: enclave_content) =
(* Append a EDL list to another. Keep the first element and replace the
second one with empty element contains functions not in the first one
if both lists contain a same EDL. The function sequence is backwards compatible.*)
let join (ec1: enclave_content list) (ec2: enclave_content list) =
let join_one (acc: enclave_content list) (ec: enclave_content) =
if List.exists (fun (x: enclave_content) -> x.enclave_name = ec.enclave_name) acc
then
let match_ec = List.find (fun (x: enclave_content) -> x.enclave_name = ec.enclave_name) acc in
let filter_one func_decls decl= List.filter(fun x -> not (x = decl)) func_decls in
let filtered_ec =
{empty_ec with
tfunc_decls = List.fold_left(filter_one) ec.tfunc_decls match_ec.tfunc_decls;
ufunc_decls = List.fold_left(filter_one) ec.ufunc_decls match_ec.ufunc_decls; }
in
acc @ filtered_ec::[]
else
acc @ ec ::[]
in
List.fold_left(join_one) ec1 ec2
in
let parse_import_file fname =
parse_enclave_ast (start_parsing fname)
in
let check_funs funcs (ec: enclave_content list) =
(* Check whether `funcs' are listed in head of `ec'. It returns a
production (x, y), where:
x - functions not listed in head `ec';
y - a new `ec' that its head contains functions from `funcs' listed in `ec'.
*)
let enclave_funcs =
let trusted_func_names = get_trusted_func_names (List.hd ec) in
let untrusted_func_names = get_untrusted_func_names (List.hd ec) in
trusted_func_names @ untrusted_func_names
in
let in_ec_def name = List.exists (fun x -> x = name) enclave_funcs in
let in_import_list name = List.exists (fun x -> x = name) funcs in
let x = List.filter (fun name -> not (in_ec_def name)) funcs in
let y =
{ (List.hd ec) with
tfunc_decls = List.filter (fun tf ->
in_import_list (get_tf_fname tf)) (List.hd ec).tfunc_decls;
ufunc_decls = List.filter (fun uf ->
in_import_list (get_uf_fname uf)) (List.hd ec).ufunc_decls; }
in (x, y::(List.tl ec))
in
(* Import functions listed in `funcs' from `importee'. *)
let rec import_funcs (funcs: string list) (importee: enclave_content list) =
(* A `*' means importing all the functions. *)
if List.exists (fun x -> x = "*") funcs
then
let finished_ec = List.fold_left (fun acc (ipd: Ast.import_decl) ->
let next_ec = parse_import_file ipd.Ast.mname
in join acc (import_funcs ipd.Ast.flist (next_ec::[]))) importee (List.hd importee).import_exprs
in
(SimpleStack.pop already_read |> ignore; finished_ec)
else
let (x, y) = check_funs funcs importee
in
match (List.hd importee).import_exprs with
[] ->
if x = []
then (SimpleStack.pop already_read |> ignore;y) (* Resolved all importings *)
else failwithf "import failed - functions `%s' not found" (List.hd x)
| ex ->
(* Continue importing even if all function importings resolved to avoid circled import.*)
let finished_ec = List.fold_left (fun acc (ipd: Ast.import_decl) ->
let next_ec = parse_import_file ipd.Ast.mname
in join acc (import_funcs x (next_ec::[]))) y ex
in
(SimpleStack.pop already_read |> ignore; finished_ec)
in
let imported_ec_list = import_funcs ["*"] (ec::[])
in
(* combine two EDLs by appending items except import. *)
let combine (acc: enclave_content) (ec2: enclave_content) =
{ acc with
include_list = acc.include_list @ ec2.include_list;
import_exprs = [];
comp_defs = acc.comp_defs @ ec2.comp_defs;
tfunc_decls = acc.tfunc_decls @ ec2.tfunc_decls;
ufunc_decls = acc.ufunc_decls @ ec2.ufunc_decls; }
in
List.fold_left (combine) (List.hd imported_ec_list) (List.tl imported_ec_list)
(* Generate the Enclave code. *)
let gen_enclave_code (e: Ast.enclave) (ep: edger8r_params) =
let ec = reduce_import (parse_enclave_ast e) in
g_use_prefix := ep.use_prefix;
g_untrusted_dir := ep.untrusted_dir;
g_trusted_dir := ep.trusted_dir;
create_dir ep.untrusted_dir;
create_dir ep.trusted_dir;
check_duplication ec;
check_structure ec;
check_allow_list ec;
(if not ep.header_only then check_priv_funcs ec);
if Plugin.available() then
Plugin.gen_edge_routines ec ep
else (
(if ep.gen_untrusted then (gen_untrusted_header ec; if not ep.header_only then gen_untrusted_source ec));
(if ep.gen_trusted then (gen_trusted_header ec; if not ep.header_only then gen_trusted_source ec))
)