The Access Permissions (AP) attribute controls whether a location can be read and written, and what privilege is necessary. This table shows the AP bit settings:
|AP||Unprivileged (EL0)||Privileged (EL1/2/3)|
If an access breaks the specified permissions, for example a write to a read-only region, an exception (labelled as a permission fault) is generated.
Privileged accesses to unprivileged data
The standard permission model is that a more privileged entity can access anything belonging to a less privileged entity. Or to put it another way, an Operating System (OS) can see all the resources allocated to an application. A hypervisor can see all the resources allocated to a virtual machine. This is because executing at a higher exception level means that the level of privilege is also higher.
However, this is not always desirable. Malicious applications might use to try to trick an OS into accessing data on the application’s behalf, which the application should not be able to see. This requires the OS to check pointers in systems calls.
The Arm architecture provides several controls to make this simpler. First, there is the
PSTATE.PAN (Privileged Access Never) bit. When this bit is set, loads and stores from EL1 (or EL2 when
E2H==1) to unprivileged regions will generate an exception (Permission Fault), as this diagram illustrates:
Note: PAN was added in Armv8.1-A.
PAN allows unintended accesses to unprivileged data to be trapped. That is, when an instruction accesses a privileged region and the OS thinks the instruction should be accessing an unprivileged region.
There are cases where the OS does need to access unprivileged regions. For example, to write to a buffer owned by an application. To support this, the instruction set provides the
STTR are unprivileged loads and stores. They are checked against EL0 permission checking even when executed by the OS at EL1 or EL2. As these are explicitly unprivileged accesses, they are not blocked by
PAN, as this diagram shows:
This allows the OS to distinguish between accesses that are intended to access privileged data and those which are expected to access unprivileged data. This also allows the hardware to use that information to check the accesses.
Note: Some historical trivia. The T in LDTR stands for translation. This is because the first Arm processors to support virtual to physical translation only did so for User mode applications, not for the OS. For the OS to access application data it needed a special load, a load with translation. Today of course, all software sees virtual addresses, but the name has remained.
As well as access permissions, there are also execution permissions. These attributes let you specify that instructions cannot be fetched from the address:
- UXN. User (EL0) Execute Never (Not used at EL3, or EL2 when
- PXN. Privileged Execute Never (Called XN at EL3, and EL2 when
These are execute never bits. This means that setting the bit makes the location not executable.
There are separate Privileged and Unprivileged bits, as application code needs to be executable in user space (EL0) but should never be executed with kernel permissions (EL1/EL2), as this diagram shows:
The architecture also provides controls bits in the System Control Register (
SCTLR_ELx) to make all write-able addresses non-executable.
A location with EL0 write permissions is never executable at EL1.
Note: Remember, Arm recommends that Device regions are always marked as Execute Never (