[compiler-rt] [ASan][Windows][x86] Modify IsMemoryPadding to Check for Possible Shortjump Corruption (PR #152016)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 4 12:12:13 PDT 2025


https://github.com/39otsu updated https://github.com/llvm/llvm-project/pull/152016

>From 670ec6c2c8cc38a55b48e730e4e15c66326755bf Mon Sep 17 00:00:00 2001
From: MacGyver Codilla <mcodilla at microsoft.com>
Date: Mon, 4 Aug 2025 12:00:30 -0700
Subject: [PATCH 1/2] Check for possible shortjump corruption

---
 compiler-rt/lib/interception/interception_win.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp
index 246a22c56c31a..a273501a75a3f 100644
--- a/compiler-rt/lib/interception/interception_win.cpp
+++ b/compiler-rt/lib/interception/interception_win.cpp
@@ -254,6 +254,14 @@ static bool RestoreMemoryProtection(
 
 static bool IsMemoryPadding(uptr address, uptr size) {
   u8* function = (u8*)address;
+    if (function[-1] == 0xEB) {
+    // Check for short jump instruction.
+    // There is a rare instance of a short jump (i.e. 0xEB) containing a 0xCC offset placed
+    // right before 4 bytes of 0xCC padding. This conditional will prevent an 
+    // erroneous detour function override by falling through to use 
+    // another interception technique.
+    return false;
+  }
   for (size_t i = 0; i < size; ++i)
     if (function[i] != 0x90 && function[i] != 0xCC)
       return false;

>From 17df9bc1a3d9e03b5843aeedcbe7bc1e715aafbd Mon Sep 17 00:00:00 2001
From: MacGyver Codilla <mcodilla at microsoft.com>
Date: Mon, 4 Aug 2025 12:00:58 -0700
Subject: [PATCH 2/2] Add tests

---
 .../Windows/intercept_detour_minus_one.asm    | 35 +++++++++++
 .../Windows/intercept_detour_minus_one.cpp    | 60 +++++++++++++++++++
 2 files changed, 95 insertions(+)
 create mode 100644 compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.asm
 create mode 100644 compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.cpp

diff --git a/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.asm b/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.asm
new file mode 100644
index 0000000000000..1c5c66f195fce
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.asm
@@ -0,0 +1,35 @@
+.386  
+
+_text segment para 'CODE'  ; Start the code segment.       
+    align 16
+    public _false_header
+    public _function_to_intercept
+
+_false_header proc
+cc:
+    db 66h, 90h               ; Padding function to force offset to 0xCC
+    nop dword ptr [eax+eax*1+10000000h] 
+    nop dword ptr [eax+eax*1+10000000h]
+    nop dword ptr [eax+eax*1+10000000h]
+    nop dword ptr [eax+eax*1+10000000h]
+    nop dword ptr [eax+eax*1+10000000h]
+    nop dword ptr [eax+eax*1+10000000h]
+    jmp cc                    ; Short jump with a 0xCC byte used as an offset
+    int 3                     ; 4 bytes of 0xCC padding
+    int 3
+    int 3
+    int 3
+_false_header endp
+
+_function_to_intercept proc
+    mov edi, edi               ; Function to be overridden
+    push ebp
+    mov ebp, esp
+    mov eax, 0 
+    pop ebp
+    ret           
+_function_to_intercept endp
+
+_text ends       
+
+end
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.cpp b/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.cpp
new file mode 100644
index 0000000000000..529e3412f7ee3
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/intercept_detour_minus_one.cpp
@@ -0,0 +1,60 @@
+// RUN: ml /c /coff /Fo%t_asm.obj %p/intercept_detour_minus_one.asm
+// RUN: %clang_cl -Od %s %t_asm.obj -Fe%t /link /INFERASANLIBS
+// RUN: %run %t 2>&1 | FileCheck %s
+
+// This test is for the Windows 32bit-specific interception technique detour. 
+// There is a rare instance of a short jump containing a 0xCC offset placed
+// right before 4 bytes of 0xCC padding. This test checks that the
+// detour function override is not applied in that instance.
+// UNSUPPORTED: asan-64-bits
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <algorithm>
+
+extern "C" __declspec(dllimport)
+bool __cdecl __sanitizer_override_function_by_addr(
+    void *source_function,
+    void *target_function,
+    void **old_target_function = nullptr
+    );
+
+template <typename F>
+F *apply_interception(const F& source, const F& target) {
+    void *old_default = nullptr;
+    if (!__sanitizer_override_function_by_addr(&source, &target, &old_default)) {
+        fputs("__sanitizer_override_function_by_addr failed.", stderr); // CHECK-NOT: __sanitizer_override_function_by_addr failed.
+        exit(1);
+    }
+    return reinterpret_cast<F*>(old_default);
+}
+
+extern "C" bool validate_interception(void * addr) {
+    // Checks if 5 preceding bytes have been
+    // corrupted by the interception.
+    auto p = static_cast<const uint8_t*>(addr); // use uint8_t for byte-wise access
+    return std::all_of(p - 5, p, [](uint8_t byte) {
+        // 0xCC: INT3 (breakpoint instruction), used for padding or debugging
+        // 0x90: NOP (no operation), used for padding or alignment
+        return byte == 0xCC || byte == 0x90;
+    });
+}
+
+int dummy_function() {
+    // Dummy function to overriding with.
+    // It should not be called directly in this test.
+    return 0;
+}
+extern "C" int false_header();
+extern "C" int function_to_intercept();
+
+int main() {
+    auto func = apply_interception(dummy_function, function_to_intercept);
+    if (validate_interception((void*)function_to_intercept)) {
+        printf("Success\n"); // CHECK: Success
+        return 0;
+    }
+    return 1;
+}
\ No newline at end of file



More information about the llvm-commits mailing list