[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:02 PDT 2025


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

The current IsMemoryPadding implementation assumes that any five 0xCC bytes is suitable for patching. This leaves open the possibility of corruption if the first 0xCC is part of a short jump when attempting to override function with the detour technique.

While a more considerate implementation is to scan through an arbitrary number of previous addresses to ensure the padding is suitable, this fix will simply check the preceding byte in case of that possible short jump scenario.

>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