• Josh Poimboeuf's avatar
    objtool: Support stack layout changes in alternatives · c9c324dc
    Josh Poimboeuf authored
    The ORC unwinder showed a warning [1] which revealed the stack layout
    didn't match what was expected.  The problem was that paravirt patching
    had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP".  That changed
    the stack layout between the PUSHF and the POP, so unwinding from an
    interrupt which occurred between those two instructions would fail.
    
    Part of the agreed upon solution was to rework the custom paravirt
    patching code to use alternatives instead, since objtool already knows
    how to read alternatives (and converging runtime patching infrastructure
    is always a good thing anyway).  But the main problem still remains,
    which is that runtime patching can change the stack layout.
    
    Making stack layout changes in alternatives was disallowed with commit
    7117f16b ("objtool: Fix ORC vs alternatives"), but now that paravirt
    is going to be doing it, it needs to be supported.
    
    One way to do so would be to modify the ORC table when the code gets
    patched.  But ORC is simple -- a good thing! -- and it's best to leave
    it alone.
    
    Instead, support stack layout changes by "flattening" all possible stack
    states (CFI) from parallel alternative code streams into a single set of
    linear states.  The only necessary limitation is that CFI conflicts are
    disallowed at all possible instruction boundaries.
    
    For example, this scenario is allowed:
    
              Alt1                    Alt2                    Alt3
    
       0x00   CALL *pv_ops.save_fl    CALL xen_save_fl        PUSHF
       0x01                                                   POP %RAX
       0x02                                                   NOP
       ...
       0x05                           NOP
       ...
       0x07   <insn>
    
    The unwind information for offset-0x00 is identical for all 3
    alternatives.  Similarly offset-0x05 and higher also are identical (and
    the same as 0x00).  However offset-0x01 has deviating CFI, but that is
    only relevant for Alt3, neither of the other alternative instruction
    streams will ever hit that offset.
    
    This scenario is NOT allowed:
    
              Alt1                    Alt2
    
       0x00   CALL *pv_ops.save_fl    PUSHF
       0x01                           NOP6
       ...
       0x07   NOP                     POP %RAX
    
    The problem here is that offset-0x7, which is an instruction boundary in
    both possible instruction patch streams, has two conflicting stack
    layouts.
    
    [ The above examples were stolen from Peter Zijlstra. ]
    
    The new flattened CFI array is used both for the detection of conflicts
    (like the second example above) and the generation of linear ORC
    entries.
    
    BTW, another benefit of these changes is that, thanks to some related
    cleanups (new fake nops and alt_group struct) objtool can finally be rid
    of fake jumps, which were a constant source of headaches.
    
    [1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
    
    Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
    Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
    c9c324dc
stack-validation.txt 14.7 KB