[HN Gopher] IsoAlloc: Detecting Uninitialized Reads with Userfau...
       ___________________________________________________________________
        
       IsoAlloc: Detecting Uninitialized Reads with Userfaultfd
        
       Author : tptacek
       Score  : 22 points
       Date   : 2021-04-08 02:33 UTC (20 hours ago)
        
 (HTM) web link (struct.github.io)
 (TXT) w3m dump (struct.github.io)
        
       | londons_explore wrote:
       | There is some code that does unintialized reads on purpose.
       | 
       | For example highly vectorized loops that process a big buffer of
       | data, but might read a bit out of bounds at the start or end of
       | the buffer. The algorithm as a whole is guaranteed not to depend
       | on the outcomes of those out of bounds reads, so as long as they
       | can be guaranteed not to segfault, they should be safe.
       | 
       | Not allowing this usually results in either slower code (can't
       | vectorize as much) or bigger code (need to have a special case
       | codepath for the first/last items to be processed). The bigger
       | code is often slower too due to worse branch prediction, more
       | cache pressure, etc.
        
         | jeffbee wrote:
         | It doesn't seem like this checker would conflict with that use
         | case. All this checker wants is that if you allocated some
         | bytes, you touch it somewhere before reading it anywhere.
        
           | volta83 wrote:
           | That conflicts.
           | 
           | Suppose you want to allocate 24 bytes and process them using
           | SIMD instructions.
           | 
           | You call `aligned_alloc(24, 16)` to get a 24 byte allocation
           | allocated to a 16 byte boundary.
           | 
           | Since the OS doesn't give the allocator 24 bytes, but whole
           | memory pages, _it is valid_ for your program to read 32 bytes
           | starting at the pointer returned, since that will never read
           | out of the memory page in which this allocation resides.
           | 
           | This means that you can actually read the 24 bytes using 1
           | AVX instruction to read 32 bytes at once into a SIMD
           | register, and do stuff with it (masking the uninitialized
           | memory, etc.).
           | 
           | From those 32 bytes that you read, only the first 24 bytes
           | might have been touched. That's a perfectly normal thing
           | (e.g. you ask the allocator for 24 bytes, and it gives you a
           | pointer to a 32 byte allocation, such that the last 8 bytes
           | will never be touched by anyone).
        
             | jeffbee wrote:
             | But this thing works page-by-page.
        
               | volta83 wrote:
               | So it only errors if you try to access a page in which no
               | byte has been initialized ?
               | 
               | That is, if a program writes only to the 0th byte of a
               | page, this tool won't warn on it reading from the 42nd
               | byte ?
        
               | chrisrohlf wrote:
               | No it will still catch it if you access the 42nd byte
               | with a read, but not if you write to any other byte
               | first. Thats just a limitation of page faults and the
               | information provided by userfaultfd.
        
               | chrisrohlf wrote:
               | Correct, this technique works on a page by page basis.
               | It's the only way to do it with userfaultfd as the
               | technique relies entirely on the first page fault being a
               | read and not a write. Im working on a feature that
               | returns an allocation that straddles 2 pages to catch
               | reads of structure paddings but it will come at the cost
               | of an additional 4k of memory and/or possibly returning
               | unaligned memory.
        
         | the8472 wrote:
         | You could over-allocate to make sure that there's some spare
         | capacity at the beginning/end. Or use mask registers or do the
         | common thing that doesn't trigger UB:
         | 
         | > need to have a special case codepath for the first/last items
         | to be processed
        
       | rwmj wrote:
       | You need to be routinely running valgrind on every bit of C code
       | you write, and that will catch uninitialized reads along with
       | many other classes of bugs.
        
         | gkfasdfasdf wrote:
         | Valgrind can prohibitively slow down your process though, where
         | many timing dependent code paths aren't hit and the app behaves
         | dramatically different than it would in production.
        
       | knorker wrote:
       | Grey text on white background. Ouch, my eyes.
        
         | chrisrohlf wrote:
         | I've gotten this feedback before. It's probably time to change
         | it from #414141
        
         | [deleted]
        
       ___________________________________________________________________
       (page generated 2021-04-08 23:01 UTC)