On Thu, Jan 10, 2019 at 08:08:37PM -0800, Andy Lutomirski wrote: > > On Jan 10, 2019, at 8:04 PM, Dave Chinner <david@xxxxxxxxxxxxx> > > wrote: > > > >> On Thu, Jan 10, 2019 at 06:18:16PM -0800, Linus Torvalds > >> wrote: > >>> On Thu, Jan 10, 2019 at 6:03 PM Dave Chinner > >>> <david@xxxxxxxxxxxxx> wrote: > >>> > >>>> On Thu, Jan 10, 2019 at 02:11:01PM -0800, Linus Torvalds > >>>> wrote: And we *can* do sane things about RWF_NOWAIT. For > >>>> example, we could start async IO on RWF_NOWAIT, and suddenly > >>>> it would go from "probe the page cache" to "probe and fill", > >>>> and be much harder to use as an attack vector.. > >>> > >>> We can only do that if the application submits the read via > >>> AIO and has an async IO completion reporting mechanism. > >> > >> Oh, no, you misunderstand. > >> > >> RWF_NOWAIT has a lot of situations where it will potentially > >> return early (the DAX and direct IO ones have their own), but I > >> was thinking of the one in generic_file_buffered_read(), which > >> triggers when you don't find a page mapping. That looks like > >> the obvious "probe page cache" case. > >> > >> But we could literally move that test down just a few lines. > >> Let it start read-ahead. > >> > >> .. and then it will actually trigger on the *second* case > >> instead, where we have > >> > >> if (!PageUptodate(page)) { if (iocb->ki_flags & > >> IOCB_NOWAIT) { put_page(page); goto would_block; > >> } > >> > >> and that's where RWF_MNOWAIT would act. > >> > >> It would still return EAGAIN. > >> > >> But it would have started filling the page cache. So now the > >> act of probing would fill the page cache, and the attacker > >> would be left high and dry - the fact that the page cache now > >> exists is because of the attack, not because of whatever it was > >> trying to measure. > >> > >> See? > > > > Except for fadvise(POSIX_FADV_RANDOM) which triggers this code > > in page_cache_sync_readahead(): > > > > /* be dumb */ if (filp && (filp->f_mode & FMODE_RANDOM)) > > { force_page_cache_readahead(mapping, filp, offset, > > req_size); return; } > > > > So it will only read the single page we tried to access and > > won't perturb the rest of the message encoded into subsequent > > pages in file. > > There are two types of attacks. One is an intentional side > channel where two cooperating processes communicate. This is, > under some circumstances, a problem, Yes, that's the covert communication channel that can cross container and machine boundaries without any required privileges. > but it’s not one > we’re about to solve in general. The other is an attacker > monitoring an unwilling process. Which uses exactly the same mechanisms as the first case. i.e. controlled invalidation and page cache residency monitoring.If we aren't going to solve the first problem case, the we aren't going to solve the second because they are one and the same problem... However, I suspect you have misunderstood the monitoring mechanism here - dispatch IO for this page doesn't prevent the information leak about that page. It's when we return EAGAIN that we leak information about page cache residency. What Linus is attempting to do is perturb the nearby state of the page cache by triggering async readahead in the EAGAIN case. Async readahead will fill all the holes in readahead window and hence destroy the information about where the page fault landed and instantiated the page cache. That would prevent the attacker from determining what code the executable is running as they would only be able to check a single page in an executable at a time and that makes the attack highly impractical. But if the attacker uses FADV_RANDOM, readahead is only triggered for the page the attacker is trying to read. Hence it does not disturb the nearby page cache residency pattern the executable's page faults left behind and so doesn't destroy the information that they are trying to extract from the unwilling process. Sure, Linus's change makes monitoring the executable file after FADV_RANDOM a "read-once" mechanism. However, detection of what code is executing is a repeated invalidate-and-sweep exercise to begin with, so it basically doesn't change the information or the rate at which the monitoring process can extract information from the file. /me hasn't thought about this sort of stuff since he was running page cache invalidation attacks on Irix system libraries way back in 2002 when he found a libc bug that killed the init process and paniced the kernel. This isn't my first rodeo - it's been well known for a long, long time that the system page cache can be exploited to monitor executing code... Cheers, Dave. -- Dave Chinner david@xxxxxxxxxxxxx