[HN Gopher] Control-flow integrity in Linux 5.13
       ___________________________________________________________________
        
       Control-flow integrity in Linux 5.13
        
       Author : chmaynard
       Score  : 123 points
       Date   : 2021-05-27 04:26 UTC (1 days ago)
        
 (HTM) web link (lwn.net)
 (TXT) w3m dump (lwn.net)
        
       | yosefk wrote:
       | This CFI implementation makes &func be different values in 2
       | different loadable kernel modules. C says they should be the same
       | value. What do the language lawyers among the compiler writers,
       | who explain why the execution of an entire program is rendered
       | meaningless by undefined behavior, tell us about this? What is
       | the definition of the language compiled with CFI enabled (it's
       | not C but some close relative)?
        
         | Joker_vD wrote:
         | Could you elaborate the scenario a bit? Two different loadable
         | kernel modules would define two different (even if named the
         | same) functions "func" so "&func" has to be two different
         | pointers... or do you mean that if that "func" was defined in
         | the kernel, and both of those modules are linked against the
         | kernel dynamically, then "&func" should yield the same value?
         | But even now they don't even without CFI: when "func" is
         | defined in the dinamically-loaded library, the "&func" value is
         | the pointer into the (calling object file's) PLT.
        
         | bcoates wrote:
         | Loadable modules aren't part of the C spec. Whatever the
         | meaning of &func in 2 different modules potentially compiled by
         | 2 different compilers is is specified by the ABI.
         | 
         | CFI is a breaking change to the contract the Linux kernel makes
         | with kernel modules, but it doesn't have anything in particular
         | to do with C the language.
        
         | derefr wrote:
         | > What is the definition of the language compiled with CFI
         | enabled (it's not C but some close relative)?
         | 
         | I think I saw mentioned, in the comments of a recent HN post
         | about undefined behavior in C, that OS kernels aren't using
         | "standard C", as they use a bunch of compiler flags to
         | constrain what the compiler does in the face of UB, contrary to
         | what the standard says the compiler is allowed to do. As such,
         | OS kernels have always kind of been using a "close relative of
         | C." (Same syntax, slightly different semantics.)
        
         | KMag wrote:
         | I'm not a language lawyer, but I'm pretty sure with x86 and
         | x86_64 ELF dynamic symbols, &func is the address of the program
         | linkage table (PLT) entry for the dynamic symbol, in the
         | current ELF executable/library.
         | 
         | The executable has its own PLT. Each library has its own PLT.
         | The executable has its own global offset table (GOT). Each
         | library has its own GOT. A given dynamically linked function
         | will have one PLT entry and one GOT entry in each library that
         | calls it. After the function's actual global entry point has
         | been resolved, its address will be in the GOT entry. However,
         | symbol resolution can be done lazily, and we don't want taking
         | the address of a function to potentially perform the lookup, so
         | I think the function address is just the start of the PLT
         | entry.
         | 
         | For instance, if the executable and several libraries it loads
         | all call printf, they'll each have their own tiny machine code
         | stub (PLT entry) that (after dynamic symbol resolution) just
         | jumps to the actual printf implementation. The executable will
         | use the start of its PLT entry for printf as its address for
         | printf. Each library will use the start of its PLT entry for
         | printf as its address for printf.
         | 
         | The PLT entry is a tiny machine code stub within the same
         | library or executable that does an indirect jump to the address
         | held in a global offset table (GOT) entry. For lazily resolved
         | symbols, the GOT is initialized to the address of a stub that
         | does the dynamic symbol resolution and overwrites the GOT entry
         | with the correct address (and then jumps to that address). Any
         | later function call will still hit the PLT and jump to the
         | address in the GOT, but the GOT will now point to the actual
         | start of the function.
        
           | pritambaral wrote:
           | So if I take the address of printf in one library, and ask
           | another library to compare it with their address of printf,
           | will they not be equal?
        
             | bombela wrote:
             | Correct. They won't be equal. Since each library has its
             | own table.
        
       | ufo wrote:
       | Does anyone know which tools the kernel developers used to
       | identify all the places in the kernel that use function pointer
       | equality?
       | 
       | Changing how all the function pointers behave is a spooky change
       | to make so they must have had some tools to help them find all
       | the tricky places, right?
        
       | DSingularity wrote:
       | I am wary of any claim to CFI without secure tracking of the
       | call-stack to verify the return destination. You need to compare
       | return destinations against a shadow call-stack. If you dont
       | protect this call stack then attackers will just evolve slightly.
       | You can encrypt/decrypt the return address grsecurity style but
       | we know that this can still be bypassed.
        
         | agumonkey wrote:
         | do you know who works on such topics ?
        
           | DSingularity wrote:
           | I'm not sure what you mean. I've read a variety of papers and
           | articles on this topic. Are you referring to the user that
           | replied to me?
        
         | jnwatson wrote:
         | It looks like backwards-edge protection is in a different
         | patch. Backwards-edge is a bit easier to implement.
        
           | DSingularity wrote:
           | Well I am not sure I agree. It seems to me that there are
           | significant security complications due to the need of making
           | a runtime comparison to determine the integrity of the return
           | destination. How are we to be convinced that the value at the
           | top of the shadow-stack is not attacker-controlled?
        
             | ndesaulniers wrote:
             | An attacker could modify the shadow stack, it's just rather
             | difficult to find where it's randomly placed and would
             | require arbitrary read/write capabilities. The kernel zeros
             | out the register used to write to the shadow stack as soon
             | as possible after use. It's not impossible to defeat, but
             | does raise the bar significantly.
             | 
             | I encourage you to read the commit message of shadow call
             | stack kernel patches, which as another commenter notes is
             | only backwards edge protection; CFI is forwards edge
             | protection.
        
       ___________________________________________________________________
       (page generated 2021-05-28 23:01 UTC)