Abstract
We introduce a novel type system for enforcing secure information flow in an imperative language. Our work is motivated by the problem of statically checking potential information leakage in Android applications. To this end, we design a lightweight type system featuring Android permission model, where the permissions are statically assigned to applications and are used to enforce access control in the applications. We take inspiration from a type system by Banerjee and Naumann to allow security types to be dependent on the permissions of the applications. A novel feature of our type system is a typing rule for conditional branching induced by permission testing, which introduces a merging operator on security types, allowing more precise security policies to be enforced. The soundness of our type system is proved with respect to non-interference. A type inference algorithm is also presented for the underlying security type system, by reducing the inference problem to a constraint solving problem in the lattice of security types. In addition, a new way to represent our security types as reduced ordered binary decision diagrams is proposed.
Background and introduction
Mobile security has become increasingly important for our daily life due to the pervasive use of mobile applications. Among the mobile devices that are currently in the market, Android devices account for the majority of them so analyses of their security have been of significant interest. There has been a large number of analyses on Android security ([2,17,19,26,53]) focusing on detecting potential security violations. Here we are interested instead in the problem of constructing secure applications, in particular, in providing guarantees of information flow security in the constructed applications.
We follow the language-based security approach whereby information flow security is enforced through type systems [12,13,46,52]. In particular, we propose a design of a type system that guarantees a non-interference property [52], i.e., typable programs are non-interferent. As shown in [20], non-interference provides a general and natural way to model information flow security. The type-based approach to non-interference requires assigning security labels to program variables and security policies to functions or procedures. Such policies are typically encoded as types, and typeability of a program implies that the runtime behavior of the program complies with the stated policies. Security labels form a lattice structure with an underlying partial order ⩽, e.g., a lattice with two elements “high” (H) and “low” (L) where

