# SecV: Secure code partitioning via multi-language secure values

Peterson Yuhala, Pascal Felber, Hugo Guiroux, Jean-Pierre Lozi, Alain Tchana, Valerio Schiavoni, Gaël Thomas

## ► To cite this version:

Peterson Yuhala, Pascal Felber, Hugo Guiroux, Jean-Pierre Lozi, Alain Tchana, et al., SecV: Secure code partitioning via multi-language secure values. Middleware '23: 24th International Middleware Conference, Dec 2023, Bologna, Italy. pp.207-219, 10.1145/3590140.3629116. hal-04355247

HAL Id: hal-04355247

https://inria.hal.science/hal-04355247v1

Submitted on 20 Dec 2023

HAL is a multi-disciplinary open access archive for the deposit and dissemination of scientific research documents, whether they are published or not. The documents may come from teaching and research institutions in France or abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des établissements d'enseignement et de recherche français ou étrangers, des laboratoires publics ou privés.

<figure><img src="image_1.png" /></figure>

Distributed under a Creative Commons CC BY 4.0 - Attribution - International License
# SecV: Secure Code Partitioning via Multi-Language Secure Values

Peterson Yuhala

University of Neuchâtel
Neuchâtel, Switzerland
peterson.yuhala@unine.ch

Pascal Felber

University of Neuchâtel
Neuchâtel, Switzerland
pascal.felber@unine.ch

Hugo Guiroux

Oracle Labs
Zürich, Switzerland
hugo.guiroux@oracle.com

Jean-Pierre Lozi

Inria
Paris, France
jean-pierre.lozi@inria.fr

Alain Tchana

Grenoble INP
Grenoble, France
alain.tchana@grenoble-inp.fr

Valerio Schiavoni

University of Neuchâtel
Neuchâtel, Switzerland
valerio.schiavoni@unine.ch

Gaël Thomas

Télécom SudParis
Institut Polytechnique de Paris
France
gael.thomas@telecom-sudparis.eu

## Abstract

Trusted execution environments like Intel SGX provide enclaves, which offer strong security guarantees for applications. Running entire applications inside enclaves is possible, but this approach leads to a large trusted computing base (TCB). As such, various tools have been developed to partition programs written in languages such as C or Java into trusted and untrusted parts, which are run in and out of enclaves respectively. However, those tools depend on language-specific taint-analysis and partitioning techniques. They cannot be reused for other languages and there is thus a need for tools that transcend this language barrier.

We address this challenge by proposing a multi-language technique to specify sensitive code or data, as well as a multi-language tool to analyse and partition the resulting programs for trusted execution environments like Intel SGX. We leverage GraalVM's TRUFFLE framework, which provides a language-agnostic abstract syntax tree (AST) representation for programs, to provide special AST nodes called *secure nodes* that encapsulate sensitive program information. Secure nodes can easily be embedded into the ASTs of a wide range of languages via TRUFFLE's *polyglot API*. Our technique includes a multi-language dynamic taint tracking tool to analyse and partition applications based on our generic secure nodes. Our extensive evaluation with micro- and macro-benchmarks shows that we can use our technique for two languages (Javascript and Python), and that partitioned programs can obtain up to 14.5% performance improvement as compared to unpartitioned versions.

**CCS Concepts:** • Security and privacy → Trusted computing; • Software and its engineering → Object oriented frameworks.

**Keywords:** Trusted Execution Environments, Intel SGX, Managed Execution Environments, Java, Truffle, GraalVM

## 1 Introduction

Trusted execution environments (TEE) use hardware cryptography to enforce confidentiality, authenticity and integrity of a memory zone called an *enclave*. TEEs are at the basis of confidential computing, and major CPU vendors have introduced them in their processors, e.g., Intel's SGX [13], AMD's SME [19], and ARM's TrustZone [3, 48], to provide security guarantees for cloud-based applications.

Minimising the trusted computing base (TCB) of a TEE-enabled program is crucial for improving security. Achieving this goal is especially difficult for managed languages because they come with a runtime that contains large system libraries (e.g., Java, Python, R, etc.). As such, leveraging tools such as SCONE [4], SGX-LKL [49], or Graphene-SGX [56] that fully embed the language runtime inside the TEE is not satisfactory. For example, the Java library shipped with OpenJDK18 contains 47,146 Java classes for a total of 5,224,426 lines of Java code. Adding such a large code base inside a TEE increases the size and the attack surface of the TCB to an unacceptable degree.

To minimise the TCB size of applications written in managed languages, the code and the data of the application and the runtime must be partitioned into *trusted* parts and *untrusted* parts, which run inside and outside of the enclave, respectively. Since manually partitioning a large code base is complex and error-prone, several middleware technologies have been developed which permit developers to automatically partition their code prior to deployment in the cloud.
Unfortunately, partitioning tools only exist for a few lan-
guages: Java with Civet [57], Montsalvat [66] and Uranus
[18], Go with GOTEE [14], and C with Glamdring [23]. These
tools depend heavily on the language semantics and cannot
be reused for other languages. To support the next program-
ming language (e.g., Python, R, JavaScript, Ruby, etc.), one
must entirely re-implement a new automatic partitioning
tool, which is time consuming and error-prone.

In this paper, we propose *to decouple the partitioning tool from the language semantics, and to implement the former once and for all languages*. To that end, we leverage TRUFFLE [61], a Java library for building high-performance language interpreters. It provides a generic abstract syntax tree (AST) composed of nodes that represent various syntactic elements of a program: *i.e.*, expressions (*e.g.*, function calls or arithmetic operations), program values (*e.g.*, literals or variables), control flow (*e.g.*, *if-else* or *for loops*), *etc*. TRUFFLE provides support for popular programming languages like Python, JavaScript, R, Ruby, C/C++, amongst many others. To implement the partitioning tool once and for all, we first introduce a new multi-language AST node type, called a *secure node*. This node contains a *secure value* corresponding to a sensitive value that has to be secured in the enclave. Then, we leverage the *polyglot interoperability protocol* provided by TRUFFLE [15], which allows the declaration of a secure value from any language with an expression as simple as `x=polyglot.eval("secV", "sInt(42)")`. Finally, we develop a generic *dynamic taint tracking* tool, POLYTAINT, which instruments TRUFFLE ASTs to track the data flow of sensitive values from secure nodes so as to determine the portions of a program (*e.g.*, functions) to be shielded inside the enclave. POLYTAINT then partitions the program into two parts, trusted and untrusted, to be executed respectively inside or outside the enclave.

In summary, we propose the following contributions:

1. Generic AST secure nodes to specify sensitive data in any TRUFFLE language.

2. POLYTAINT, a TRUFFLE instrumentation tool which performs dynamic taint tracking on generic polyglot programs and partitions the programs based on the use of secure values.

3. An extensive experimental evaluation demonstrating the effectiveness of our approach via micro-benchmarks in JavaScript and Python, as well as real-world applications: *PageRank* [2] and *linear regression* [64]. Our analysis of partitioned programs shows we can reduce the size of the TCB (*i.e.*, improved security) and improve performance (up to 14.5%) at the same time.

The rest of the paper is organised as follows. §2 provides background concepts. We present our threat model in §3. §4 describes the architecture and workflow of SECV, followed in §5 by an extensive experimental evaluation. We discuss the limitations of SECV in §6, and provide ideas for further

use cases in §7. Related work is discussed in §8, while we
conclude and hint at future work in §9.

2 Background

2.1 Intel software guard extensions (SGX)

Intel SGX [13, 63] is a set of hardware instructions to cre-
ate secure memory regions called enclaves. Enclaves pro-
vide strong confidentiality and integrity guarantees for code
and data processed on a malicious node whose privileged
software (e.g., kernel, hypervisor, etc.) is potentially com-
promised. Enclave data is stored in an encrypted portion of
DRAM, the enclave page cache (EPC). EPC pages are transpar-
ently decrypted by a memory encryption engine (MEE) only
when loaded into a CPU cache line. Enclaves typically have
limited memory resources: 128 MB or 256 MB per socket in
the more popular first generation SGX-enabled CPUs. The
SGX Linux kernel driver supports paging from EPC memory
to regular DRAM to accommodate enclaves larger than the
EPC, but this comes at a performance cost [59].

Software that leverages Intel SGX is usually split in two
parts: a *trusted* part which executes in enclave mode, and
an *untrusted* part which executes in non-enclave mode. The
Intel SGX SDK permits interaction between both parts via
ecalls (from untrusted to trusted code), and ocalls (from
trusted to untrusted). Intel SGX enclaves operate only in
user mode and OS services (*i.e.*, system calls) cannot be ex-
ecuted directly within the enclaves [4, 56]. They must in-
stead be relayed to the untrusted part via ocalls. Both ecalls
and ocalls trigger expensive context switches in the CPU
[59] (accounting for up to 13,500 CPU cycles). Reducing the
number of ecalls and ocalls is therefore key in designing
efficient enclave software.

