Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kcov: prefault the kcov_area

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.

[akpm@linux-foundation.org: code cleanup]
[akpm@linux-foundation.org: add comment explaining kcov_fault_in_area()]
[akpm@linux-foundation.org: fancier code comment from Mark]
Link: http://lkml.kernel.org/r/20180504135535.53744-3-mark.rutland@arm.com
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Mark Rutland and committed by
Linus Torvalds
dc55daff c9484b98

+16
+16
kernel/kcov.c
··· 324 324 return 0; 325 325 } 326 326 327 + /* 328 + * Fault in a lazily-faulted vmalloc area before it can be used by 329 + * __santizer_cov_trace_pc(), to avoid recursion issues if any code on the 330 + * vmalloc fault handling path is instrumented. 331 + */ 332 + static void kcov_fault_in_area(struct kcov *kcov) 333 + { 334 + unsigned long stride = PAGE_SIZE / sizeof(unsigned long); 335 + unsigned long *area = kcov->area; 336 + unsigned long offset; 337 + 338 + for (offset = 0; offset < kcov->size; offset += stride) 339 + READ_ONCE(area[offset]); 340 + } 341 + 327 342 static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, 328 343 unsigned long arg) 329 344 { ··· 387 372 #endif 388 373 else 389 374 return -EINVAL; 375 + kcov_fault_in_area(kcov); 390 376 /* Cache in task struct for performance. */ 391 377 t->kcov_size = kcov->size; 392 378 t->kcov_area = kcov->area;