Getting contact info with a permission check
In designing an information flow type system for Android, we encounter a common pattern of conditionals that would not be typable using conventional type systems. Consider the pseudo-code in Listing 1. Such a code fragment could be part of a phone dialer or a social network service app such as Facebook and WhatsApp, where getContactNo provides a public interface to query the phone number associated with a name. The (implicit) security policy in this context is that contact information (the phone number) can only be released if the calling app has the permission READ_CONTACT. The latter is enforced using the checkPermission API in Android. Suppose phone numbers are labelled with H, and the empty string is labelled with L. If the interface is invoked by an app that has the required permission, the phone number (H) is returned; otherwise an empty string (L) is returned. In both cases, no data leakage happens: in the former case, the calling app is authorized; and in the latter case, no sensitive data is ever returned. By this informal reasoning, the function complies with the implicit security policy and it should be safe to be called in any context, regardless of the permissions the calling app has. However, in the traditional (non-value dependent) typing rule for the if-then-else construct, one would assign the same security level to both branches, and the return value of the function would be assigned level H. As a result, if this function is called from an app with no permission, assigning the return value to a variable with security level L has to be rejected by the type system even though no sensitive information is leaked. To cater for such a scenario, we need to make the security type of getContactNo depend on the permissions possessed by the caller.
Banerjee and Naumann [3] proposed a type system (which we shall refer to as
In
where Q is a set of permissions that are disabled. When
In adapting
Some permissions in Android 6 and above require user approval at runtime, but for the purpose of typing the ‘test’ command, we make the assumption that these permissions are enabled as well.

An example with a non-monotonic policy
For an example of an application for which a non-monotonic policy is desirable, consider for example an application that provides the location information related to a certain advertising ID (in Listing 2), where the latter provides a unique ID for the purpose of anonymizing mobile users to be used for personalized advertising (instead of relying on hardware device IDs such as IMEI numbers). If one can correlate an advertising ID with a unique hardware ID, it will defeat the purpose of the anonymizing service provided by the advertising ID. To prevent that, getInfo returns the location information for an advertising ID only if the caller does not have access to the device ID. That is, if the caller of getInfo possess the permission to access the device ID, then the caller should not be able to obtain any information that contains the location information; so this policy is non-monotonic: a caller with no permissions is allowed to access the sensitive information (location), whereas another caller with more permission is not allowed to access that information.
To simplify the discussion, let us assume that the permissions to access the IMEI number and the location information are denoted by p and q, respectively; and aid denotes a unique advertising ID generated and stored by the app for the purpose of anonymizing user tracking and loc denotes the location information. The function getInfo first tests whether the caller has access to the IMEI number. If it does, and if it has access to the location information, then only the location information is returned. If the caller has no access to the IMEI number, but can access the location information, then the combination of the advertising id and the location information aid++loc is returned. In all other cases, the empty string is returned. Let us consider a lattice with four elements ordered as:
The issue with the example in Listing 2 is due to the inability to determine statically whether ‘test(P)’ succeeds, leading to a conservative choice in the rule R1 of assigning the same types to both branches of the test. As a result,
We therefore designed a more precise type system for information flow under an access control model inspired by the Android framework. Our type system solves the problem of typing non-monotonic policies without resorting to downgrading or declassifying information. The technique we use is to keep information related to both branches of
The contributions of our work are four-fold.
We develop a lightweight type system in which security types are dependent on a permission-based access control mechanism, and prove its soundness with respect to a notion of non-interference (Section 2). A novel feature of the type system is the type merging constructor, used for typing the conditional branch in permission checking, which allows us to model non-monotonic information flow policies.
We identify a problem of explicit flow through function calls in the setting where permissions are not propagated during function calls. This problem arises as a byproduct of Android’s permission model, which is significantly different from that of JVM, and adopting a standard typing rule for function calls such as the one proposed for Java in [3] would lead to unsoundness. We call this problem the parameter laundering problem and we propose a typing rule for function calls that prevents it.
We show that the type inference is decidable for our type system, by reducing it to a constraint solving problem (Section 3).
We give a new way to represent our security types as reduced ordered binary decision diagrams, which generalizes boolean functions to functions mapping boolean formula to a multi-value set. We believe the representation will be efficient in practice.
This paper is an extension of [9]. It first extends the security type system with global variables and proves that it is still sound with respect to non-interference2
Due to space constraints, only the main proofs are presented here, and the other proofs, including the proofs for type inference and representation, can be found in the appendix.
The rest of the paper is organized as follows. Section 2 presents our security type system and its properties. Section 3 and Section 4 give the type inference for our security type system and the representation for our types, respectively. Section 5 presents related work. And Section 6 concludes the paper and discusses some future work.
In this section, we present the proposed information flow type system. Section 2.1 informally discusses a permission-based access control model, which is an abstraction of the permission mechanism used in Android. Section 2.2 and Section 2.3 give the operational semantics of a simple imperative language that includes permission checking constructs based on the abstract permission model. Section 2.4 and Section 2.5 describe the type system for our language and prove its soundness with respect to a notion of non-interference.
A model of permission-based access control
Instead of taking all the language features and the library dependencies of Android apps into account, we focus on the permission model used in inter-component communications within and across apps. Such permissions are used to regulate access to protected resources, such as device id, location information, contact information, etc.
In Android, an app specifies the permissions it needs at the installation time via a manifest file. In recent versions of Android (since Android 6.0, API level 23), some of these permissions need to be granted by users at runtime. But at no point a permission request is allowed if it is not already specified in the manifest. For now, we assume a permission enforcement mechanism that applies to Android versions prior to version 6.0,3
To be specific, runtime permission request requires the compatible version specified in the manifest file to be greater than or equal to API level 23, and running OS should be at least Android 6.0.
An Android app may provide services to other apps, or other components within the app itself. Such a service provider may impose permissions on other apps who want to access its services. Communication between apps is implemented through Binder IPC (inter-process communications) [14].
In our model, a program can be seen as a highly abstracted version of an app, and the intention is to show how one can reason about information flows in such a service provider when access control is imposed on the calling app. In the following we shall not model explicitly the IPC mechanism of Android, but will instead model it as a function call. Note that this abstraction is practical since it can be achieved by conventional data and control flow analyses, together with the modeling of Android IPC specific APIs. The feasibility has been demonstrated by frameworks like FlowDroid [2], Amandroid [53], IccTA [26], etc.4
We have also been implementing a permission-dependent information flow analysis tool on top of Amandroid. The basic idea is similar to the one mentioned in this paper, however the focus is improving the precision of information leakage detection rather than non-interference certification.
One significant issue that has to be taken into account is that the Android framework does not track IPC call chains between apps and permissions of an app are not propagated to the callee. That is, an app A calling another app B does not grant B the permissions assigned to A. This is different from the traditional type systems such as
As mentioned earlier, we do not model directly all the language features of an Android app, but use a much simplified language to focus on the permission mechanism part. The language is a variant of the language considered in [52], extended with functions and an operator for permission checks.
We model an app as a collection of functions (services), together with a statically assigned permission set. A system, denoted by
We assume that only one function is executed at a time, so we do not model concurrent executions of apps. We think that in the Android setting, considering sequential behavior only is not overly restrictive. This is because the communication between apps are (mostly) done via IPC. Shared states between apps, which is what contributes to the difficulty in concurrency handling, is mostly absent, apart from the very limited sharing of preferences. In such a setting, each invocation of a service can be treated independently as there is usually no synchronization needed between different invocations. Additionally, we assume functions in a system are not (mutually) recursive, so there is a finite chain of function calls from any given function. The absence of recursion is not a restriction, since our functions are supposed to model communications in Android, which are rarely recursive. We denote with
For simplicity, we consider only programs manipulating integers, so the expressions in our language all have the integer type. Boolean values are encoded as 0 (false) and any non-zero values (true). The grammar for expressions is given below:
A function declaration has the following syntax:
Given a system

Evaluation rules for expressions and commands, given a function definition table
Given a system
An evaluation environment is a finite mapping from variables to values (i.e., integers). We denote with
The operational semantics for expressions and commands is given in Fig. 1. The evaluation judgment for expressions has the form
The evaluation judgment for commands takes the form
The operational semantics of most commands are straightforward. We explain the semantics of the test primitive and the function call. Rules (E-CP-T) and (E-CP-F) capture the semantics of the
Security types
In information flow type systems such as [52], it is common to adopt a lattice structure to encode security levels. Security types in this setting are just security levels. In our case, we generalize the security types to account for the dependency of security levels on permissions. So we shall distinguish security levels, given by a lattice structure which encodes sensitivity levels of information, and security types, which are mappings from permissions to security levels. We assume the security levels are given by a lattice
A base security type (or base type) t is a mapping from
As we shall see, if a variable is typed by a base type, the sensitivity of its content may depend on the permissions of the app which writes to the variable. In contrast, in traditional information flow type systems, a variable annotated with a security level has a fixed sensitivity level regardless of the permissions of the app that writes to the variable.
Next, we show that the set of base types with the order
For
From now on, we shall drop the subscripts in
Accordingly, a security level l can be lifted to the base type
Given a security level l, we define
A function type has the form
In our type system, security types of expressions (commands, functions, resp.) may be altered depending on the execution context. That is, when an expression is used in a context where a permission check has been performed (either successfully or unsuccessfully), its type may be adjusted to take into account the presence or absence of the checked permission. Such an adjustment is called a promotion or a demotion. Given a permission p, the promotion and demotion of a base type t with respect to p are:
Given
We first define a couple of operations on security types and permissions that will be used later.
A function called by different callers may have different the permission contexts, so we define type projection to extract types according to the permission sets of callers.
Given
In order to type the permission check
Given a permission p and two types
A typing environment is a finite mapping from variables to base types. We use the notation
Given a typing environment Γ, its promotion and demotion along p are typing environments
There are three typing judgments in our type system as explained below. All these judgments are implicitly parameterized by a function type table, Expression typing: Command typing: Function typing: The typing judgment takes the form:
When proving the soundness of our type system, we need to make sure that the system of apps are well-typed in the following sense:
Let

Typing rules for expressions, commands and functions.
The typing rules are given in Fig. 2. Most of them are common to information flow type systems [3,46,52] except for T-CP and T-CALL. Note that the types for constants depend on the constants themselves. Taking loc and aid in Section 1 for example, their types are respectively
In T-CP , to type statement
In T-CALL , the callee function
Notice that the type of the argument

An example for parameter laundering issue
Let us consider the example in Listing 3. Let
If we were to adopt the modified T-CALL’ instead of T-CALL, then we can assign the following types to the above functions:

Typing derivations for functions A.f, B.g and C.getsecret.

A typing derivation for function M.main.
As shown in Fig. 3, B.g can be given type
With the correct typing rule for function calls, the function
Finally, the reader may check that if we fix the type of

Typing derivation for functions A.getInfo in Listing 2.
We first define an indistinguishability relation between evaluation environments. Such a definition typically assumes an observer who may observe values of variables at a certain security level. In the non-dependent setting, the security level of the observer is fixed, say at
Given two evaluation environments
Note that in Definition 2.10, η and
Recall that we assume no (mutual) recursions, so every function call chain in a well-typed system is finite; this is formalized via the rank function below. We will use this as a measure in our soundness proof (Lemma 2.9).
The next two lemmas relate projection, promotion/demotion and the indistinguishability relation.
If
If
The key to the soundness proof is the following two lemmas, which are the analogs to the simple security property and the confinement property in [52].
Suppose
The proof proceeds by induction on the derivation of We have We have We have the typing derivation
and the evaluations
By induction on we have
since
Suppose
By induction on the derivation of In this case x is non-global,
and the evaluation under
That is, In this case x is global,
and the evaluation under
That is, In this case c has the form
and we have that
where The remaining is to prove
From This follows straightforwardly from the induction hypothesis. We look at the case where the condition of the while loop evaluates to true, otherwise it is trivial. In this case the typing derivation is
and the evaluation derivation is
Applying the induction hypothesis (on typing derivation) and the inner induction hypothesis (on the evaluation derivation) we get This follows from the induction hypothesis and transitivity of the indistinguishability relation. This case follows from the induction hypothesis and the fact that we can choose fresh variables for local variables, and that the local variables are not visible outside the scope of letvar. We have:
There are two possible derivations for the evaluation. In one case, we have
Since The case where Straightforward by induction.
Before we define the notion of non-interference for a system, we need to define what it means for a command to be non-interferent.
A command c executed in app A is said to be non-interferent iff for all
The main technical lemma is that well-typed commands are non-interferent.
Suppose
If In this case,
where
So In this case,
where
So In this case
The evaluation derivation under
We consider here only the case where
The lemma then follows straightforwardly from the induction hypothesis.
According to Lemma 2.7, if
Applying the induction hypothesis to In this case we have In this case we have In this case, c has the form
where
where Moreover, since we consider only well-typed systems, the function
Let Claim 1: Proof: Let Claim 2: Proof: From Claim 1, we know that If Claim 3: Proof: We first note that if
We need to consider two cases, one where Assume that
since For the case where Let
Well-typed systems are non-interferent.
Follows from Lemma 2.9. □
The statement we are trying to prove, i.e.,
then
This section describes a decidable inference algorithm for the language in Section 2.2. Section 3.1 firstly rewrites the typing rules (Fig. 2) in the form of permission trace rules (Fig. 6), then reduces the type inference into a constraint solving problem; Section 3.2 provides procedures to solve the generated constraints.

Permission trace rules for expressions, commands and functions.
Permission tracing
In an IPC between different apps (components), there may be multiple permission checks in a calling context. Therefore, to infer a security type for an expression, a command or a function, we need to track the applications of promotions
Given a base type t and a permission trace Λ, the application of Λ to t, denoted by
We also extend the application of a permission trace Λ to a typing environment Γ (denoted by
The partial subtyping relation
The application of permission traces to types preserves the subtyping relation.
The following four lemmas discuss the impact of permission checking order on the same or different permissions.
Lemmas 3.2 and 3.3 state that the order of applications of promotions and demotions on different permissions does not affect the result, which enables us to solve the constraints in any order (see Section 3.2). Lemmas 3.4 and 3.5 indicate that only the first application takes effect if there exist several (consecutive) applications of promotions and demotions on the same permission p. Therefore, we can safely keep only the first application, by removing the other applications on the same permission.
Let
Lemma 3.6 states the application of trace Λ and the merging along p are orthogonal if
We keep the applications of the promotions and demotions symbolically (i.e., representing the applications as combinations of typing environments and permission traces), and move the subsumption rules (guarded by permission traces) for expressions and commands to where they are needed. This yields the syntax-directed typing rules given in Fig. 6, which we call the permission trace rules and mark with a subscript
Next, we show the trace rules are sound and complete with respect to the typing rules, that is, an expression (command, function, resp.) is typeable under the trace rules, if and only if it is typeable under the typing rules.
If
If
If
If
If
If
To infer types for functions in System
To describe the side conditions (i.e.,
A type substitution is a finite mapping from type variables to security types:
Given a constraint set C and a substitution θ, we say θ is a solution to C, denoted by
The constraint generation rules are presented in Fig. 7, where each rule is marked with a subscript

Constraint generation rules for expressions, commands and functions, given function type table
Next, we show the constraint rules are sound and complete with respect to permission trace rules, that is, the constraint set generated by the derivation of an expression (command, function, resp.) under the constraint rules is solvable, if and only if an expression (command, function, resp.) is typable under trace rules.
The following statements hold:
If
If
If
The following statements hold:
If
If
If

The example in Listing 2 in a calling context
Recall the function getInfo in Listing 2 and assume that getInfo is defined in app A (thus A.getInfo) and called by app B through the function fun (thus B.fun). The rephrased program is shown in Listing 4, where
If we split the types for commands into global ones and non-global ones, we could have simpler constraints.
Indeed, the constraint set for fun is
To start with, we present the interpretation and the difference on traces that are needed for constraint solving. A permission trace Λ is a property on permission sets and can be interpreted as a set of permission sets. Formally, the interpretation of Λ is
We now present an algorithm for solving the constraints generated by the rules in Fig. 7. For these constraints, both types appearing on the two sides of subtyping are guarded by the same permission trace. But during the process of solving these constraints, new constraints, whose two sides of subtyping are guarded by different traces, may be generated. Take the constraint
It is easy to transform a permission guarded constraint set C into a generalized constraint set
The constraint solving consists of three steps: 1) decompose types in constraints into ground types and type variables; 2) saturate the constraint set by the transitivity of the subtyping relation; 3) solve the final constraint set by merging the lower and upper bounds of same variables and unifying them to emit a solution.
Decomposition
The first step is to decompose the types into the simpler ones, namely, type variables and ground types, according to their structures. This decomposition is formalized as the decomposing rules, which are given in Fig. 8. Rules (CD-CUP), (CD-CAP), (CD-SVAR) and (
Rule (

Constraint solving rules, including decomposition (
After decomposition, constraints have one of the forms:
Considering a variable α, to ensure any lower bound (e.g.,
It should be
Assume that there is an order < on type variables and the smaller variable has a higher priority. If two variables
Let us consider the constraint set
Given two constraint sets
If
There are two kinds of variables in our setting: global variables and non-global ones, and the constraints for them are all guarded by permission traces. Since the types for non-global variables are dependent on the permission sets, we need to consider the satisfiability of (any subset of) the permission traces of a non-global variable α under any permission set when constructing a type for it. For that, we consider the evaluation of a type t guarded by a permission trace Λ along a permission set P. If
Let us consider a non-global variable α and assume that the constraints on it to be solved are
(with the convention
Since the types for global variables are invariant for all permission sets, that is, the type for a global variable
(with the convention
Moreover, due to the absence of loops in constraints and that the variables are in order, we can solve the constraints in reverse order on variables by unification. The unification algorithm unify is presented as follows. 
Consider the constraint set
Next, we show that our unification is sound and complete.
If
If
Let sol be the function for the constraint solving algorithm, that is,
If
If
To conclude, an expression (command, function, resp.) is typable, iff it is derivable under the constraint rules with a solvable constraint set by our algorithm. Therefore, our type inference system is sound and complete. Moreover, as the function call chains are finite, the constraint generation terminates with a finite constraint set, which can be solved by our algorithm in finite steps. Thus, our type inference system terminates.
The type inference system is sound, complete and decidable.
□
As given in Definition 2.1, our types are defined as mappings from the power set
There are a number of choices one can make as to how to encode the ‘default’ mappings; for example, one could use propositional logic, first order logic, or some ad hoc data structures. Two main considerations are the space complexity of the chosen representation (in the average case) and the space/time complexity of the type-related operations on the representation. Here we choose a representation of types that builds on the concept of reduced ordered binary decision diagrams (ROBDDs) [7,24]. ROBDD has been well-studied and has been applied successfully in areas such as model checking [11], so we think it is a good choice to build our representation on.
The principle idea behind choosing ROBDDs as the underlying data structure for representing types lies in the fact that any permission set can be encoded as bit vectors (after fixing an order on these permissions), namely, the presence or absence of permissions can be represented by a sequence of 1s and 0s. Taking the permission set
For simplicity, we shall refer to ROBDDs as just binary decision diagrams (BDDs). BDDs have some important and useful properties: (i) canonicity, that is to say for a fixed boolean variable ordering, each boolean function has a canonical (i.e. unique) representation (up to isomorphism) [7,24]; and (ii) operations on BDDs are efficient. With proper ordering, the time complexity of operations usually depends linearly on the number of boolean variables [7]. Note that a security type with a two-element lattice (‘High’ and ‘Low’) is essentially a boolean function. So the worst case space complexity of the BDD representation (or any known representation of boolean functions, for that matter) is exponential [7].
In this section, we give the definition of a binary decision diagram that fits our types, which is a modification10
Our modification generalizes the set of outputs from binary to an arbitrary finite set.
To start with, let us consider a general function
Given an arbitrary finite set S and a natural number n, a truth table of order n is a string
Given a finite set S, there are
Our modified definition of a binary decision diagram to represent the function
A binary decision diagram (BDD) is a rooted, directed graph with vertex set V containing two types of vertices. A nonterminal vertex (node) m has as attributes an argument index
Furthermore, a strict ordering restriction is imposed on nonterminal vertices. For any nonterminal vertex m, if
A BDD is reduced if no two nodes are allowed to have the same triple of values

Examples of binary decision diagram.
The truth table of function
We now show that our modified definition of a BDD still holds the property of canonicity. To that end, we modify a proof for canonicity as described by Knuth [24].
Given an arbitrary finite set S, any function of the form
We argue that every truth table τ can be represented as a power of a unique bead. Let τ be a truth table of order n that is not a bead. Then, by Definition 4.1, it is the square of a truth table
A truth table τ of order
Now we come to the crux of the argument: the nodes of a binary decision diagram for a function f (as described above) are in bijection with the beads of f. A function’s truth tables of order
in the BDD. Let the truth table corresponding to this node be
Considering the function

BDD formed by beads of
Let us return to our types. As mentioned above, permission sets can be encoded as bit vectors, where ‘0’ represents the absence of a permission and ‘1’ represents the presence of a permission. Moreover, operations on sets such as
A base security type t is a mapping from the set of all possible bit vectors representing permission sets to the lattice of security levels, i.e.
Using Theorem 4.1, the new definition of a type, and the definition of a binary decision diagram, we define the representation of types as follows:
Let t be a base type. The representation of t, denoted by
In order to evaluate a permission set for the representation of a given type, one can simply move down the diagram by choosing the branch that corresponds to the 0–1 value in the bit vector for that particular permission. If a node for a permission p does not exist, then one can simply skip that permission as the output security level does not depend on p’s value. Formally, the evaluation of the representation r along (a bit vector representing) a permission set P can be defined as
Let t be a type. Then for any permission set P, we have

Representation of type t.
To illustrate, let us consider the type t that maps the sets of permissions
An example in practice is the type
Such a multiple permission checking refers to

Representation of type for video calling.

Representation of type for location.
Unlike BDDs in [7], the outputs of our BDDs are a multi-value lattice. It is quite difficult to come up with a general framework describing operations on types as directly working on the Boolean functions (i.e., BDDs on two-value lattice). Therefore, we directly provide algorithms for operations on types mentioned in Section 2.4, via reasoning on the structure of the diagram. Here we focus on the operations only for our types, namely, projection, promotion, demotion, and merging. While the others, namely, the intersection
Firstly, with respect to the definition of type presentations, the properties for promotion, demotion, projection, and merging defined in Definition 2.5, 2.6, and 2.7 still hold.
Given a base type t, a permission p, and a permission set
Given a type t and a permission set
Given two types
According to Lemma 4.3, we argue that we can simply represent the projection as a binary decision diagram with a single vertex, being a sink node.
Given a base type t and a permission set

Algorithms for promotion and demotion.
The case for promotion and demotion is slightly more complicated. Recalling the Definition 2.5, the promotion of a base type t with respect to a permission p involves removing all branches where
The function
Figure 15 shows the promotion of the type t in Fig. 9 with respect to

Example for promotion (the dashed lines in red denote the modifications needed by the promotion).
The correctness of the algorithms for promotion and demotion is given in Lemma 4.6.
Given a base type t and a permission p, then
One can see that the number of operations required for promoting a type depends on the number of nodes in

Algorithm for merge.
Finally, let us describe how the merge operation could be implemented. Recall the definition: the merging of two types
The second case is slightly more complex. Let us assume p occurs in both
An example of the merge of the type t in Fig. 9 and another type s along

Example for merge.
Given two types
Note that unlike
There is a large body of work on language-based information flow security. We shall discuss only closely related work.
We have extensively discussed the work by Banerjee and Naumann [3] and highlighted the major differences between our work and theirs in Section 1.
Flow-sensitive and value-dependent information flow type systems provide a general treatment of security types that may depend on other program variables or execution contexts [23,27,29,30,32–38,40,44,49–51,54–56]. Hunt and Sands [23] proposed a flow-sensitive type system where order of execution is taken into account in the analysis, and demonstrated that the system is precise. But the system is simpler than ours. Mantel et. al. [34] introduced a rely-guarantee style reasoning for information flow security in which the same variable can be assigned different security levels depending on whether some assumption is guaranteed, which is similar to our notion of permission-dependent security types. Li and Zhang [27] proposed both flow-sensitive and path-sensitive information flow analysis with program transformation techniques and dependent types. Information flow type systems that may depend on execution contexts have been considered in work on program synthesis [44] and dynamic information flow control [54]. Our permission context can be seen as a special instance of execution context, however, our intended applications and settings are different from [44,54], and issues such as parameter laundering does not occur in their setting. Lourenço and Caires [33] provided a precise dependent type system where security labels can be indexed by data structures, which can be used to encode the dependency of security labels on other values in the system. It may be possible to encode our notion of security types as a dependent type in their setting, by treating permission sets explicitly as an additional parameter to a function or a service, and to specify security levels of the output of the function as a type dependent on that parameter. Currently it is not yet clear to us how one could give a general construction of the index types in their type system that would correspond to our security types, and how the merge operator would translate to their dependent type constructors, among other things. We leave the exact correspondence to the future work.
Recent research on information flow has also been conducted to deal with Android security issues ([8,10,18,19,22,31,39]). SCandroid [8,19] is a tool automating security certification of Android apps that focuses on typing communication between applications. Unlike our work, they do not consider implicit flows, and do not take into account access control in their type system. Ernst et al [18] proposed a verification model, SPARTA, for use in app stores to guarantee that apps are free of malicious information flows. Their approach requires the collaboration between software vendor and app store auditor and the additional modification of Android permission model to fit for their Information Flow Type-checker; soundness proof is also absent. Our work is done in the context of providing information flow security certificates for Android applications, following the Proof-Carrying-Code architecture by Necula and Lee [41] and does not require extra changes on existing Android application supply chain systems. Cassandra [31] performs the security analysis of apps on a server, which employs the proof-carrying code paradigm such that the server’s analysis result can be validated on the client. H. Gunadi [22] presented a type system for DEX bytecode and prove the soundness of the type system with respect to a notion of non-interference. Both [31] and [22] do not consider permission-dependent. ComDroid [10] detects communication vulnerabilities in Android applications from the perspectives of Intent senders and Intent recipients. Weir [39] is a practical DIFC system for Android, allowing data owner applications to set secrecy policies and control the export of their data to the network. And a number of security analysis tools, such as TaintDroid [16], DroidSafe [21] and TaintART [47], perform a data flow analysis on Android apps to track information-flow. But there is no soundness result for these analyses.
A few inference algorithms [28,43] have been proposed to infer dependent labels. Lifty [43] employed refinement types to encode information flow security, and proposed an inference algorithm based on the inference engine of liquid types [45]. While this is a neat solution for a two-level security lattice, the encoding does not apply to applications that require multiple security labels. Li and Zhang [28] proposed a general framework for designing and checking label inference algorithms for information flow analysis with dependent security labels. Based on the framework, novel inference algorithms that are both sound and complete are developed. The predicated constraint they proposed can express predicate on program state, and thus is a general version of ours. Limited to our setting, their one-shot algorithm is equivalent to ours. However, although efficient, the algorithms, except the one-shot one, may be over-conservative. For example, the early-accept algorithm and hybrid algorithm would infer
Conclusion and future work
We have provided a lightweight yet precise type system featuring Android permission model for enforcing secure information flow in an imperative language and proved its soundness with respect to non-interference. Compared to existing work, our type system can specify a broader range of security policies, including non-monotonic ones. We have also proposed a decidable type inference algorithm by reducing it to a constraint solving problem, as well as a new way to represent our security types as reduced ordered binary decision diagrams.
We next discuss briefly several directions for future work.
The immediate one is to extend our system to richer programming languages, including object-oriented features (like [48]), exceptions (like [6]), etc. Another extension is to incorporate the efficient type representation in the inference algorithm. A proposed solution is to encode our permission guarded constraints as a special BDD, where the outputs are the constraints without guards (i.e., constraints on the output labels).
We also plan to apply our type system to real Android applications to enforce permission-dependent information flow policies. A main challenge is to facilitate type inference so that a programmer does not need to type every variable and instead focuses only on policy specifications of a service. To enable this, we need to be able to extract all permissions relevant to an app and to identify all commands relevant to permission checking in an app. The former is straightforward since the permissions that can be granted to an app is statically specified in the app’s manifest file. For the latter, the permission checking code segments (typically library function calls) can be located with pre-processed static analyses (e.g., [2,53]).
Another interesting direction is in modeling runtime permission request. From Android 6.0 and above, several permissions are classified as dangerous permissions and granting of these permissions is subject to users’ approval at runtime. This makes enforcing non-monotonic policies impossible in some cases, e.g., when a policy specifies the absence of a dangerous permission in releasing sensitive information. However, an app can only request for a permission it has explicitly declared in the manifest file, so to this extent, we can statically determine whether a permission request is definitely not going to be granted (as it is absent from the manifest), and whether it can potentially be granted. And fortunately (but unfortunately from a security perspective) the typical scenarios are that users grant all the requested permissions during runtime when requested (in order to gain a better user experience with the app). Therefore one can assume optimistically that all permissions in the manifest are finally granted. In the future, we plan to resolve this issue with weaker assumptions. One feasible approach is to model dangerous permissions in a typing environment separately and allow policies to be non-monotonic on non-dangerous permissions only.
Lastly, our eventual goal is to translate source code typing into Dalvik bytecode typing, following a similar approach done by Gilles Barthe et al. [4–6] from Java source to JVM bytecode. The key idea that we describe in the paper, i.e., precise characterizations of security of IPC channels that depends on permission checks, can still be applied to richer type systems such as those used in the Cassandra project [31] or Gunadi’s type system [22]. We envision our implementation can piggyback on, say, Cassandra system to improve the coverage of typable applications.
Footnotes
Acknowledgments
This work has been partially supported by the National Natural Science Foundation of China under Grants No. 61972260, 61772347, 61836005, Guangdong Basic and Applied Basic Research Foundation under Grant No. 2019A1515011577, Guangdong Science and Technology Department under Grant No. 2018B010107004, and the National Satellite of Excellence in Trustworthy Software Systems (Award No. NRF2018NCR-NSOE003) funded by NRF Singapore under National Cyber-security R&D (NCR) programme.
Proofs for soundness
Proofs for type inference
To prove the completeness, we need to prove some auxiliary lemmas.
Before proving our unification is sound and complete, we show the
Consider a type variable α with
If
The proof of (a): Let us consider any lower bound The proof of (b): Let Otherwise, α is global. The proof is similar to the case above and based on the fact that the types for global variables are invariance for all permission sets. □ By induction on Trivial. In this case we have We prove that the statement When