To facilitate the deployment of code in enclaves, several
tools [4, 49, 53, 56] make it possible to run unmodified ap-
plications inside SGX enclaves. While this approach is rel-
atively straightforward to use for developers, it increases
the chance of introducing security vulnerabilities into the
enclave (due to the large TCB). Some language-specific al-
ternatives [18, 23, 57, 66] have been proposed recently to
partition programs and keep only sensitive code and data
within enclaves.

2.2 GraalVM

GraalVM is a high-performance JDK distribution that can
execute programs implemented in a wide range of high-level
languages, e.g., JavaScript, Python, Java, Kotlin, etc. [6, 66]. At
the heart of GraalVM is the *Graal* compiler [47], a dynamic
just-in-time (JIT) compiler that produces highly optimised
machine code.

**GraalVM native images.** GraalVM provides a tool that can compile ahead-of-time (AOT) programs implemented in JVM-based languages (e.g., Java, Scala, etc.) to native executables, called *native images*. This tool leverages static analysis [60]
<figure><img src="image_2.png" /><figcaption>**Figure 1.** Integer addition node executed in the AST to produce the sum of the values of left and right child.</figcaption></figure>

to determine which program elements (classes, methods, fields) are reachable at run time. These reachable elements, together with runtime components (i.e., the garbage collector, support for thread scheduling and synchronisation, etc.) are AOT-compiled into the final native image. This significantly lowers the memory footprint when compared to programs interpreted in a regular JVM, making native images very suitable for TEEs. In addition, GraalVM native images can run initialisation code at build time instead of at run time [60], which leads to faster start-up times in native images compared to other runtime environments.

**TRUFFLE.** It is a framework provided by the GraalVM ecosystem to build tools and programming language implementations as self-modifying Abstract Syntax Tree (AST) interpreters [15, 43]. At low level, TRUFFLE provides a base Node class that is leveraged by language implementers to build other AST nodes representing the semantic constructs of their programming language, e.g., an addition operation, a variable write, etc. Essentially, every node in a TRUFFLE AST is executed (via an execute method) at runtime to produce a result.

In Fig. 1 for example, the integer addition node will be executed at runtime to produce the sum of the left and child node values, which are computed by calling the *execute* methods of the left and right nodes respectively (depth-first traversal).

The key advantage of TRUFFLE is that all programs implemented in a supported language are parsed to a common AST representation (i.e., the TRUFFLE AST), which is then manipulated in a language-agnostic fashion. TRUFFLE languages include JavaScript (JS) [32], Ruby [46], R [30], Python [31], LLVM-based languages [42], and more.

**Polyglot API.** TRUFFLE allows developers to build *polyglot* applications that combine code written in different languages. This interoperability is provided by the *polyglot interoperability protocol*, a set of standardised messages (*polyglot API*) implemented in every TRUFFLE language [15, 39]. This API allows to transfer objects from one language scope to another as TRUFFLE values, i.e., an instance of Value class.

