Skip to content

Commit

Permalink
kcov: prefault the kcov_area
Browse files Browse the repository at this point in the history
On many architectures the vmalloc area is lazily faulted in upon first
access.  This is problematic for KCOV, as __sanitizer_cov_trace_pc
accesses the (vmalloc'd) kcov_area, and fault handling code may be
instrumented.  If an access to kcov_area faults, this will result in
mutual recursion through the fault handling code and
__sanitizer_cov_trace_pc(), eventually leading to stack corruption
and/or overflow.

We can avoid this by faulting in the kcov_area before
__sanitizer_cov_trace_pc() is permitted to access it.  Once it has been
faulted in, it will remain present in the process page tables, and will
not fault again.

[[email protected]: code cleanup]
[[email protected]: add comment explaining kcov_fault_in_area()]
[[email protected]: fancier code comment from Mark]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Mark Rutland <[email protected]>
Acked-by: Andrey Ryabinin <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Mark Rutland authored and torvalds committed Jun 14, 2018
1 parent c9484b9 commit dc55daf
Showing 1 changed file with 16 additions and 0 deletions.
16 changes: 16 additions & 0 deletions kernel/kcov.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,21 @@ static int kcov_close(struct inode *inode, struct file *filep)
return 0;
}

/*
* Fault in a lazily-faulted vmalloc area before it can be used by
* __santizer_cov_trace_pc(), to avoid recursion issues if any code on the
* vmalloc fault handling path is instrumented.
*/
static void kcov_fault_in_area(struct kcov *kcov)
{
unsigned long stride = PAGE_SIZE / sizeof(unsigned long);
unsigned long *area = kcov->area;
unsigned long offset;

for (offset = 0; offset < kcov->size; offset += stride)
READ_ONCE(area[offset]);
}

static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
unsigned long arg)
{
Expand Down Expand Up @@ -372,6 +387,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
#endif
else
return -EINVAL;
kcov_fault_in_area(kcov);
/* Cache in task struct for performance. */
t->kcov_size = kcov->size;
t->kcov_area = kcov->area;
Expand Down

0 comments on commit dc55daf

Please sign in to comment.