Are Stage 1 & 2 walk repeat loops bounded?

I am going through the Arm Pseudocode for memory translation, and I stumbled upon something that I cannot really explain.

In the functions S2Translate and S2Walk (similar for stage 1) for Aarch64, there are loops that correspond to "repeat until descriptor does not change anymore" (the access flags, dirty bits etc might be changing upon translation, and these changes are written during translation). I was now wondering whether this repeat loop is bounded by some condition. At first sight, I assumed it would be repeated at most once, but I am too ignorant in the realms of Arm to conclude that this is the case.

Intuitively, an adversary can keep changing the descriptor in memory (resetting the accessflag and whatnot), resulting in an infinite loop. Is this actually possible, or are there memory consistency rules or atomicity constraints that I overlooked? If yes (which I assume), can you please point me to what I should read up on? It seems odd that Arm would formulate their specification like this, but also cannot see something preventing a loop (assuming "perfect conditions").

Thank you

  • Let's take the S2Walk as one example. 

    • The outside repeat will end in limited loops.   
    • The S2NextWalkStateLast() or S2NextWalkStateTable() will end in limited loops or return with fault descriptor.

    Please be aware that the MMU translation table is 64-bit VA, which can have 3-level or 4-level MMU tables with different MMU translation table granule size.

    <quote>

    repeat

    ...

    case desctype of
    when DescriptorType_Table
    walkstate = AArch64.S2NextWalkStateTable(walkstate, walkparams, descriptor);

    // Detect Address Size Fault by table descriptor
    if AArch64.OAOutOfRange(walkstate, walkparams.ps, walkparams.tgx, ipa_64) then
    fault.statuscode = Fault_AddressSize;
    return (fault, AddressDescriptor UNKNOWN, TTWState UNKNOWN, bits(64) UNKNOWN);

    when DescriptorType_Page, DescriptorType_Block
    walkstate = AArch64.S2NextWalkStateLast(walkstate, ss, walkparams, ipa,
    descriptor);

    when DescriptorType_Invalid
    fault.statuscode = Fault_Translation;
    return (fault, AddressDescriptor UNKNOWN, TTWState UNKNOWN, bits(64) UNKNOWN);

    otherwise
    Unreachable();

    until desctype IN {DescriptorType_Page, DescriptorType_Block};

    </quote>

  • Thank you for the response. I was just wondering about the inner loop though. What prevents this from looping indefinitely? Is there a consistency rule that ensures boundedness?

  • I am not sure I got your concern points. Why you think the inner loop will have the infinite looping?

    VA->PA address translation is 64-bit VA in AArch64 state.  The parameters of S2Walk()  are finite width, but not infinite?

    • (FaultRecord, AddressDescriptor, TTWState, bits(64)) AArch64.S2Walk( FaultRecord fault_in, AddressDescriptor ipa, S2TTWParams walkparams, SecurityState ss, AccType acctype, boolean iswrite, boolean s1aarch64)


    Please refer to Arm.ARM Chapter D. Arm AArch64 memory model.

    • If an implementation is executing in AArch64 state, then it is using the Armv8 Virtual Memory System Architecture (VMSA), specifically the 64-bit VMSAv8-64.
  • The inner loop is a `repeat ... until new_descriptor == descriptor`. This loops. If a malicious actor tries to overwrite the descriptor, and `MemSwapTableDesc` always needs to swap 2 non-identical descriptors, what will happen? Is it an infinite loop?

    I am aware what code is called and that everything is finite, it is just that one loop and condition that seems strange to me

  • AArch64.MemSwapTableDesc is an atomic operation.   I cannot image one scenario that how the offending descriptor is repeatedly looped here.

    If a malicious actor overwrites a offending descriptor, it is possible. However, the memory attribute check or permission check will figure it out or raise the fault as the bad descriptor.   Even though the offending descriptor passes all the checks and goes  into S1Translate(), S2Translate() for EL0/EL1,   I think it is still finite loops as it obeys the AArch64 VMSA rules.