1. `import org.graalvm.polyglot.*;`
2. `class PolyglotTest {`
3.   `public static void main(String[] args) {`
4.     `Context polyglot = Context.create();`
5.     `Value array = polyglot.eval("js", "[1, 2, 3, 44]");`
6.     `int result = array.getArrayElement(3).asInt();`
7.     `System.out.println(result); // prints 44`
8.   }`

<figure><img src="image_3.png" /><figcaption>**Figure 2.** Instrumenting a variable write with a wrapper node. The event node's `onReturnValue` method could be used to register the name of the variable being written, as well as the corresponding value.</figcaption></figure>

9}

**Listing 1.** Java application accessing an object from a JS language scope via the polyglot API.

Listing 1 shows the transfer of an array object created in a JS language scope into the Java language scope via the polyglot API (line 6). The first parameter to the `polyglot.eval` call is the truffle language id, a unique string identifier for TRUFFLE guest languages, while the second parameter is the guest source code to execute. Such cross-language object exchange is possible in all TRUFFLE languages, which simplifies the implementation of multi-language applications.

In this paper, the language accessing the objects generated by another interpreter will be referred to as the *host language* (Java in Listing 1), and the language used to generate the objects accessed by the host language is called the *guest language* (JS in Listing 1). Objects of the host language will be referred to as *regular objects* while those of the guest language will be referred to as *foreign objects*.

**TRUFFLE instrumentation agents.** The TRUFFLE framework provides an API to dynamically intercept the execution of nodes in the AST [15, 58, 62]. This API was used to implement a program profiler [27], a debugger [33], and a taint-tracking tool [21]. In POLYTAINT, we leverage this API to implement our partitioning tool.

To intercept the execution of nodes, the developer has to implement an *instrumentation agent* [58]. The agent intercepts the execution of nodes by leveraging *syntactic tags* associated to the AST nodes. These tags give the semantics of the nodes (e.g., *call tag*, *variable write tag etc.*).

Fig. 2 illustrates how an instrumentation agent works. When the agent is loaded, it associates an *event node* to a tag [29]. In our example, the agent attaches the event node *E* to any AST node with the *variable write tag*. At execution, when TRUFFLE visits an AST node with the instrumented tag for the first time, it replaces the node by a wrapper node [44] connected to the event node. In our example, when TRUFFLE visits the node *N* for the first time, it wraps *N* in *W* and connects *W* with *E*.

A wrapper node implements a special execute function. Upon a call, it first calls `onEnter` on the associated event node, then execute on the wrapped node, and finally `onReturnValue` on the associated event node. These functions
<figure><img src="image_4.png" /><figcaption>Figure 3. Overview of SECV’s workflow.</figcaption></figure>

can collect metrics, modify the wrapped node, and even
replace the wrapped node by another node.

3 Threat model

We consider a powerful adversary with full control over the
software stack, including privileged software (i.e., host OS,
hypervisor) with access to the physical hardware (i.e., DRAM,
secondary storage, etc.). The adversary's goal is to disclose
sensitive data or damage its integrity.

The SECV workflow assumes enclave program development, taint tracking and program partitioning, as well as final enclave code building and signing are all done in a trusted environment, to prevent malicious code tampering disclosing sensitive information at runtime. The integrity of the trusted partition can be ensured via remote attestation [9, 13].

The adversary cannot open the CPU package to extract
decrypted enclave secrets. The final enclave code does not in-
tentionally leak sensitive information (e.g., encryption keys).
We do not consider denial-of-service (DoS) and side-channel
attacks [7, 52], for which mitigations exist [16, 26].

4 Design and workflow of SECV

SECV is a framework that analyses applications and parti-
tions them into *trusted* and *untrusted* parts for architectures
with TEE support. It supports applications written in *any*
Truffle language, and introduces *secure values* to specify sen-
sitive data. Fig. 3 presents an overview of SECV’s workflow,
which comprises 4 phases: (1) identification of sensitive vari-
ables that contain *secure values* (see §4.1), (2) dynamic taint
tracking with POLYTAINT (see §4.2 and §4.3), (3) program
partitioning with the *partitioner* (see §4.4), and (4) native im-
age building with the *image generator* and final application
creation (see §4.5).

We illustrate our system’s workflow by considering a sim-
ple linear regression program written in Python (see List-
ing 2). We consider a security scenario where we wish to keep
the learned model (i.e., m and c) confidential [17, 25, 50, 65].
In the following, we will show how one can use SECV to
enforce this scenario.

4.1 Identifying and specifying sensitive data

We propose a multi-language approach to specify sensitive
data via the use of newly-introduced *secure nodes* in a TRUF-
FLE AST. All values associated with these secure nodes are

1 import polyglot
2 m = 0.0
3 c = 0.0
4 L = 0.0001
5 N = 10000
6 numIter = 100
7 def readXData(n):
8     # logic (omitted)
9     return xData
10 def readYData(n):
11     # logic (omitted)
12     return yData
13 def arraySum(A):
14     sumA = 0
15     for a in A:
16         sumA += a
17     return sumA
18 def trainModel(numIterations):
19     X = readXData(N)
20     Y = readYData(N)
21 for i in range(numIterations):
22     Y_pred = m * X + c
23     D_m = (-2/N) * arraySum(X * (Y-Y_pred))
24     D_c = (-2/N) * arraySum(Y-Y_pred)
25     m = m - L * D_m
26     c = c - L * D_c
27 trainModel(numIter)
28

<figure><img src="image_5.png" /><figcaption>Figure 4. The TRUFFLE framework can be leveraged to provide an AST generator that produces secure nodes which can be injected in any TRUFFLE AST via the polyglot API.</figcaption></figure>

referred to as *secure values* and they will represent sensitive
information which must be kept inside the enclave. To make
this possible, we leverage the TRUFFLE framework to build
a *secure node generator* which injects secure nodes into any
TRUFFLE AST via the polyglot API. This secure node gen-
erator is implemented like a regular TRUFFLE language but
provides primarily special AST nodes which comprise se-
cure information. Our preliminary prototype provides secure
node types: sInt, sDouble, sBoolean, and sArray to contain
secure int values, secure double values, secure boolean val-
ues, and secure array object values respectively. For example,
Listing 3 shows how a JavaScript program can inject a secure
integer value into its AST.

1 var myInt = 2;
2 var secInt = Polyglot.eval("secV", "sInt(4)");
3 myInt = secInt + 2;
4 console.log(myInt); // prints 6

<caption>**Listing 3.** Injecting a secure integer node with value 4 into a JavaScript program via the polyglot API.</caption>
for example, `arraySum` is considered a neutral function by virtue of the tainted inputs `Y-Y_pred` and `X*(Y-Y_pred)`. Moreover, untrusted code could also perform array sums without sensitive inputs.

Neutral functions are included fully in both the trusted and untrusted partitions; there are no proxy functions involved. The rationale behind this design choice is twofold: the approach allows for good security and performance. That is, enclave code can access the functionality of the neutral function without leaking sensitive information, via input parameters, to the outside (security), and the code out of the enclave does not need to perform an expensive `ecall` transition to access the functionality of the neutral function (performance). Utility functions such as those that calculate generic sums, products, etc. are examples of neutral functions.

Untrusted functions. These are functions which do not have any access to secure variables during the lifetime of the program. Untrusted functions are included fully only in the untrusted partition. However, they have *proxies* in the trusted side which perform `ocall` transitions to access the functionality in the untrusted side of the partitioned application. The rationale for this design choice is *TCB size reduction*, and thus *security*, i.e., code which is not sensitive need not be in the enclave following the principle of least privilege [51].

## 4.3 AST instrumentation

To identify the trusted (T), neutral (N) and untrusted (U) functions, `POLYTAINT` uses a dynamic analysis technique. It executes the program once during the development phase. During this execution, it identifies the secure values, and then deduces the state of the functions, i.e., trusted, untrusted, and neutral. During the in-vitro execution, `POLYTAINT` performs taint propagation by leveraging the TRUFFLE instrumentation framework, which makes it possible to intercept the execution of AST nodes. To instrument the TRUFFLE AST, `POLYTAINT` intercepts the executions of these node types: variable read/write, object field read/write, array element read/write and function call nodes (see §2.2). `POLYTAINT` provides an event node class for each of the node types to be wrapped, as well as a base event node class `PolyTaintNode` which all the event node classes inherit from. `PolyTaintNode` implements common functionality which is used by the event node classes.

`POLYTAINT` maintains a map data structure called the *taint map* which tracks program symbols (e.g., variables, functions, etc.) that access secure values. A unique *string identifier* is associated to each program symbol such as *e.g.*, a variable or function. The *taint map* associates *string identifiers* to *taint labels*. We have 3 *taint labels*: 1 for tainted/trusted nodes, 2 for neutral nodes, and 0 for untrusted nodes (i.e., representing untrusted functions).

<caption>**Algorithm 1** — Pseudo-code to check for tainted nodes in AST</caption>

1: **procedure** TRAVERSEAST(*node*)
2:     **if** IS TAINTED(*node*) **then**
3:         **return** true
4:     **end if**
5:     **for** *Node* *child* **in** *node*.GETCHILDREN() **do**
6:         **if** TRAVERSEAST(*child*) **then**
7:             **return** true
8:         **end if**
9:     **end for**
10:    **return** false
11:  **end procedure**
12: **procedure** IS TAINTED(*node*)
13:     **String** *id* ← GETIDENTIFIER(*node*)
14:     **if** taintMap. CONTAINS(*id*) **then**
15:         **return** taintMap. GET(*id*) = 1
16:     **else**
17:         **boolean** polygotEvalTest ← *node* **is** polyglot.EVAL call
18:         **boolean** secVLiteralTest ← *node* **has** "secV" literal node
19:         **as input**
20:     **return** polyglotEvalTest ∧ secVLiteralTest
21: **end procedure**

During instrumentation, `POLYTAINT` leverages the `onReturnValue` callback of event nodes to test for tainted program symbols in a node's AST. This is done by recursively traversing the node's child nodes and checking if the child nodes are associated with tainted symbols. As illustrated in Algorithm 1, `POLYTAINT` considers that a child node is tainted if (i) the child node corresponds to a call to the polyglot API with the `SecV` language identifier (i.e., `"secV"`) or (ii) the child node is a value that was tainted before. In the remainder of this section, we outline how `POLYTAINT` performs taint tracking for the different intercepted nodes.

**Variable write:** This is a statement that assigns a value to a program variable. For example the JS statement:

var x = y + Polyglot.eval("secV", "sInt(4)") inside a function's scope is considered a local variable assignment. Similarly, in our illustrative example, statements like
m = polyglot.eval(language="secV", string="sDouble(0.0)")
c = polyglot.eval(language="secV", string="sDouble(0.0)")
and

`Y_pred=m*X+c` are variable assignments.

The TRUFFLE instrumentation API provides a generic standard tag operation `StandardTags.writeVariableTag` [41] to identify nodes corresponding to variable write statements in all TRUFFLE languages. At run time, `POLYTAINT` wraps every variable write node and creates a corresponding *variable write event node*. The TRUFFLE API makes it possible to obtain a node object descriptor [37] which provides the unique name of the program variable being written to, as well as the corresponding AST node. In the `onReturnValue`
callback of the *variable write event node*, POLYTAINT leverages node object descriptors to obtain the exact names of the target program variables being written to. For example, for the 3 aforementioned variable write statements, the variable names are respectively *m*, *c* and *Y_pred*.

POLYTAINT uses the variable names to construct the unique String identifiers for these variables. Still in the `onReturnValue` callback, POLYTAINT checks the *taint map* for the presence of *m*, *c* and *Y_pred*. If they are present, the `onReturnValue` callback returns. Otherwise, POLYTAINT leverages the `TRUFFLE` API to obtain the *right-hand side (rhs)* expression node associated with the variable write statements. Algorithm 1 traverses the child nodes of the *rhs* expressions to check for the presence of any tainted nodes.

As explained previously, the presence of child nodes corresponding to the `polyglot.eval` call as well as the `"secV"` string literal as one of its input arguments tests positive for a *taint source*. This will mark variables *m* and *c* as tainted in the *taint map*, i.e., taint labels of 1 for their identifiers. Similarly, *Y_pred* will be marked as tainted due to the presence of tainted variables *m* and *c* in the *rhs* expression involved in the assignment of *Y_pred*. If a tainted variable is written in a function, this function is also tagged as tainted in the *taint map*. For example, the variable write node corresponding to the statement `Y_pred=m*X+c` is tainted as a result of the presence of tainted variables *m* and *c* in the statement, therefore the enclosing function `trainModel` is marked as tainted in the *taint map*. POLYTAINT calls ` name` on the root node object [45] to obtain the unique name of the enclosing function/method for any instrumented node.

**Variable read:** This is a statement that reads the value of a program variable. Variable read operations could occur in an if/else statement, a for loop, a function call via parameters, etc. The TRUFFLE instrumentation API provides a generic standard tag

`StandardTags.ReadVariableTag` [41] to identify nodes that correspond to variable read statements in all TRUFFLE languages.

At run time, POLYTAINT wraps every variable read node and creates a corresponding *variable read event node*. The event node's `onReturnValue` callback is used to obtain the unique name of the variable being read from the node object descriptor, and the *taint map* is checked for the presence of the variable's identifier. If a tainted variable is read in a function (e.g., `while(i<tainted_variable)`), this function is also tagged as tainted in the *taint map*. If a tainted function argument is read (e.g., reading the value of `Y_pred` in `arraySum(Y-Y_pred)` on line 24), the function node is tracked as a neutral function in the *taint map* (i.e., taint label = 2) if it has not been tagged as tainted. In other words, a taint label of 1 for tainted/secure function is preferred for the function node over a taint value of 2 for neutral. As such, `arraySum` is tagged as a neutral function in the *taint map*.

**Object field write:** This operation assigns a value to an object's field or property. At the time of this writing, the `TRUFFLE` API does not provide standard tags to identify generic object field write nodes but `TRUFFLE` languages typically provide language-specific tags to identify such nodes. For example, `TRUFFLE-JS` provides the `WritePropertyTag` [36] to identify object property writes. In some languages like JS, the global scope is an object and global variables are properties of this object. As such, global variable writes in JS are instrumented via the `WritePropertyTag`. Object field writes are instrumented with *object field write event nodes* in a similar fashion to variable writes.

**Object field read:** This operation reads an object's field or property. Similarly to object field writes, since there are no generic standard tags yet in the `TRUFFLE` API to identify object field read nodes, `TRUFFLE` languages typically provide language-specific tags like `ReadPropertyTag` [36] in `TRUFFLE-JS` to identify an object field read node. Object field reads are instrumented with *object field read event nodes* in a similar fashion to variable reads.

**Array element write (resp. read):** This is an operation that writes (resp. reads) a value to an array object, e.g., `array[0]=4otecp.f(a[3]<0)`. Array element writes (resp. reads) are instrumented in a similar fashion to object field writes (resp. reads), with *array element write* (resp. *read event nodes*).

**Function call:** This is an expression that passes control (and possible arguments) to a function or method in a program. The `TRUFFLE` instrumentation API provides a generic standard tag

`StandardTags.CallTag` [41] to identify language nodes that correspond to guest language functions and methods in all `TRUFFLE` languages. At run time, `POLYTAINT` wraps every function call node and creates a corresponding *call event node*. The call event node's `onReturnValue` callback is used to test first for tainted arguments.

`TRUFFLE` languages usually provide methods to obtain argument nodes during instrumentation, e.g., `getArgumentNodes` provided by `TRUFFLE-JS` (`JSFunctionCallNode` [35]) and `TRUFFLE-Python` (`PythonCallNode` [40]). If an argument node for a function corresponds to a tainted symbol, e.g., a tainted variable, this function is tagged as neutral in the *taint map* if it has not been tagged as tainted. For example, `arraySum` is tagged as neutral by virtue of the presence of the tainted variable `Y_pred` in the argument expression.

`POLYTAINT` maintains a list (*seen list*) that comprises all functions/methods that have been visited during execution at run time. Every function/method in the list is identified as a `PolyTaintFunction` object, which is an instance of `PolyTaintFunction` class. This class defines attributes representing the actual function node (i.e., Node object), a list representing the different argument types passed to the function
(e.g., `int` for `trainModel`, `Object` for `arraySum`, etc.), a `String` representing the return type (e.g., `double` for `arraySum`, void for `trainModel`, etc.) of the function, and an integer value representing the function's taint label. The `onReturnValue` callback of every event node contains a `VirtualFrame` [34] parameter comprising the actual argument values passed to the instrumented node, e.g., a function call node, and an `Object` parameter representing the actual result received after the node is executed. `POLYTAINT` leverages these two parameters to obtain the corresponding argument types passed (if they exist) to every function call node and return types, respectively. The input and return types are used later on during program partitioning (§4.4).

At the end of program instrumentation with `POLYTAINT`, all application functions which have been executed at run time (in the seen list), but have not been tagged as trusted/tainted or neutral in the taint map are considered untrusted functions. As such, for our illustrative example, after instrumentation, we should have set `T`: `trainModel`, set `N`: `arraySum`, and set `U`: `readXData` and `readYData`. This information is then passed to the program partitioner which builds two programs representing the trusted and untrusted partitions of the instrumented program.

## 4.4 Program partitioning

The aim of the partitioning stage is to separate the original program into two parts: a trusted part which executes inside the enclave and comprises functions `T` ∪ `N`, and an untrusted part which executes outside the enclave and comprises `U` ∪ `N`. By removing the `U` functions from the enclave, we reduce the size of the TCB. Furthermore, eliminating `U` functions also eliminates any functions of the language runtime's system libraries invoked by `U`, which are often very large (as stated in the introduction).

In order to interpret the ASTs of the `N` and `T` functions inside the enclave at runtime, we have to also embed the corresponding `TRUFFLE` language interpreter in the enclave. However, `TRUFFLE` is written in Java, which means that we need a Java runtime inside the enclave to execute the `TRUFFLE` interpreter. Since embedding a full JVM with its system library inside the enclave would increase the size and the attack surface of the TCB to an unacceptable degree, we choose to base the design of our partitioning tool on native images (§2.2). `GRAALVM`'s Native Image technology makes it possible to include only reachable program elements (i.e., methods, classes, and objects) into the resulting native image, making it suitable for restricted environments like Intel SGX enclaves. Any unused classes in the `TRUFFLE` interpreters or `GRAALVM`'s runtime components (e.g., garbage collector) are pruned out of the native images. The remainder of this section describes our partitioning approach.

### 4.4.1 Generated functions.
`GRAALVM` AoT only supports compiling Java applications to create native images (i.e., no

JS, Python, etc.), therefore, we need to find a way to run the partitioned guest application code (e.g., JS, Python, etc.) via native images. To achieve this, the partitioner leverages `TRUFFLE`'s polyglot API to embed the guest code inside two Java programs: `Trusted.java` for the trusted partition and `Untrusted.java` for the untrusted partition. This is done by creating static Java methods that correspond to each of the methods seen during taint analysis (i.e., `T`, `N`, and `U`). These static methods simply execute the corresponding ASTs of the guest functions they represent.

For instance, Listing 5 illustrates how the AST of a JS function `multi` can be executed from within a Java application, and how a Java method `hello` can be executed from within a JS program.

```java
1 import org.graalvm.polyglot.*;
2 public class Example {
3     public static void main(String[] args) {
4         Context ctx = Context newBuilder().allowAllAccess(true).build();
5         Value jsMulti = ctx.eval("js", "(function multi(a, b){return a*b;})");
6         Value res = jsMulti.execute(6, 7);
7         System.out.println(res); // prints 42 in Java scope
8         Value jaHello =
9             ctx.asValue(Polyglot.class).getMember("static").getMember("hello");
10        String jsStringFunc = "function jsHello(jaHello){jaHello();}jsHello;"
11        Value jsFunction = ctx.eval("js", jsStringFunc);
12        jsFunction.execute(jaHello); // prints "Hello Java" in js scope
13    }
14 public static void hello() {
15     System.out.println("Hello Java");
16 }
17 }
```

<caption>**Listing 5.** Java host and guest JS interaction via polyglot API.</caption>

Using the same idea outlined in Listing 5, `Trusted.java` therefore comprises static Java methods for `trainModel`, `arraySum`, `readXData` and `readYData`. The static Java methods for `trainModel` and `arraySum` execute the corresponding AST code, i.e., the actual guest source code for those functions, while the static Java methods for `readXData` and `readYData` (proxy methods) perform `ocall` transitions to execute the real methods in the untrusted partition (see below). The actual source code of a function is obtained via the `getSourceSection` method [45] of the `TRUFFLE` Node class. This is illustrated by Listing 6.

```java
1 public class Trusted {
2     Context ctx = Context newBuilder().allowAllAccess(true).build();
3     public static void trainModel(int iter){
4         Value arraySumVal = ctx.asValue(Trusted.class);
5         getMember("static").getMember("arraySum");
6         ctx.eval("python", "//trainModel code");
7         execute(iter, arraySumVal, ...);
8     }
9     public static double arraySum(Object array){
10         double ret = ctx.eval("python", "// arraySum code here");
11         execute(array).asDouble();
12         return ret;
13     }
14     public static Object readXData(int n){
15         ocall_readXData(n);
16     }
17 }
18
```

<caption>**Listing 6.** `Trusted.java` after partitioning</caption>
Similarly, `Untrusted.java` comprises static Java methods for `trainModel`, `arraySum`, `readXData` and `readYData`. The static Java methods for `readXData`, `readYData` and `arraySum` execute the corresponding AST code for those functions while the static Java method for `trainModel` performs an `ecall` transition to execute the method in the trusted partition. Listing 7 exemplifies this.

```java
1 public class Untrusted {
2     Context ctx = Context newBuilder().allowAllAccess(true).build();
3     public static Object readXData(int n) {
4         return ctx.eval("python", "// readXData code here").execute(n);
5     }
6     public static void trainModel(int n) {
7         ecall_trainModel(n);
8     }
9     ...
10 }
```

<caption>**Listing 7.** Untrusted.java after partitioning</caption>

### 4.4.2 Transition between the partitions.
With our program partitioned, we must allow communication between the two partitions. That is, how does a Java method in `Trusted.java` (i.e., the trusted partition) invoke another Java method in `Untrusted.java` (i.e., the untrusted partition)?

To achieve this, we leverage GRAALVM C entry points [28, 66]. These are special Java methods in a native image program which are callable from a regular C/C++ program or another native image. As such, C entry point methods can act as relays between the trusted and untrusted partitions. That is, a proxy function in partition-x can invoke the corresponding real static method (m) in the opposite partition-y if there is a C entry point method for m in partition-y.

For the trusted partition (i.e., `Trusted.java`), the program partitioner generates C entry point methods corresponding to all the trusted functions. These entry point methods are the target of `ecalls` from the untrusted code calling a trusted function in the final SGX application. Similarly, for the untrusted partition (i.e., `Untrusted.java`), C entry point methods are generated for the untrusted functions. These entry point methods are the target of `ocalls` from the trusted partition calling an untrusted function.

### 4.4.3 Serialisation.
Because objects cannot be sent across an enclave boundary [12, 66], any static methods that have non-primitive input or return types (e.g., `arrays`, `strings`, `etc`) to be transferred across the enclave serialise the input or return values into a byte array. The byte array is then marshalled across the enclave boundary in the corresponding `ecall` or `ocall`, and deserialised on the opposite side into a Java `Object`. Serialisation and deserialisation are done using Java's `ObjectOutputStream` and `ObjectInputStream` [38] classes, respectively.

### 4.4.4 Execution of a function.
Thanks to native image, the trusted and the untrusted partitions execute the (binary) compiled version of the corresponding `TRUFFLE` guest language interpreter at runtime. At runtime, the guest language

interpreter parses the string that represents the guest code, builds the AST, and executes it. When the AST of a function f contains a call to another function g, the symbol table of the interpreter may not contain the symbol g. In that case, the `TRUFFLE` API leverages Java reflection to find a static Java method that corresponds to the symbol g and executes it. As an example, in Listing 5, Java reflection is used to expose the Java function `javaHello` (or the JS function `multi`) to `TRUFFLE` interpreter when executing `jsHello`.

## 4.5 Building native images and the SGX program

The aim of `ImageGenerator` is to AoT compile `Trusted.java` and `Untrusted.java` into relocatable object files (`trusted.o` and `untrusted.o` respectively). When building a native image, `GRAALVM` makes it possible to provide which guest language implementations (i.e., the `TRUFFLE` interpreters) should be made available in the resulting native image. In the case of `SECV`, all guest languages involved in the partitioned program are provided as inputs to the *native image generator*. This tool performs static analysis (see §2.2) [60] to determine the reachable Java elements, i.e., the classes, methods, and objects from the Java program that is being compiled and the `TRUFFLE` language implementations that are required to run the corresponding Java programs. These reachable components are then AoT compiled into native images: `trusted.o` and `untrusted.o`.

It is worth noting that the native image builder does not AoT compile guest language code (e.g., JS, Python, etc.) that is embedded in the Java programs. Indeed, the guest language code will be interpreted or JIT compiled by `GRAALVM` at run time. The `ecall` and `ocall` definitions are compiled into object files and linked with `trusted.o` and `untrusted.o`, as well as `SGX C/C++` library code, to create the final Intel `SGX` application. A small shim library is included inside the enclave which seamlessly relays unsupported system calls (e.g., `read`, `write`, etc.) to the untrusted runtime via `ocalls`, which perform the real system calls and return the results back to the enclave.

# 5 Evaluation

The experimental evaluation of `SECV` seeks to answer the following research questions:

**RQ1:** What is the implementation effort for a multi-language tool (i.e., `SECV`) as compared to language-specific tools? (§5.1.1)

**RQ2:** What is the cost of injecting `SECV` nodes into a program AST? (§5.1.2)

**RQ3:** What is the cost of taint tracking with `POLYTAINT`? (§5.1.3)

**RQ4:** How does partitioning affect application performance? (§5.1.4)
<table><caption>**Table 1.** Total lines of code for existing partitioning tools and their underlying frameworks.</caption><thead><tr><th>Partitioning tool</th><th>Supported languages</th><th>FrameWORKS used + LoC</th><th>Tool's LoC</th></tr></thead><tbody><tr><td>Civet</td><td>Java</td><td>SOOT + Phosphor (≈ 421K LoC)</td><td>6,870</td></tr><tr><td>Montsalvat</td><td>Java</td><td>Javassist (≈ 38K LoC)</td><td>3,500</td></tr><tr><td>Glamdring</td><td>C</td><td>Frama-C + Program slicer (≈ 90K LoC)</td><td>5,000</td></tr><tr><td>SECV</td><td>JS, Python, R, etc.</td><td>Truffle (≈ 276K LoC)</td><td>4,880</td></tr></tbody></table>

<figure><img src="image_6.png" /><figcaption>**Figure 6.** Cost of using secure values in JS and Python programs.</figcaption></figure>

## 5.1 Experimental setup

Our evaluation is conducted on a server equipped with a quad-core Intel Xeon E3-1270 CPU clocked at 3.80 GHz, and 64 GB of DRAM. The processor has 32 KB L1 instruction and data caches, a 256 KB L2 cache, and a 8 MB L3 cache. The server runs Ubuntu 18.04.1 LTS 64 bit and Linux 4.15.0-142. We run the Intel SGX platform software with version 2.16 of the SDK and and version 2.14 of the driver. The EPC size on this server is 128 MB, of which 93.5 MB is usable by enclaves. The enclaves have maximum heap sizes of 8 GB and stack sizes of 8 MB. All native images are built with a maximum heap size of 4 GB. We use GRAALVM version 22.1.0. All reported measurements are averaged over 5 runs.

### 5.1.1 Implementation efforts (RQ1). What is the implementation effort for a multi-language tool as compared to language-specific tools?
Table 1 reports the total lines of code (LoC) of various enclave code partitioning tools, as well as the LoC for the underlying frameworks used by these tools. We observe that SECV's code base is ≈ 1.41× and ≈ 1.02× smaller as compared to the full code bases of Civet and Glamdring, respectively. We argue that the SECV approach is better in terms of code simplicity (leverages a single self-contained framework) and efficiency (achieves the desired functionality with fewer lines of code). Furthermore, SECV provides a single extensible multi-language system in relatively fewer LoC, contrary to the other tools which are language specific.

### 5.1.2 Overhead of SecV nodes (RQ2). What is the cost of injecting SECV nodes into a program's AST?

The goal of this experiment is to measure the overhead caused by the use of secure nodes in a polyglot program. In this regard, we generate synthetic programs in JS and Python that comprise functions with varying numbers of variables that receive secure values (e.g., secure `int`, double

<figure><img src="image_7.png" /><figcaption>**Figure 7.** Cost of taint tracking with POLYTAINT for JS and Python.</figcaption></figure>

values) via the polyglot API. We compare the run time of the functions with a similar setup where the functions define regular values, i.e., no injection of SECV nodes via the polyglot API. Fig. 6 shows the results obtained for different value types used.

*Observation.* For JS programs, using `sInt` variables is ≈ 1.05× slower on average as compared to using regular integer variables without the polyglot API. Moreover, using `sDouble` variables is ≈ 1.04× slower on average as compared to using regular double type variables without the polyglot API. For Python programs, using `sInt` variables is ≈ 1.14× slower on average as compared to using regular `int` type variables without the polyglot API. Furthermore, using `sDouble` variables in Python is ≈ 1.11× slower on average as compared to using regular double type variables.

*Discussion.* For both JS and Python, using SECV nodes involves calls to the `TRUFFLE` polyglot API, as opposed to defining regular program variables for which there is no intermediate API involved. This explains the additional cost when introducing secure values in the program. In practice, programs will typically define few secure variables leading to a small overhead relative to the total runtime cost of the full program

*Take-away 1: The cost of injecting secure nodes into a program's AST is small.*

### 5.1.3 Overhead of taint tracking (RQ3). What is the cost of taint tracking with POLYTAINT?

In this experiment, we aim to measure the performance of taint tracking with POLYTAINT. To that end, we generate synthetic polyglot programs in JS and Python. The synthetic programs consist of a varying number of functions that all perform a bubble sort on a SECV array of 1,000 randomly generated numbers. We chose a bubble sort algorithm here because it performs many variable read/write operations, which trigger the creation of taint tracking instrumentation nodes in POLYTAINT. The programs run on GRAALVM using their corresponding `TRUFFLE` interpreters, with or without POLYTAINT taint tracking enabled. Fig. 7 shows the comparative performance of both setups.
<figure><img src="image_8.png" /><figcaption>**Figure 8.** Effect of program partitioning on a generic synthetic program.</figcaption></figure>

**Observation.** The experimental results show that the JS program running with POLYTAINT taint tracking enabled is ≈ 1.56× slower when compared to the JS program running without taint tracking enabled. Similarly, for the Python program running with POLYTAINT taint tracking enabled, it is ≈ 1.31× slower on average when compared to the variant without taint tracking enabled.

**Discussion.** For both JS and Python programs, POLYTAINT introduces more nodes (i.e., wrappers and execution event nodes) to their ASTs during instrumentation. As seen in §4.3, those nodes perform operations to track tainted variables, which explains the performance decrease (i.e., longer runtime) when taint tracking is enabled, as compared to the variants where the programs run without any taint tracking.

**Take-away 2:** Taint tracking via AST instrumentation introduces overhead at run time. This overhead is due to the additional operations performed by instrumentation nodes introduced in the program's AST.

### 5.1.4 Effect of partitioning on performance (RQ4).

*How does partitioning affect application performance?*

To study the performance impact of partitioning on application performance with SECV, we first use a synthetic polyglot benchmark written in JS. We then partition two real-world applications that implement the PageRank algorithm [2] and the linear regression ML algorithm [64].

**Synthetic benchmark.** Our synthetic benchmark is a JS program that comprises 100 functions that each perform a bubble sort on a local array variable of size 100. We leverage secure values (secure array or regular array) in a varying percentage of the functions, analyse the polyglot programs with POLYTAINT and partition these programs for enclaves. The purpose of our synthetic benchmark is to highlight the effect of partitioning on a generic program. Fig. 8 shows the results.

We observe that as more functions are partitioned out of the enclave, the overall performance of the program improves. This is explained firstly by the reduced number of libc-related ocalls performed by the embedded GRAALVM runtime components (language interpreters, GC, etc.), and

<figure><img src="image_9.png" /><figcaption>**Figure 9.** Results for partitioning the PageRank and Linear Regression programs.</figcaption></figure>

secondly by the fact that we have less overhead due to enclave data encryption-decryption operations to and from the EPC by the MEE [66].

**Take-away 3:** Enclave performance improves as more computations are delegated to the untrusted side. This is due to less expensive enclave context switches, e.g., via ocalls, as well as less expensive enclave-related cryptographic operations.

We have observed the effect of partitioning on a generic synthetic program. We now leverage SECV to partition real-world applications. For these applications, we evaluate 3 modes for running the programs: entirely inside the enclave (no-part), as a normal polyglot native image without SGX (native), and as a partitioned native image using SECV (part). The partitioning scheme adopted for each application is not necessarily realistic, but aims to highlight and explain the performance improvement obtained after partitioning.

**PageRank.** PageRank [2] is a popular algorithm that is used in graph processing frameworks [22] to weigh the relative importance of nodes (i.e., node ranks) in a directed graph. One could envision a scenario where the node ranks are to be secured within an enclave. We leverage SECV to partition a PageRank program with the node rank data structure (a secure array) tagged as a secure variable. The PageRank program comprises a function to generate a directed graph and obtain the corresponding adjacency list (graph pre-processing), and other functions which use the adjacency list to perform the PageRank algorithm. The graphs are generated using the RMAT algorithm [8], and all graphs with *n* vertices have 2·*n* edges. Fig. 9 (a) shows the results obtained when running partitioned and unpartitioned versions of PageRank with varying graph sizes.

**Observation.** The partitioned version of the PageRank program is ≈ 1.12× faster on average (i.e., about 10.8% performance improvement) as compared to the unpartitioned version. Furthermore, we have on average ≈ 2.53× fewer ocalls in the partitioned program as compared to the unpartitioned one. The native version of the program (i.e., no SGX) is ≈ 6.85× and 6.17× faster on average as compared to the unpartitioned and partitioned versions, respectively.
**Discussion.** After partitioning the program, the functions responsible for graph generation and pre-processing are moved to the untrusted partition, while those that perform the PageRank algorithm to obtain node ranks remain in the trusted partition. At runtime, the pre-processed graph (in the form of an adjacency list) is serialised in the untrusted partition and marshalled into the enclave (see §4.4.3) via an *ocall*. It is then deserialised to recreate the adjacency list object which is used by the in-enclave PageRank algorithm to compute the final page ranks. The computations outside the enclave runtime do not incur expensive context switches via *ocalls*, or SGX-related cryptographic operations. That explains the performance improvement as well as the reduced number of *ocalls* in the partitioned version as compared to running the full program inside the enclave. This also explains why the native application is fastest: no SGX-related cryptographic operations, no enclave context switches, and less memory restrictions (the enclave has only about 93MB of memory). However, it is the least secure of all.

**Linear regression.** Securing ML models with TEEs is common [25, 65]. We aim to partition a linear regression program for an enclave runtime. Similar to PageRank, a linear regression program typically consists of functions to read and pre-process (i.e., normalisation and standardisation) the input datasets to be used, and other functions which use the dataset to train the ML model. We leverage SECV to specify secure variables (i.e., m and c in Listing 2), analyse and partition the linear regression program. Fig. 9 (b) shows the results when the linear regression model is trained for 100 training iterations for partitioned and unpartitioned versions of the program.

**Observation.** The partitioned version of the linear regression program is ≈ 1.17× faster on average (i.e., about 14.5% performance improvement) as compared to the unpartitioned version. Moreover, we have on average ≈ 1.36× fewer *ocalls* in the partitioned program when compared to the unpartitioned one. The native version of the program is ≈ 4.73× and 4.04× faster on average when compared to the unpartitioned and partitioned versions, respectively.

**Discussion.** After program partitioning, the functions for data generation and pre-processing (which do not access m and c) are partitioned out of the enclave, while the trusted functions that train the model (and hence access the secure values m and c) are part of the enclave partition. At runtime, dataset pre-processing is done in the untrusted partition; this entails standardising both the X and Y datasets. The standardised datasets (arrays) are then serialised and marshalled into the enclave runtime via *ocalls*, deserialised inside the enclave, and used to train the linear regression model. By offloading the data generation and pre-processing phases to the untrusted runtime, the enclave is relieved of expensive computations, which leads to an overall improvement in application performance when compared to running the full program

inside the enclave. Similar to PageRank, partitioning introduces overhead for data serialisation and deserialisation, but this overhead is offset by the performance gain from performing some computations outside (i.e., no expensive *ocalls*), rather than inside the enclave.

**Take-away 4:** *The TCB size of programs can be decreased without degrading performance. As a matter of fact, the performance analysis of both PageRank and linear regression shows that decreasing the TCB size via partitioning can lead to better performance: security and performance are improved at the same time.*

# 6 Limitations of our design

**Limited code coverage.** The present design of SECV is based on a purely dynamic program analysis approach, where programs are run once during the development phase to deduce the set of *trusted* (*T*), *neutral* (*N*), and *untrusted* (*U*) functions, which are then used to create the trusted and untrusted partitions. However, this design provides limited code coverage, potentially leaving security-sensitive code out of the enclave; limited code coverage is a fundamental limitation of most dynamic analysis tools [20]. To address this problem, *static analysis* can be applied, but it has major drawbacks. Firstly, it performs an over-approximation of the code to be tainted (due to polymorphism), which leads to a larger TCB. Secondly, Truffle ASTs exist only at runtime, making static analysis infeasible. Ultimately, a mixture of both static and dynamic taint analysis techniques could be a reasonable compromise. We leave this as future work.

**Application termination.** During analysis, different program runs (with varying inputs) could result in different execution paths being taken at runtime. This could in turn result in different (and possibly conflicting) sets for *T*, *N*, and *U*. From a security perspective, such a scenario may lead to secure values being leaked to the untrusted partition at runtime. In our prototype implementation, we avoid this problem by checking if a value is secure just before serializing it in in-enclave proxies. Thanks to this check, in the worst case, the application will terminate with an error, but will not let a secure value escape the enclave. Running multiple tests or using symbolic execution could decrease the probability of terminating the program. Similarly, we leave this implementation as future work.

# 7 Leveraging SECV for other use cases

SECV's design can be extended to encompass more generic compartmentalization of applications to not only enhance security, but also promote modularity and maintainability. In terms of security, the current design can be extended to support Intel's new TEE technology: *trust domain extensions (TDX)* [10]. This entails running the trusted and untrusted partitions in separate VMs, or "trust domains".
In a context unrelated to security, SECV can also be extended to partition applications into smaller loosely coupled microservices that run in dedicated VMs, hence promoting flexibility and maintainability.

## 8 Related work

We classify related work into 3 categories: (i) tools that run full, unmodified applications inside enclaves, (ii) taint tracking tools, and (iii) code partitioning tools.

**Running full applications inside enclaves.** Various tools like SCONE [4], Graphene-SGX [56], TWINE [24], and SGX-LKL [49] propose solutions to run entire legacy applications inside enclaves. Their approach severely increases the size of the TCB, at the risk of added security vulnerabilities. SECV provides a generic solution to partition applications for enclaves while trying to keep the TCB as small as possible.

**Taint tracking tools.** Several tools exist for taint tracking. Phosphor [5] is a dynamic taint tracking tool for Java programs, while Dytan [11] performs taint tracking in x86 binaries. TAJ [55] provides efficient static taint analysis for Java applications. [54] provide a tool to extract taint specifications for JavaScript libraries. However, these tools are language-specific (i.e., only for Java, only for x86 assembly code, JS, etc.) and cannot be readily leveraged to analyse code in different languages. SECV bridges this gap with a language-agnostic taint tracking approach. TruffleTaint [21] leverages the TRUFFLE framework to provide a language-agnostic platform to build dynamic taint analysis applications. While we leverage very similar instrumentation techniques, we provide a novel way to specify sensitive values through the introduction of secure AST nodes, and leverage these to partition code for the enclave runtime in a language-agnostic fashion.

**Language specific partitioning tools.** Several tools have been proposed to partition code written in specific languages for enclaves. Glamdring [23] provides a technique to automatically partition C applications, while Montsalvat [66], Civet [57], and Uranus [18] propose solutions to partition Java applications for Intel SGX enclaves. Among those partitioning systems, none provide a language-independent way to partition enclave programs. SECV solves this problem by introducing a multi-language technique to partition programs for enclaves.

## 9 Conclusion and Future Work

In this paper, we presented SECV, a multi-language approach to analyse and partition programs for Intel SGX enclaves. SECV provides generic *secure nodes* that encapsulate sensitive program data, and that can be injected into the ASTs of programs written in a wide range of programming languages. SECV provides a dynamic taint tracking tool, POLYTAINT, which tracks the flow of sensitive data from *secure nodes* in a program at run time, and partitions the program into trusted and untrusted parts which are executed in and out

of the secure enclave, respectively. Our evaluation of SECV shows it can reduce the program's TCB size without any performance degradation.

**Future work.** We plan to extend SECV along two directions:

*More data types.* This entails extending SECV's *secure node generator* to cover more data types and data structures, e.g., maps, lists, etc., as described in §4.1.

*Support for more languages.* POLYTAINT can be extended to fully cover more TRUFFLE languages like TruffleRuby, FastR, etc. Regarding LLVM-based languages like C/C++, the Truffle framework provides an LLVM interpreter, Sulong [42]. This interpreter can also be extended to run programs in Go, by leveraging tools like GoLLVM [1]. Alternatively, Truffle's W ebAssembly (Wasm) implementation can be leveraged to provide a common compilation target for supported languages: Rust, Go, Kotlin, etc. Adding support for these languages in SECV mainly involves incorporating wrapper nodes to handle language-specific semantic constructs and AST nodes in the new language. There is in-depth documentation online to facilitate these extensions.

## Acknowledgments

This work has been supported in part by Oracle donation CR 3801 and project 200021_178822 of the Swiss National Science Foundation (FNS). We are grateful to the anonymous reviewers and our shepherd, Alexios Voulimeneas, for their valuable feedback.

## References

[1] GoLLVM. https://go.googlesource.com/goLLvm/, 2023. Accessed: Sept. 19, 2023.

[2] Alon Altman and Moshe Tennenholtz. Ranking systems: the PageRank axioms. In *Proceedings of the 6th ACM conference on Electronic Commerce (EC 05)*, Vancouver, BC, Canada, 2005.

[3] Julien Amacher and Valerio Schiavoni. On the performance of ARM TrustZone. In *IFIP International Conference on Distributed Applications and Interoperable Systems*, pages 133–151. Springer, 2019.

[4] Sergei Arnautov, Bohdan Trach, Franz Gregor, Thomas Knauth, Andre Martin, Christian Priebe, Joshua Lind, Divya Muthukumaran, Dan O'Keeffe, Mark L. Stillwell, David Goltzsche, Dave Eyers, Rüdiger Kapitza, Peter Pietzuch, and Christof Fetzer. SCONE: Secure Linux containers with intel SGX. In *12th USENIX Symposium on Operating Systems Design and Implementation (OSDI 16)*, Savannah, GA, USA, 2016.

[5] Jonathan Bell and Gail Kaiser. Dynamic taint tracking for java with phosphor (demo). In *Proceedings of the 2015 International Symposium on Software Testing and Analysis, ISSTA 2015*, page 409–413, New York, NY, USA, 2015. Association for Computing Machinery.

[6] Daniele Bonetta. GraalVM: metaprogramming inside a polyglot system (invited talk). In *Proceedings of the 3rd ACM SIGPLAN International Workshop on Meta-Programming Techniques and Reflection (SPLASH 18)*, Boston, MA, USA, 2018.

[7] Jo Van Bulck, Marina Minkin, Ofir Weisse, Daniel Genkin, Baris Kasikci, Frank Piessens, Mark Silberstein, Thomas F. Wenisch, Yuval Yarom, and Raoul Strackx. Foreshadow: Extracting the keys to the Intel SGX kingdom with transient out-of-order execution. In *27th USENIX Security Symposium (USENIX Security 18)*, Baltimore, MD, USA, 2018.
[8] Deepayan Chakrabarti, Yiping Zhan, and Christos Faloutsos. R-MAT: A recursive model for graph mining. In Proceedings of the 2004 SIAM International Conference on Data Mining (SDM 04), Lake Buena Vista, FL, USA, 2004.

[9] Guoxing Chen, Yinqian Zhang, and Ten-Hwang Lai. OPERA: Open remote attestation for Intel's secure enclaves. In Proceedings of the 2019 ACM SIGSAC Conference on Computer and Communications Security (CCS 19), London, United Kingdom, 2019.

[10] Pau-Chen Cheng, Wojciech Ozga, Enriquillo Valdez, Salman Ahmed, Zhongshu Gu, Hani Jamjoom, Hubertus Franke, and James Bottomley. Intel tdx demystified: A top-down approach. arXiv preprint arXiv:2303.15540, 2023.

[11] James Clause, Wanchun Li, and Alessandro Orso. Dytan: A generic dynamic taint analysis framework. In Proceedings of the 2007 International Symposium on Software Testing and Analysis, ISSTA '07, page 196–206, New York, NY, USA, 2007. Association for Computing Machinery.

[12] Intel Corporation. Intel SGX Developer Ref. for Linux. https://download.01.org/intel-sgx/sgx-linux/2.15/docs/Intel_SGX_Developer_Reference_Linux_2.15_Open_Source.pdf. Accessed: Oct 23, 2022.

[13] Victor Costan and Srinivas Devadas. Intel SGX explained. IACR Cryptology ePrint Archive, 2016(86):1–186, 2016.

[14] Adrien Ghosn, James R Larus, and Edouard Bugnion. Secured routines: Language-based construction of trusted execution environments. In 2019 USENIX Annual Technical Conference (USENIX ATC 19), pages 571–586, 2019.

[15] Matthias Grimmer, Chris Seaton, Roland Schatz, Thomas Würthinger, and Hanspeter Mössenböck. High-performance cross-language interoperability in a multi-language runtime. SIGPLAN Not., 51(2):78–90, oct 2015.

[16] Daniel Gruss, Julian Lettner, Felix Schuster, Olga Ohrimenko, István Haller, and Manuel Costa. Strong and efficient cache side-channel protection using hardware transactional memory. In 26th USENIX Security Symposium, (USENIX Security 2017), Vancouver, BC, Canada, 2017.

[17] Intel. Reference Architecture for Privacy Preserving Machine Learning with Intel® SGX and TensorFlow* Serving. https://www.intel.com/content/www/us/en/developer/articles/technical/privacy-preserving-ml-with-sgx-and-tensorflow.html, 2022. Accessed: Oct 28, 2022.

[18] Jianyu Jiang, Xusheng Chen, TszOn Li, Cheng Wang, Tianxiang Shen, Shixiong Zhao, Heming Cui, Cho-Li Wang, and Fengwei Zhang. Uranus: Simple, efficient SGX programming and its applications. In Proceedings of the 15th ACM Asia Conference on Computer and Communications Security (ASIACCS 2020), Taipei, Taiwan, 2020.

[19] David Kaplan, Jeremy Powell, and Tom Woller. AMD memory encryption. White paper, 2016.

[20] Paul Kirth, Mitchel Dickerson, Stephen Crane, Per Larsen, Adrian Dabrowski, David Gens, Yeoul Na, Stijn Volckaert, and Michael Franz. Pkru-safe: Automatically locking down the heap between safe and unsafe languages. In Proceedings of the Seventeenth European Conference on Computer Systems, EuroSys '22, page 132–148, New York, NY, USA, 2022. Association for Computing Machinery.

[21] Jacob Kreindl, Daniele Bonetta, Lukas Stadler, David Leopoldseder, and Hanspeter Mössenböck. Multi-language dynamic taint analysis in a polyglot virtual machine. In Proceedings of the 17th International Conference on Managed Programming Languages and Runtimes, MPLR 2020, page 15–29, New York, NY, USA, 2020. Association for Computing Machinery.

[22] Aapo Kyrola, Guy Blelloch, and Carlos Guestrin. Graphchi: Large-scale graph computation on just a PC. In 10th USENIX Symposium on Operating Systems Design and Implementation (OSDI 12), Boston, MA, USA, 2012.

[23] Joshua Lind, Christian Priebe, Divya Muthukumaran, Dan O'Keeffe, Pierre-Louis Aublin, Florian Kelbert, Tobias Reiher, David Goltzsche, David Eyers, Rüdiger Kapitza, Christof Fetzer, and Peter Pietzuch. Glamdring: Automatic application partitioning for Intel SGX. In 2017 USENIX Annual Technical Conference (USENIX ATC 17), Santa Clara, CA, USA, 2017.

[24] Jämes Ménétrey, Marcelo Pasin, Pascal Felber, and Valerio Schiavoni. Twine: An embedded trusted runtime for webassembly. In 37th IEEE International Conference on Data Engineering, ICDE 2021, Chania, Greece, April 19-22, 2021, pages 205–216. IEEE, 2021.

[25] Payman Mohassel and Yupeng Zhang. Secureml: A system for scalable privacy-preserving machine learning. In 2017 IEEE Symposium on Security and Privacy (SP), pages 19–38, 2017.

[26] Oleksii Oleksenko, Bohdan Trach, Robert Krahn, Mark Silberstein, and Christof Fetzer. Varys: Protecting SGX enclaves from practical side-channel attacks. In 2018 USENIX Annual Technical Conference (USENIX ATC 2018), Boston, MA, USA, 2018.

[27] Oracle. GraalVM Profiling Command Line Tools. https://www.graalvm.org/22.2/tools/profiling/.

[28] Oracle. GraalVM SDK Java API Reference – CEntryPoint. https://www.graalvm.org/sdk/javadoc/org/graalvm/nativeimage/c/function/CEntryPoint.html, 2021. Accessed: April 29, 2021.

[29] Oracle. Execution Event Node. https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/instrumentation/ExecutionEventNode.html, 2022. Accessed: Oct 11, 2022.

[30] Oracle. Fast R. https://github.com/oracle/fastr, 2022. Accessed: Oct 11, 2022.

[31] Oracle. Graal Python. https://github.com/oracle/graalpython, 2022. Accessed: Oct 11, 2022.

[32] Oracle. GraalJS. https://github.com/oracle/graaljs, 2022. Accessed: Oct 11, 2022.

[33] Oracle. GraalVM Chrome Debugger. https://www.graalvm.org/22.2/tools/chrome-debugger/, 2022. Accessed: Oct 11, 2022.

[34] Oracle. Interface Frame. https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/frame/Frame.html, 2022. Accessed: Oct 15, 2022.

[35] JSFunctionCallNode. https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/function/JSFunctionCallNode.java, 2022. Accessed: Oct 15, 2022.

[36] Oracle. JSTags. https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.truffle.js/src/oracle/truffle/js/nodes/instrumentation/JSTags.java, 2022. Accessed: Oct 15, 2022.

[37] Oracle. NodeObjectDescriptor. https://github.com/oracle/graalpython/blob/master/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/instrumentation/NodeObjectDescriptor.java, 2022. Accessed: Oct 15, 2022.

[38] Oracle. Object Streams. https://docs.oracle.comjavase/tutorial/essential/io/objectstreams.html, 2022. Accessed: Oct 20, 2022.

[39] Oracle. Polyglot Programming. https://www.graalvm.org/22.0/reference-manual/polyglot-programming/, 2022. Accessed: Feb 18, 2022.

[40] Oracle. PythonCallNode. https://github.com/oracle/graalpython/blob/master/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/PythonCallNode.java, 2022. Accessed: March 1, 2022.

[41] Oracle. Standard Tags. https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/instrumentation/StandardTags.html, 2022. Accessed: Oct 11, 2022.

[42] Oracle. Sulong. https://github.com/oracle/graal/tree/master/sulong, 2022. Accessed: Oct 11, 2022.

[43] Oracle. Truffle Language Implementation Framework. https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/, 2022. Accessed: Feb 18, 2022.
<footnote>[44] Oracle. Truffle Language Implementation Framework. https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/instrumentation/InstrumentableNode WrapperNode.html, 2022. Accessed: Oct 3, 2022.</footnote>

<footnote>[45] Oracle. Truffle Node. https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/nodes/Node.html, 2022. Accessed: Oct 15, 2022.</footnote>

<footnote>[46] Oracle. Truffle Ruby. https://github.com/oracle/truffleruby, 2022. Accessed: Oct 11, 2022.</footnote>

<footnote>[47] Oracle. Graal Compiler. https://www.graalvm.org/22.2/reference-manual/java/compiler/, 2023.</footnote>

<footnote>[48] Sandro Pinto and Nuno Santos. Demystifying Arm TrustZone: A Comprehensive Survey. *ACM Comput. Surv.*, 51(6), jan 2019.</footnote>

<footnote>[49] Christian Priebe, Divya Muthukumaran, Joshua Lind, Huanzhou Zhu, Shujie Cui, Vasily A Sartakov, and Peter Pietzuch. SGX-LKL: Securing the host OS interface for trusted execution. *arXiv preprint arXiv:1908.11143*, 2019.</footnote>

<footnote>[50] Do Le Quoc, Franz Gregor, Sergei Arnautov, Roland Kunkel, Pramod Bhatotia, and Christof Fetzer. SecureTF: A secure TensorFlow framework. In *Proceedings of the 21st International Middleware Conference (Middleware 20)*, New York, NY, USA, 2020.</footnote>

<footnote>[51] Jerome H Saltzer and Michael D Schroeder. The protection of information in computer systems. *Proceedings of the IEEE*, 63(9):1278-1308, September 1975.</footnote>

<footnote>[52] Michael Schwarz, Samuel Weiser, Daniel Gruss, Clémentine Maurice, and Stefan Mangard. Malware guard extension: Using SGX to conceal cache attacks. In *International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment (DIMVA 17)*, Bonn, Germany, 2017.</footnote>

<footnote>[53] Youren Shen, Hongliang Tian, Yu Chen, Kang Chen, Runji Wang, Yi Xu, Yubin Xia, and Shoumeng Yan. Occlum: Secure and efficient multitasking inside a single enclave of intel SGX. In James R. Larus, Luis Ceze, and Karin Strauss, editors, *ASPLOS '20: Architectural Support for Programming Languages and Operating Systems, Lausanne, Switzerland, March 16-20, 2020*, pages 955-970. ACM, 2020.</footnote>

<footnote>[54] Cristian-Alexandru Staicu, Martin Toldam Torp, Max Schäfer, Anders Møller, and Michael Pradel. Extracting taint specifications for javascript libraries. In *Proceedings of the ACM/IEEE 42nd International Conference on Software Engineering, ICSE '20*, page 198-209, New York, NY, USA, 2020. Association for Computing Machinery.</footnote>

<footnote>[55] Omer Tripp, Marco Pistoia, Stephen J. Fink, Manu Sridharan, and Omri Weisman. Taj: Effective taint analysis of web applications. In *Proceedings of the 30th ACM SIGPLAN Conference on Programming Language Design and Implementation, PLDI '09*, page 87-97, New York, NY, USA, 2009. Association for Computing Machinery.</footnote>

<footnote>[56] Chia-Che Tsai, Donald E. Porter, and Mona Vij. Graphene-SGX: A practical library OS for unmodified applications on SGX. In *2017 USENIX Annual Technical Conference (USENIX ATC 2017)*, Santa Clara, CA, USA, 2017.</footnote>

<footnote>[57] Chia-Che Tsai, Jeongseok Son, Bhushan Jain, John McAvey, Raluca Ada Popa, and Donald E. Porter. Civet: An efficient Java partitioning framework for hardware enclaves. In *29th USENIX Security Symposium (USENIX Security 20)*, Online, 2020.</footnote>

<footnote>[58] Michael L Van de Vanter, Chris Seaton, Michael Haupt, Christian Humer, and Thomas Würtheringer. Fast, flexible, polyglot instrumentation support for debuggers and other tools. *Art Sci. Eng. Program.*, 2(3):14, 2018.</footnote>

<footnote>[59] Nico Weichbrodt, Pierre-Louis Aublin, and Rüdiger Kapitza. sgx-perf: A performance analysis tool for intel SGX enclaves. In *Paulo Ferreira and Liuba Shrira, editors, Proceedings of the 19th International Middleware Conference (Middleware 2018)*, Rennes, France, 2018.</footnote>

<footnote>[60] Christian Wimmer, Codrut Stancu, Peter Hofer, Vojin Jovanovic, Paul Wögerer, Peter B Kessler, Oleg Pliss, and Thomas Würtheringer. Initialize once, start fast: application initialization at build time. In *2019 ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA 19)*. Athens, Greece, 2019.</footnote>

<footnote>[61] Thomas Würtheringer, Christian Wimmer, Andreas Wöß, Lukas Stadler, Gilles Duboscq, Christian Humer, Gregor Richards, Doug Simon, and Mario Wolczko. One vm to rule them all. Onward! 2013, page 187-204, New York, NY, USA, 2013. Association for Computing Machinery.</footnote>

<footnote>[62] Thomas Würtheringer, Andreas Wöß, Lukas Stadler, Gilles Duboscq, Doug Simon, and Christian Wimmer. Self-optimizing ast interpreters. *SIGPLAN Not.*, 48(2):73-82, oct 2012.</footnote>

<footnote>[63] Bin Cedric Xing, Mark Shanahan, and Rebekah Leslie-Hurd. Intel® software guard extensions (intel® sgx) software support for dynamic memory allocation inside an enclave. In *Proceedings of the Hardware and Architectural Support for Security and Privacy 2016, HASP 2016*, New York, NY, USA, 2016. Association for Computing Machinery.</footnote>

<footnote>[64] Wenju Xu, Baocang Wang, Jiasen Liu, Yange Chen, Pu Duan, and Zhiyong Hong. Toward practical privacy-preserving linear regression. *Inf. Sci.*, 596(C):119-136, jun 2022.</footnote>

<footnote>[65] Peterson Yuhala, Pascal Felber, Valerio Schiavoni, and Alain Tchana. Plinius: Secure and persistent machine learning model training. In *2021 51st Annual IEEE/IFIP International Conference on Dependable Systems and Networks (DSN)*, pages 52-62, 2021.</footnote>

<footnote>[66] Peterson Yuhala, Jāmes Ménétrey, Pascal Felber, Valerio Schiavoni, Alain Tchana, Gaël Thomas, Hugo Guiroux, and Jean-Pierre Lozi. Montsalvat: Intel SGX Shielding for GraalVM Native Images. In *Proceedings of the 22nd International Middleware Conference, Middleware '21*, page 352-364, New York, NY, USA, 2021. Association for Computing Machinery.</footnote>