From f86db34defc323135106dc12e9fa888003cdcbd7 Mon Sep 17 00:00:00 2001 From: Jianzhou Zhao Date: Wed, 13 Jan 2021 01:37:16 +0000 Subject: [PATCH] [MSan] Move origins for overlapped memory transfer Reviewed-by: eugenis Differential Revision: https://reviews.llvm.org/D94572 --- compiler-rt/lib/msan/msan_poisoning.cpp | 79 ++++++++++++++++++- .../test/msan/chained_origin_memmove.cpp | 57 +++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 compiler-rt/test/msan/chained_origin_memmove.cpp diff --git a/compiler-rt/lib/msan/msan_poisoning.cpp b/compiler-rt/lib/msan/msan_poisoning.cpp index d121d45a1951..a92b0565cfa8 100644 --- a/compiler-rt/lib/msan/msan_poisoning.cpp +++ b/compiler-rt/lib/msan/msan_poisoning.cpp @@ -94,23 +94,98 @@ void CopyOrigin(const void *dst, const void *src, uptr size, } } +void ReverseCopyOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) + return; + + uptr d = (uptr)dst; + uptr end = (d + size) & ~3UL; + + // Copy right unaligned origin if that memory is poisoned. + if (end < d + size) { + u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); + if (o) { + if (__msan_get_track_origins() > 1) + o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(end) = o; + } + } + + uptr beg = d & ~3UL; + + if (beg + 4 < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + if (__msan_get_track_origins() > 1) { + u32 *src = (u32 *)MEM_TO_ORIGIN(s + end - beg - 4); + u32 *src_s = (u32 *)MEM_TO_SHADOW(s + end - beg - 4); + u32 *src_begin = (u32 *)MEM_TO_ORIGIN(s); + u32 *dst = (u32 *)MEM_TO_ORIGIN(end - 4); + u32 src_o = 0; + u32 dst_o = 0; + for (; src >= src_begin; --src, --src_s, --dst) { + if (!*src_s) + continue; + if (*src != src_o) { + src_o = *src; + dst_o = ChainOrigin(src_o, stack); + } + *dst = dst_o; + } + } else { + REAL(memmove) + ((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), end - beg - 4); + } + } + + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = GetOriginIfPoisoned((uptr)src, beg + 4 - d); + if (o) { + if (__msan_get_track_origins() > 1) + o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(beg) = o; + } + } +} + +void MoveOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + // If destination origin range overlaps with source origin range, move + // origins by coping origins in a reverse order; otherwise, copy origins in + // a normal order. + uptr src_aligned_beg = reinterpret_cast(src) & ~3UL; + uptr src_aligned_end = (reinterpret_cast(src) + size) & ~3UL; + uptr dst_aligned_beg = reinterpret_cast(dst) & ~3UL; + if (dst_aligned_beg < src_aligned_end && dst_aligned_beg >= src_aligned_beg) + return ReverseCopyOrigin(dst, src, size, stack); + return CopyOrigin(dst, src, size, stack); +} + void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, StackTrace *stack) { if (!MEM_IS_APP(dst)) return; if (!MEM_IS_APP(src)) return; if (src == dst) return; + // MoveOrigin transfers origins by refering to their shadows. So we + // need to move origins before moving shadows. + if (__msan_get_track_origins()) + MoveOrigin(dst, src, size, stack); REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), (void *)MEM_TO_SHADOW((uptr)src), size); - if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); } void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, StackTrace *stack) { if (!MEM_IS_APP(dst)) return; if (!MEM_IS_APP(src)) return; + // Because origin's range is slightly larger than app range, memcpy may also + // cause overlapped origin ranges. REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), (void *)MEM_TO_SHADOW((uptr)src), size); - if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); + if (__msan_get_track_origins()) + MoveOrigin(dst, src, size, stack); } void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { diff --git a/compiler-rt/test/msan/chained_origin_memmove.cpp b/compiler-rt/test/msan/chained_origin_memmove.cpp new file mode 100644 index 000000000000..af6935767bc9 --- /dev/null +++ b/compiler-rt/test/msan/chained_origin_memmove.cpp @@ -0,0 +1,57 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 --check-prefix=CHECK-%short-stack < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=10 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 --check-prefix=CHECK-%short-stack < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 --check-prefix=CHECK-%short-stack < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=10 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 --check-prefix=CHECK-%short-stack < %t.out + +#include +#include + +int xx[10000]; +volatile int idx = 30; + +__attribute__((noinline)) void fn_g(int a, int b) { + xx[idx + OFFSET] = OFFSET == 0 ? a : b; +} + +__attribute__((noinline)) void fn_f(int a, int b) { + fn_g(a, b); +} + +__attribute__((noinline)) void fn_h() { + memmove(&xx[25], &xx, 7500); +} + +int main(int argc, char *argv[]) { + int volatile z1; + int volatile z2; + fn_f(z1, z2); + fn_h(); + return xx[25 + idx + OFFSET]; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: {{#0 .* in main .*chained_origin_memmove.cpp:}}[[@LINE-4]] + +// CHECK: Uninitialized value was stored to memory at +// CHECK-FULL-STACK: {{#1 .* in fn_h.*chained_origin_memmove.cpp:}}[[@LINE-15]] +// CHECK-SHORT-STACK: {{#0 .* in __msan_memmove.*msan_interceptors.cpp:}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK-FULL-STACK: {{#0 .* in fn_g.*chained_origin_memmove.cpp:}}[[@LINE-27]] +// CHECK-FULL-STACK: {{#1 .* in fn_f.*chained_origin_memmove.cpp:}}[[@LINE-24]] +// CHECK-SHORT-STACK: {{#0 .* in fn_g.*chained_origin_memmove.cpp:}}[[@LINE-29]] + +// CHECK-Z1: Uninitialized value was created by an allocation of 'z1' in the stack frame of function 'main' +// CHECK-Z2: Uninitialized value was created by an allocation of 'z2' in the stack frame of function 'main' +// CHECK: {{#0 .* in main.*chained_origin_memmove.cpp:}}[[@LINE-22]]