Defending against SPOILER

  1. 3 weeks ago

    Eric W

    Mar 7 Pre-Release Testers, Xojo Pro

    I refer to the horrific SPOILER vulnerability said to be in all Intel core processors . According to Intel:

    ...we expect that software can be protected against such issues by employing side channel safe development practices. This includes avoiding control flows that are dependent on the data of interest.

    Any idea what this means (apart from perhaps "we can't help you") in real life?

    In my posts I have in good faith demonstrated that SPOILER is a problem affecting our applications not only OSes, and that program logic can be separated from data to some extent, to possibly avoid potential speculative execution vulnerabilities, as shown in the pseudo code. However when it comes to implementation in practice, problems arise with nested control structures. Even if threads can be controlled to execute in the order they are created, the need to re-implement variables into dynamic globals means in effect, it will involve almost total re-writes of apps. I would dare say the same may be true of programming languages too. I don't see how Intel's exceedingly brief advice concerning this can possibly be followed in real life.

    So the easiest to implement potential 'antidote' is the thousand additions quirk, which presumably flushes some CPU cache with dummy values enough to spoil SPOILER. I can only speculate that this is so because neither Intel nor the researchers have disclosed how the additions-thingy might work. We simply have not been given enough information by those in the know to secure sensitive data on this basis. So if we can't change our code, what can we done? Here are some possibilities:

    • Don't run apps on Intel - not practical because almost the entire cloud and most notebook and desktop computers have "Intel Inside"
    • Separate the file system and cores into security zones, so that untrusted apps run on untrusted cores - doable on Linux at least, but not for the feint hearted. There might also be significant losses in load-balancing efficiencies. And if one core can be used to gain root access, it can compromise them all.
    • Do not run a trusted app and an untrusted app on the same "Intel Inside" computer - probably where we are at. Cloud users must simply trust that the cloud provider's hypervisor separates tennants. This is reasonable since cloud providers owe a duty of care and this is their core business activity.

    None of these possibilities involve application programmers like us, nor indeed development tool programmers. Therefore the way I see it now, Louis is essentially right.

  2. Louis D

    Mar 7 Pre-Release Testers, Xojo Pro Montreal, QC, Canada

    at our level, not much. I would think that Intel, Apple, Microsoft and Linux kernel maintainers must be talking. (maybe not all at the same table...)

  3. Dave S

    Mar 7 San Diego, California USA

    an issue? perhaps..... horrific... don't think so.. or almost every computer in the world would be useless about now.
    I think this is an attempt to sensaltionitize the know Intel anomaly that was reported (and I believe fixed) over a year ago. I may be wrong, but that what it looks like

  4. Louis D

    Mar 7 Pre-Release Testers, Xojo Pro Montreal, QC, Canada

    It is said to be different than Spectre, which was fixed. Incidently, MS is distributing a patch that alleviates the performance hit caused by the initial fix to Spectre.

    But I am with you. "Horrific" is perhaps a slight overstatement.

  5. 2 weeks ago

    Eric W

    Mar 8 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    As it's directly related to my work I have to deal with this, with or without adjectives...

    However JavaScript on a Web page allowing the reading of passwords etc from (host) system memory that " works from within virtual machines and sandboxed environments" , which could not be corrected by microcode patch, seems horrific to me, and indeed, the researchers (who want the kudos for their discovery) make it sound that way too:

    SPOILER is particularly helpful for attacks in sand-boxed low-privilege environments such as JavaScript, where previous methods require a time-consuming brute forcing of the memory addresses.
    ...
    Microarchitectural attacks from JavaScript have a high impact as drive-by attacks in the browser can be accomplished with-out any privilege or physical proximity.

    Speculative execution can be applied without our knowing to parts of our apps so as to leverage spare CPU cycles that exist between memory reads/writes, thus speeding up our Apps execution by 5-30%. This is done efficiently though a special memory model which reportedly can be compromised by attackers to (eventually) obtain all of a system's secret data (i.e. root access).

    Why I'm saying this SPOILER will probably become OUR problem to deal with too, is because we are the ones who write the high-level branch subject to involuntary speculative execution even though most of us (including me) are not or only barely aware of the lower level programming this involves on Intel's chips. For example, in 2015 Paul Lefebvre in his Xojo blog post "Tips: Dealing with the Problem of Passwords" summarised the program flow, which notably is coded by us not only the OS vendors:

    To validate that the password is correct, when the user logs in you calculate the hash of the password they entered and then check to see if it matches the hash of the password you have stored. If they are the same then you know the password is correct even though you do not know the actual password.
    ...
    ...use a “salt” along with the password to create the hash. The salt is an extra value that you add to the password to generate hashes that make rainbow tables largely useless. You can use the same salt value for all the passwords or you can use something more specific for each password.

    Moreover in 2012 Chris Musty responded to the RealSoftware blog "Dealing with the Problem of Passwords" saying:

    Every time a user logs in, a random salt should be generated and appended (prefixed, jumbled whatever) to the password before being hashed. Storing the hash AND the salt separately in the database (or even a readable text file) makes it easy to validate the user but extremely hard to crack.

    Admittedly none of his matters if an attacker eventually gets root access to a system but that is not guaranteed. So my argument is if we wrote the code handling passwords in user space most likely we will be the ones needed to modify it; if we write code like this pseudo:

    strPasswordHash = SHA512(strPassword + strSalt)
    If strStoredHash = strPassWordHash then
       // Let user continue with program
    else
       //Tell user to go away
    end if

    then maybe we are the ones (i.e. not just Microsoft, Linus Torvalds et al or Apple) who are implementing the " control flows that are dependent on the data of interest" that Intel is talking about. Maybe this problem needs to be solved in XojoLand (i.e. in non-privileged User Space controlled by us and the language-compiler) and not in the kernel, since it is the structure of our code that triggers the (apparently automatic) speculative execution in hardware seemingly largely beyond kernel developer' control...

    So where are we then? The aforementioned blog in 2012 rightly considered database security thus:

    The best way to not allow passwords to be compromised is to not store the password at all. Unfortunately, I've seen far too many apps that have databases with a "password" column that contains the actual password!

    I think due to the vulnerabilities of speculative execution - which may well be good for things like speeding up loops but not-so-good for verifying authentications - the time has come to extend that time-honoured principle to salted hashes that are presently stored in memory. May I propose avoiding using salted hashes in branch execution such as If/Then and loops, by instead generating Booleans from them, then feeding a Boolean to the branch for comparison. Thus the pseudo code becomes something like:

    Function BitwiseStringComparison ( memPasswordHash,  memStoredHash) as Boolean
    if memPasswordHash.Size = memStoredHash.Size Then    
    Do  
     intComparator = memPasswordHash.IntegerValue(intOffset) XOR memStoredHash.IntegerValue(intOffset)
       offset = offset + 1
       if intComparator <> 0 or or intOffset = memPasswordHash.Size then 
             If intComparator = 0 Then
                   Return TRUE
             Else
                    Return FALSE
             End Ff
      End if
    Loop
    Else
       Return FALSE
    End if
    End Function
    
    memPasswordHash = SHA512(memPassword + memSalt)
    boolPaswordIsCorect = BitwiseStringComparison ( memPasswordHash,  memStoredHash)
    
    //Speculative execution block starts here?
    If boolPasswordIsCorrect = TRUE then
       // Let user continue with program
    else
       //Tell user to go away
    end if

    The branching in this pseudo code is performed on a Boolean or integers, and not such "control flows that are dependent on the data of interest" as Intel would put it. I've no way to test if this works of course, but having read the researchers' paper I see no reason to suppose why an assignment made before a branch should form part of speculative execution memory. I base this on my reading of the researchers' paper which states:

    ...if the stores are committed before the victim’s speculative load,there will be no dependency resolution hazard.

    This comment seems consistent with Intel's reported response that there exist "side channel safe development practices" (of which there 's precious little information out there I could find - sounds like PR to me).

    So as I understand it from an application programmer's standpoint, speculative execution may cause my pseudo code's TRUE branch to run in potentially vulnerable privileged memory before the BitwiseStringComparison return is fetched. However the call to memory to check the function's return will now be to a boolean value not the stored hash value. But if this be so, I'm still left wondering how far down the branch will other variables containing sensitive information be loaded into potentially leaky speculative execution? If Speculative Execution starts with 'If' does it end with 'End', or sometime sooner?

  6. Eric W

    Mar 8 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    I'm counting on the Do..Loop not bringing the salted hash into speculative execution because it's outside the branch - i.e. it's not a conditional Do Until or For...Next or Select Case etc. The branching in this case that might be subject to speculative execution are If Then control structures within the Do...Loop.

    Probably better to do it inline with 64 lines of 8 bit integers (byte) for 512 bits of hash :

    intComparator = memPasswordHash.int8(0) XOR memStoredHash.int8(0)
    intComparator = intComparator + memPasswordHash.Int8(1) XOR memStoredHash.Int8(1)
    ...
    intComparator = intComparator + memPasswordHash.Int8(63) XOR memStoredHash.Int8(63)
    if intComparator = 0 then
          Return TRUE
    ...
  7. Eric W

    Mar 8 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    Continuing the idea of doing comparisons after assignment createing a proxy value to (hopefully) insulate sensitive data from speculative execution, < and > operators can be implemented in an assignment statement by subtracting one value from another and then checking the sign bit (see Norman Palardy's explanation of this ). If it's a 1 then the first value is less than the second, if its a 0 the first value is equal to or greater than the second. This means the value being checked for branching, and thus potentially subject to speculative execution memory mapping, is not the original values but the 0 or 1 sign bit of the assignment, as can be illustrated by the following pseudo code:

    Function IsEqual ( intSensitiveValueX,  intSensitiveValueY) as Boolean
       intEqualsTest = intSensitiveValue1 - intSensitiveValue2  
       //Speculative execution block starts here?
      if intEqualsTest = 0 then
             Return TRUE
      else 
             Return FALSE
       end if
    End Function
    
    
    Function IsGreaterThan ( intSensitiveValueX,  intSensitiveValueY) as Boolean
       intEqualsTest = intSensitiveValue1 - intSensitiveValue2  
       intComparator as int64 = (intSensitiveValue1 - intSensitiveValue2)  And &b1000000000000000000000000000000000000000000000000000000000000000
       //Speculative execution block starts here?
      if intEqualsTest = 0 then
             Return FALSE
      else 
         if intComparator = 0 then
              Return TRUE
          else
             Return FALSE
          end if
       end if
    End Function
    
    Function IsLessThan ( intSensitiveValueX,  intSensitiveValueY) as Boolean
       intComparator as int64 = (intSensitiveValue1 - intSensitiveValue2)  And &b1000000000000000000000000000000000000000000000000000000000000000
       //Speculative execution block starts here?
       if intComparator = 0 then
              Return FALSE
       else
             Return TRUE
       end if
    End Function
    
    boolAnswer = IsGreaterThan ( intSensistiveValue1, IntSensitiveVaue2)
    
    //Speculative execution block starts here?
    If boolAnswer = TRUE then
       // continue with program flow A
    else
       //continue with program flow B
    end if

    Of course I have no way of verifying that the sensitive data variables are not loaded into potentially vulnerable speculative execution memory but per the above discussion I can see no reason why they would be. Hopefully this is the type of thing Intel means by saying "...we expect that software can be protected against such issues by ... avoiding control flows that are dependent on the data of interest."

  8. Eric W

    Mar 9 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    Noticed the function in my first pseudo code attempt was nested in an If/Then which could have been subject to a SPOILER attack. Instead now this:

    Function BitwiseStringComparison ( memPasswordHash,  memStoredHash) as Boolean
          intHashSizeCheck = memPasswordHash.Size - memStoredHash.Size
        
          intComparator = memPasswordHash.int8(0) XOR memStoredHash.int8(0)
          intComparator = intComparator + memPasswordHash.Int8(1) XOR memStoredHash.Int8(1)
           ... //lines 2 to 62 go here
          intComparator = intComparator + memPasswordHash.Int8(63) XOR memStoredHash.Int8(63)
    
        //Speculative execution block starts here
         if intComparator = 0 AND intHashSizeCheck = 0 then
                   Return TRUE
             Else
                    Return FALSE
             End Ff
    End Function
  9. Eric W

    Mar 9 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    Noticed a logic error in the pseudo code when testing intHashSizeCheck for zero difference, which should be NOTed. The line should read:

    if intComparator = 0 AND NOT intHashSizeCheck = 0 then

    It should be noted that the efficacy of the proposed workaround relies on feeding the branch a different variable with different data for evaluation to try to avoid the original data's inclusion in speculative execution that might be subject to SPOILER issues as referred to by Intel (i.e not speculative execution in general.)

    All this does not make for intuitive and easy-to-maintain code so hopefully for apps run in relatively uncontrolled environments where other code running on a core can't necessarily be trusted, "SPOILER hardened" will be a compile option or function-level directive, or a code modification macro in the IDE to be run just before a build. I think the best place would be in the language compiler since there may be branching in the language or framework that the low-code developer cannot know or may not notice. For example, is a timeout in a socket implemented via conditional branch testing the time (which might not be SPOILER-hardened) or is the socket killed by an external timing code (which might be better) ? It is also better done by the language since only code when running o n Intel chips might need the modifications.

  10. Eric W

    Mar 9 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    The way I read them, in "SPOILER: Speculative Load Hazards Boost Rowhammer and Cache Attacks" we are effectively told there is no fix but Intel states workarounds "include" avoiding control flows that are dependent on the data of interest. So what else might be included? The researchers claim to have read 10MB of secret speculative memory using nothing but JavaScript - that's as I understand it a copy of user data (belonging to any user(s)) held in trust by the kernel - so quite a lot it seems. Presumably this 10MB could include things beyond two hashes being evaluated in an If/Then statement that is speculatively executed whether we like it or not. I suppose the 10MB could for example, include hashes created in the branch load speculatively executed when a user changes their password, or other sensitive data. So the issue arises how to keep such information usually present or manipulated within a branch outside of SPOILER's speculative reach.

    We could put statements touching sensitive data into a separate thread: If the code within Select Case or If Then clauses are not statements with sensitive data to be executed but assignments to variables of addresses to methods containing the statements, such address(es) as found applicable could be passed subsequent to the execution of the branch (i.e. after it's expiry), to a (new) thread outside the scope of the present function. Xojo may have an advantage over other languages here with its cooperative threading, in which context switching usually isn't that expensive. And I would be very surprised if code executing on a different cooperative thread would be drawn into the speculative execution started by an expired branch. But if I'm wrong about that, the researchers say the "SPOILEReffect" can itself be spoiled after only 1000 Adds are executed between a Store and a Load. So if we pass variables to the thread by Value, then do 1000 ads, the thread should be SPOILER-free apparently (at least we might say on the i7 processor they tested). I imagine such use of threads might also harden against speculative execution triggered other than by code branching, if any.

    However I cannot think of a way to harden For Each control structures, which intrinsically rely on "control flows that are dependent on the data of interest" as it seems Intel warns about, form the low-code developer's point of view. That is something I think only Xojo Inc. can deal with. For the reasons already given, I believe these changes are best implemented via control structure clause-level directives in the language itself. Like most low-code developers, I don't have the skills or time to confirm that these workarounds work. Given the potential for mischief, I think Intel or the researchers should have been more helpful about what needs to be done.

  11. Eric W

    Mar 10 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    Here is an example of the proposed threading solution: The pseudo code below uses a thread running the appropriate method address (that was further 'insulated' by its value being passed after being converted to a string and back again) to write or update sensitive data depending on its evaluation:

    Module1
         Sub Update
              //Update some sensitive data
           End sub
           Sub WriteToFile
               //Write sensitive data to file
          End Sub
    
    Function IsEqual ( intSensitiveValueX,  intSensitiveValueY) as String
       intEqualsTest = intSensitiveValue1 - intSensitiveValue2  
       //Speculative execution block starts here?
      if intEqualsTest = 0 Then     //Is equal
             memWriteToFileAddress(0).ptr = AddressOf WriteToFile
             Return memWriteToFileAddress.StringValue(8)
      Else       //is not equal
              memUpadateAddress(0).ptr = AddressOf Update
             Return memUpdateAddress.StringValue(8)
       End If
    End Function
    
    Sub Start
       memDataMethodIndicator = IsEqual( intSensistiveValue1, IntSensitiveVaue2)
      
        intAddition = intAddition + 1
        // copy & paste more such additions if necessary to  mess up SPOILER attacks
    
       SensitiveDataThread = New Thread
       AddHandler SensitiveDataThread.Run, AddressOf memDataMethodIndicator(0).Ptr
       SensitiveDataThread.Run
    End Sub

    Of course it may be some steps aren't needed. I guess we shall have to wait and see.

  12. Eric W

    Mar 10 Pre-Release Testers, Xojo Pro Answer
    Edited 2 weeks ago

    In my posts I have in good faith demonstrated that SPOILER is a problem affecting our applications not only OSes, and that program logic can be separated from data to some extent, to possibly avoid potential speculative execution vulnerabilities, as shown in the pseudo code. However when it comes to implementation in practice, problems arise with nested control structures. Even if threads can be controlled to execute in the order they are created, the need to re-implement variables into dynamic globals means in effect, it will involve almost total re-writes of apps. I would dare say the same may be true of programming languages too. I don't see how Intel's exceedingly brief advice concerning this can possibly be followed in real life.

    So the easiest to implement potential 'antidote' is the thousand additions quirk, which presumably flushes some CPU cache with dummy values enough to spoil SPOILER. I can only speculate that this is so because neither Intel nor the researchers have disclosed how the additions-thingy might work. We simply have not been given enough information by those in the know to secure sensitive data on this basis. So if we can't change our code, what can we done? Here are some possibilities:

    • Don't run apps on Intel - not practical because almost the entire cloud and most notebook and desktop computers have "Intel Inside"
    • Separate the file system and cores into security zones, so that untrusted apps run on untrusted cores - doable on Linux at least, but not for the feint hearted. There might also be significant losses in load-balancing efficiencies. And if one core can be used to gain root access, it can compromise them all.
    • Do not run a trusted app and an untrusted app on the same "Intel Inside" computer - probably where we are at. Cloud users must simply trust that the cloud provider's hypervisor separates tennants. This is reasonable since cloud providers owe a duty of care and this is their core business activity.

    None of these possibilities involve application programmers like us, nor indeed development tool programmers. Therefore the way I see it now, Louis is essentially right.

  13. Eric W

    Mar 11 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    For completeness, if my speculation that the 1000 additions mentioned in "SPOILER: Speculative Load Hazards Boost Rowhammer and Cache Attacks" Section 7 are sufficient to spoil SPOILER is true, and no threads are necessary, then the following pseudo code illustrates how this might be done according to my previous posts:

    Function IsEqual ( intSensitiveValueX,  intSensitiveValueY) as Boolean
       intEqualsTest = intSensitiveValue1 - intSensitiveValue2  
       //Speculative execution block starts here?
      if intEqualsTest = 0 Then     //Is equal
             Return True
      Else  
              Return False
       End If
    End Function
    
    boolSensitiveDataIsEqual = IsEqual( memPasswordHash,  memStoredHash)
    
    //Speculative execution block starts here?
    If boolSensitiveDataIsEqual = TRUE then
        do until intCounter = 1000     //Flush Cache against SPOILER
                intValue(intCounter) = intValue(intCounter) + 1
          Loop
          // Now we can so something with sensitive data
         ...
    else
          do until intCounter = 1000     //Flush Cache against SPOILER
             intValue(intCounter) = intValue(intCounter) + 1
       Loop
       // Now we can so something else with sensitive data
      ...
    end if

    The code converts sensitive data in the branche's test to another form for branch evaluation and adds some instructions and data to the branch to flush the cache/pipeline/whatever out to hopefully foil SPOILER. Other test functions for < and > are in previous posts. Maybe a random number of adds >1000 would be a good idea. It may be the adds should be inline and not in a loop.

    What evidence is there that flushing the cache/pipeline/whatever will do the trick? The following is taken from the research paper:

    Then, we test add and leal, which use the Arithmetic Logic Unit (ALU) and the Address Generation Unit (AGU), respectively. Figure 12 shows that only 1000 adds can be executed between the stores and load before the SPOILER effect is lost.

    What is the problem with this quote? The flush must happen within each branch that is speculatively executed, but this does not cover the branch's test itself, which itself may have some data. Does Intel specifically warn about this? Yes.

    Intel received notice of this research, and we expect that software can be protected against such issues by employing side channel safe development practices. This includes avoiding control flows that are dependent on the data of interest.

    Has this particular warning been issued before about other things? Not that I can tell by googling Intel.com.

    Why don't they tell us exactly what to do if they have such expectations? Why the cryptic crossword? I don't know, maybe legal reasons. I have no idea.

    Of course there's no way for me to confirm the solution by testing, for being an application programmer this really isn't my bag despite being stuck with it. I hope someone else picks this up who understands all the intricacies.

  14. Tim J

    Mar 11 Pre-Release Testers, Xojo Pro Dehydrating in AZ

    I'm not sure how the Xojo compiler compiles a simple 1 + 1, but I suspect that Intel's offering is discussing the asm "add" instruction specifically:

    add — Integer Addition
    The add instruction adds together its two operands, storing the result in its first operand. Note, whereas both operands may be registers, at most one operand may be a memory location.
    Syntax
    add <reg>,<reg>
    add <reg>,<mem>
    add <mem>,<reg>
    add <reg>,<con>
    add <mem>,<con>

    Examples
    add eax, 10 — EAX ← EAX + 10
    add BYTE PTR [var], 10

    — add 10 to the single byte stored at memory address var

  15. Eric W

    Mar 12 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    Thanks for the explanation Tim. I have never done assembler and unfortunately Xojo's Biwise which I am quite familiar with has no Add method to operate registers (nor does MBS Math ). The good news is simple loops would probably be unfolded to individual statements by the compiler so they shouldn't (but how can we be sure?) trigger another (possibly out of order and thus ineffective for our purposes) speculative execution by branching due to testing for the loop's exit condition. (Which is why I preferred 1000 lines Adds instead.) The bad news is the optimiser could fold what would otherwise be Adds into Stores (and there is no Pragma to stop this) which may undermine the desired diffusive effect on the alleged SPOILER attack's timing.

    So when adding it all up, we simply have not been given enough information by Intel to be certain any of this will make a difference anyway, which is very annoying. They have known about it for months!

or Sign Up to reply!