[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)