https://github.com/kuroyukiasuna created 
https://github.com/llvm/llvm-project/pull/210635

Adds a new UBSan check, `unaligned-pointer-subtraction`, that detects 
subtraction of two pointers whose byte distance is not an exact multiple of the 
pointee size.

AI Usage Disclosure
LLM was used to help the investigation and test writing/fixing.

Fixes #206775

>From abc8c77173cf569e110f586206808ef8c50ab1ae Mon Sep 17 00:00:00 2001
From: Jerry Dang <[email protected]>
Date: Sun, 19 Jul 2026 15:23:30 -0400
Subject: [PATCH] Enable ubsan to detect undefined subtraction of two pointers

---
 clang/docs/ReleaseNotes.md                    |  4 +
 clang/docs/UndefinedBehaviorSanitizer.md      |  2 +
 clang/include/clang/Basic/Sanitizers.def      |  4 +-
 clang/lib/CodeGen/CGExprScalar.cpp            | 20 +++++
 clang/lib/CodeGen/SanitizerHandler.h          |  4 +
 clang/test/CodeGen/allow-ubsan-check.c        |  8 +-
 .../ubsan-unaligned-pointer-subtraction.c     | 84 +++++++++++++++++++
 clang/test/Driver/amdgpu-validate-sanitize.cl |  2 +-
 .../Driver/fsanitize-annotate-debug-info.c    |  6 +-
 clang/test/Driver/fsanitize-merge.c           |  6 +-
 clang/test/Driver/fsanitize-minimal-runtime.c |  8 +-
 clang/test/Driver/fsanitize-recover.c         |  2 +-
 clang/test/Driver/fsanitize-skip-hot-cutoff.c |  8 +-
 clang/test/Driver/fsanitize-trap.c            |  6 +-
 clang/test/Driver/fsanitize-undefined.c       | 10 +--
 clang/test/Driver/fsanitize.c                 |  4 +-
 compiler-rt/lib/ubsan/ubsan_checks.inc        |  2 +
 compiler-rt/lib/ubsan/ubsan_handlers.cpp      | 33 ++++++++
 compiler-rt/lib/ubsan/ubsan_handlers.h        |  9 ++
 compiler-rt/lib/ubsan/ubsan_interface.inc     |  2 +
 .../ubsan_minimal/ubsan_minimal_handlers.cpp  |  1 +
 .../Pointer/unaligned-pointer-subtraction.c   | 66 +++++++++++++++
 22 files changed, 260 insertions(+), 31 deletions(-)
 create mode 100644 clang/test/CodeGen/ubsan-unaligned-pointer-subtraction.c
 create mode 100644 
compiler-rt/test/ubsan/TestCases/Pointer/unaligned-pointer-subtraction.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a39bc2dfc5b6d..fb7d66e0e7183 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -423,6 +423,10 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 
 ### Sanitizers
 
+- Introduced `-fsanitize=unaligned-pointer-subtraction`, a new 
UndefinedBehaviorSanitizer check that
+  flag subtraction of two pointers whose byte distance is not a multiple of 
the element size.
+  It is part of the `undefined` group.
+
 ### Python Binding Changes
 
 ### OpenMP Support
diff --git a/clang/docs/UndefinedBehaviorSanitizer.md 
b/clang/docs/UndefinedBehaviorSanitizer.md
index 25587c6e8cb3c..0a23b2a9d6aa5 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.md
+++ b/clang/docs/UndefinedBehaviorSanitizer.md
@@ -173,6 +173,8 @@ Available checks are:
 - `-fsanitize=pointer-overflow`: Performing pointer arithmetic which
   overflows, or where either the old or new pointer value is a null pointer
   (excluding the case where both are null pointers).
+- `-fsanitize=unaligned-pointer-subtraction`: Subtraction of two pointers whose
+  byte distance is not a multiple of the element size.
 - `-fsanitize=return`: In C++, reaching the end of a
   value-returning function without returning a value.
 - `-fsanitize=returns-nonnull-attribute`: Returning null pointer
diff --git a/clang/include/clang/Basic/Sanitizers.def 
b/clang/include/clang/Basic/Sanitizers.def
index da85431625026..c58c28f9dc7eb 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -113,6 +113,7 @@ SANITIZER("shift-base", ShiftBase)
 SANITIZER("shift-exponent", ShiftExponent)
 SANITIZER_GROUP("shift", Shift, ShiftBase | ShiftExponent)
 SANITIZER("signed-integer-overflow", SignedIntegerOverflow)
+SANITIZER("unaligned-pointer-subtraction", UnalignedPointerSubtraction)
 SANITIZER("unreachable", Unreachable)
 SANITIZER("vla-bound", VLABound)
 SANITIZER("vptr", Vptr)
@@ -152,7 +153,8 @@ SANITIZER_GROUP("undefined", Undefined,
                     FloatCastOverflow |
                     IntegerDivideByZero | NonnullAttribute | Null | ObjectSize 
|
                     PointerOverflow | Return | ReturnsNonnullAttribute | Shift 
|
-                    SignedIntegerOverflow | Unreachable | VLABound | Function)
+                    SignedIntegerOverflow | Unreachable | VLABound | Function |
+                    UnalignedPointerSubtraction)
 
 // -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
 SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 81016d3d5d308..a7b8c7f406991 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -20,6 +20,7 @@
 #include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "ConstantEmitter.h"
+#include "SanitizerHandler.h"
 #include "TargetInfo.h"
 #include "TrapReasonBuilder.h"
 #include "clang/AST/ASTContext.h"
@@ -5021,6 +5022,25 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
     divisor = CGF.CGM.getSize(elementSize);
   }
 
+  // If the unaligned-pointer-subtraction sanitizer is on, verify at runtime
+  // that the byte distance is an exact multiple of the element size.
+  if (CGF.SanOpts.has(SanitizerKind::UnalignedPointerSubtraction)) {
+    auto checkOrdinal = SanitizerKind::SO_UnalignedPointerSubtraction;
+    auto CheckHandler = SanitizerHandler::UnalignedPointerSubtraction;
+    SanitizerDebugLocation SanScope(&CGF, {checkOrdinal}, CheckHandler);
+
+    llvm::Value *Zero = llvm::ConstantInt::get(CGF.PtrDiffTy, 0);
+    llvm::Value *Rem = Builder.CreateSRem(diffInChars, divisor, "sub.ptr.rem");
+    llvm::Value *IsExact = Builder.CreateICmpEQ(Rem, Zero, "sub.ptr.exact");
+
+    llvm::Constant *StaticArgs[] = {
+        CGF.EmitCheckSourceLocation(op.E->getExprLoc()),
+        CGF.EmitCheckTypeDescriptor(op.E->getType())};
+    llvm::Value *DynamicArgs[] = {diffInChars, divisor};
+    CGF.EmitCheck({{IsExact, checkOrdinal}}, CheckHandler, StaticArgs,
+                  DynamicArgs);
+  }
+
   // Otherwise, do a full sdiv. This uses the "exact" form of sdiv, since
   // pointer difference in C is only defined in the case where both operands
   // are pointing to elements of an array.
diff --git a/clang/lib/CodeGen/SanitizerHandler.h 
b/clang/lib/CodeGen/SanitizerHandler.h
index 871e17c22d3fa..b39f4f22de48c 100644
--- a/clang/lib/CodeGen/SanitizerHandler.h
+++ b/clang/lib/CodeGen/SanitizerHandler.h
@@ -70,6 +70,10 @@
   SANITIZER_CHECK(                                                             
\
       VLABoundNotPositive, vla_bound_not_positive, 0,                          
\
       "Variable length array bound evaluates to non-positive value")           
\
+  SANITIZER_CHECK(                                                             
\
+      UnalignedPointerSubtraction, unaligned_pointer_subtraction, 0,           
\
+      "Pointer subtraction where the byte distance is not a multiple of the "  
\
+      "element size")                                                          
\
   SANITIZER_CHECK(BoundsSafety, bounds_safety, 0,                              
\
                   "") // BoundsSafety Msg is empty because it is not considered
                       // part of UBSan; therefore, no trap reason is emitted 
for
diff --git a/clang/test/CodeGen/allow-ubsan-check.c 
b/clang/test/CodeGen/allow-ubsan-check.c
index 0fc95c41b3e3d..beb6fbdcb3c41 100644
--- a/clang/test/CodeGen/allow-ubsan-check.c
+++ b/clang/test/CodeGen/allow-ubsan-check.c
@@ -200,7 +200,7 @@ void use(double*);
 // CHECK-NEXT:    call void @use(ptr noundef nonnull [[VLA]]) #[[ATTR7:[0-9]+]]
 // CHECK-NEXT:    [[IDXPROM:%.*]] = sext i32 [[I]] to i64
 // CHECK-NEXT:    [[TMP1:%.*]] = icmp ule i64 [[TMP0]], [[IDXPROM]]
-// CHECK-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 71), 
!nosanitize [[META6]]
+// CHECK-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 72), 
!nosanitize [[META6]]
 // CHECK-NEXT:    [[TMP3:%.*]] = and i1 [[TMP1]], [[TMP2]], !nosanitize 
[[META6]]
 // CHECK-NEXT:    br i1 [[TMP3]], label %[[TRAP:.*]], label %[[BB4:.*]]
 // CHECK:       [[BB4]]:
@@ -219,7 +219,7 @@ void use(double*);
 // TR-NEXT:    call void @use(ptr noundef nonnull [[VLA]]) #[[ATTR8:[0-9]+]]
 // TR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[I]] to i64
 // TR-NEXT:    [[TMP1:%.*]] = icmp ule i64 [[TMP0]], [[IDXPROM]]
-// TR-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 71), 
!nosanitize [[META6]]
+// TR-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 72), 
!nosanitize [[META6]]
 // TR-NEXT:    [[TMP3:%.*]] = and i1 [[TMP1]], [[TMP2]], !nosanitize [[META6]]
 // TR-NEXT:    br i1 [[TMP3]], label %[[TRAP:.*]], label %[[BB4:.*]]
 // TR:       [[BB4]]:
@@ -227,7 +227,7 @@ void use(double*);
 // TR-NEXT:    [[TMP5:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !tbaa 
[[DOUBLE_TBAA10:![0-9]+]]
 // TR-NEXT:    ret double [[TMP5]]
 // TR:       [[TRAP]]:
-// TR-NEXT:    call void @llvm.ubsantrap(i8 71) #[[ATTR7]], !nosanitize 
[[META6]]
+// TR-NEXT:    call void @llvm.ubsantrap(i8 72) #[[ATTR7]], !nosanitize 
[[META6]]
 // TR-NEXT:    unreachable, !nosanitize [[META6]]
 //
 // REC-LABEL: define dso_local double @lbounds(
@@ -238,7 +238,7 @@ void use(double*);
 // REC-NEXT:    call void @use(ptr noundef nonnull [[VLA]]) #[[ATTR5:[0-9]+]]
 // REC-NEXT:    [[IDXPROM:%.*]] = sext i32 [[I]] to i64
 // REC-NEXT:    [[TMP1:%.*]] = icmp ule i64 [[TMP0]], [[IDXPROM]]
-// REC-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 71), 
!nosanitize [[META6]]
+// REC-NEXT:    [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 72), 
!nosanitize [[META6]]
 // REC-NEXT:    [[TMP3:%.*]] = and i1 [[TMP1]], [[TMP2]], !nosanitize [[META6]]
 // REC-NEXT:    br i1 [[TMP3]], label %[[TRAP:.*]], label %[[BB4:.*]]
 // REC:       [[BB4]]:
diff --git a/clang/test/CodeGen/ubsan-unaligned-pointer-subtraction.c 
b/clang/test/CodeGen/ubsan-unaligned-pointer-subtraction.c
new file mode 100644
index 0000000000000..08217f2f6eba3
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-unaligned-pointer-subtraction.c
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c -w -emit-llvm -o - %s 
\
+// RUN:     -fsanitize=unaligned-pointer-subtraction | \
+// RUN:     FileCheck %s --check-prefixes=CHECK,CONLY
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c++ -w -emit-llvm -o - 
%s \
+// RUN:     -fsanitize=unaligned-pointer-subtraction | \
+// RUN:     FileCheck %s
+//
+// Verify that -fsanitize=unaligned-pointer-subtraction instruments the
+// subtraction of two pointers: it checks at runtime that the byte distance is
+// an exact multiple of the element size, and skips the check when the element
+// size is one (where the remainder is trivially zero).
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct { int x, y; } A; // sizeof(A) == 8
+
+// Constant element size: the byte distance is divided by 8, so a check is
+// emitted before the exact division.
+// CHECK-LABEL: define{{.*}} i64 @f_const(
+long f_const(int *p) {
+  // CHECK:      %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+  // CHECK-NEXT: %sub.ptr.rem = srem i64 %sub.ptr.sub, 8, !nosanitize
+  // CHECK-NEXT: %sub.ptr.exact = icmp eq i64 %sub.ptr.rem, 0, !nosanitize
+  // CHECK-NEXT: br i1 %sub.ptr.exact, label %cont, label 
%handler.unaligned_pointer_subtraction{{.*}}, !nosanitize
+  // CHECK:      call void 
@__ubsan_handle_unaligned_pointer_subtraction{{.*}}(ptr @{{[0-9]+}}, i64 
%sub.ptr.sub, i64 8)
+  // CHECK:      %sub.ptr.div = sdiv exact i64 %sub.ptr.sub, 8
+  return (A *)(p + 1) - (A *)p;
+}
+
+// Element size one (char): no divide is emitted, so no check either.
+// CHECK-LABEL: define{{.*}} i64 @f_char(
+long f_char(char *a, char *b) {
+  // CHECK-NOT: srem
+  // CHECK-NOT: __ubsan_handle_unaligned_pointer_subtraction
+  // CHECK:     ret i64
+  return a - b;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// The remaining cases use pointer arithmetic that is only valid in C: VLA
+// element types and the GNU void*/function-pointer extensions. They are
+// compiled and checked only in the C RUN line (CONLY).
+#ifndef __cplusplus
+
+// VLA element type: the divisor is a runtime value (sizeof(int) * n), so the
+// remainder check runs against a non-constant divisor.
+// CONLY-LABEL: define{{.*}} i64 @f_vla(
+long f_vla(int n, int (*p)[n]) {
+  int (*q)[n] = (int (*)[n])((char *)p + 4);
+  // CONLY:      %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+  // CONLY-NEXT: %[[ELT:[0-9]+]] = mul nuw i64 4, %{{[0-9]+}}
+  // CONLY-NEXT: %sub.ptr.rem = srem i64 %sub.ptr.sub, %[[ELT]], !nosanitize
+  // CONLY-NEXT: %sub.ptr.exact = icmp eq i64 %sub.ptr.rem, 0, !nosanitize
+  // CONLY:      call void 
@__ubsan_handle_unaligned_pointer_subtraction{{.*}}(ptr @{{[0-9]+}}, i64 
%sub.ptr.sub, i64 %[[ELT]])
+  // CONLY:      %sub.ptr.div = sdiv exact i64 %sub.ptr.sub, %[[ELT]]
+  return q - p;
+}
+
+// void* arithmetic (GNU extension) has element size one: no check.
+// CONLY-LABEL: define{{.*}} i64 @f_void(
+long f_void(void *a, void *b) {
+  // CONLY-NOT: srem
+  // CONLY-NOT: __ubsan_handle_unaligned_pointer_subtraction
+  // CONLY:     ret i64
+  return a - b;
+}
+
+typedef void (*fp)(void);
+
+// Function-pointer arithmetic (GNU extension) has element size one: no check.
+// CONLY-LABEL: define{{.*}} i64 @f_func(
+long f_func(fp a, fp b) {
+  // CONLY-NOT: srem
+  // CONLY-NOT: __ubsan_handle_unaligned_pointer_subtraction
+  // CONLY:     ret i64
+  return a - b;
+}
+
+#endif // !__cplusplus
diff --git a/clang/test/Driver/amdgpu-validate-sanitize.cl 
b/clang/test/Driver/amdgpu-validate-sanitize.cl
index 907ad201b15b3..ddd80374cdfc4 100644
--- a/clang/test/Driver/amdgpu-validate-sanitize.cl
+++ b/clang/test/Driver/amdgpu-validate-sanitize.cl
@@ -33,7 +33,7 @@
 // CHECK-SAME: "-fsanitize=address"
 
 
-// GENERIC: 
"-fsanitize=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound,unsigned-integer-overflow,unsigned-shift-base,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion,local-bounds,alloc-token"
 
"-fsanitize-recover=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,vla-bound,unsigned-integer-overflow,unsigned-shift-base,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion"
 "-fsanitize-trap=local-bounds" 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// GENERIC: 
"-fsanitize=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound,unsigned-integer-overflow,unsigned-shift-base,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion,local-bounds,alloc-token"
 
"-fsanitize-recover=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,vla-bound,unsigned-integer-overflow,unsigned-shift-base,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,implicit-bitfield-conversion"
 "-fsanitize-trap=local-bounds" 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 
 // FIXME: Should not be forwarding argument
diff --git a/clang/test/Driver/fsanitize-annotate-debug-info.c 
b/clang/test/Driver/fsanitize-annotate-debug-info.c
index 27ffe950579d3..53ce753e53837 100644
--- a/clang/test/Driver/fsanitize-annotate-debug-info.c
+++ b/clang/test/Driver/fsanitize-annotate-debug-info.c
@@ -8,7 +8,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow 
-fsanitize-annotate-debug-info                                 %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fsanitize-annotate-debug-info=bool                   
    -fsanitize-annotate-debug-info=undefined                       %s -### 2>&1 
| FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                                       
     -fsanitize-annotate-debug-info=undefined 
-fsanitize-annotate-debug-info=bool %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-PSEUDO
-// CHECK-UNDEFINED-PSEUDO: 
"-fsanitize-annotate-debug-info=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-PSEUDO: 
"-fsanitize-annotate-debug-info=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing arguments (-fsanitize-annotate-debug-info 
-fno-sanitize-annotate-debug-info=signed-integer-overflow) take precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                                       
     -fsanitize-annotate-debug-info           
-fno-sanitize-annotate-debug-info=signed-integer-overflow %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO2
@@ -17,7 +17,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-annotate-debug-info                     
    -fsanitize-annotate-debug-info=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO2
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow 
-fsanitize-annotate-debug-info           
-fno-sanitize-annotate-debug-info=signed-integer-overflow %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO2
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow 
-fsanitize-annotate-debug-info=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO2
-// CHECK-UNDEFINED-PSEUDO2: 
"-fsanitize-annotate-debug-info=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// CHECK-UNDEFINED-PSEUDO2: 
"-fsanitize-annotate-debug-info=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing -fno-sanitize-annotate-debug-info takes precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                           
-fno-sanitize-annotate-debug-info                                    %s -### 
2>&1 | not FileCheck %s --check-prefix=CHECK-UNDEFINED-PSEUDO3
@@ -37,7 +37,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-annotate-debug-info                     
    -fsanitize-annotate-debug-info=undefined 
-fno-sanitize-annotate-debug-info=alignment,null %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-PSEUDO4
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow 
-fsanitize-annotate-debug-info           
-fno-sanitize-annotate-debug-info=alignment,null %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-PSEUDO4
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined 
-fno-sanitize-annotate-debug-info=signed-integer-overflow 
-fsanitize-annotate-debug-info=undefined 
-fno-sanitize-annotate-debug-info=alignment,null %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-PSEUDO4
-// CHECK-UNDEFINED-PSEUDO4: 
"-fsanitize-annotate-debug-info=array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-PSEUDO4: 
"-fsanitize-annotate-debug-info=array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing arguments (-fno-sanitize-annotate-debug-info 
-fsanitize-annotate-debug-info=alignment,null) take precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                                       
  -fno-sanitize-annotate-debug-info=undefined 
-fsanitize-annotate-debug-info=alignment,null %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-PSEUDO5
diff --git a/clang/test/Driver/fsanitize-merge.c 
b/clang/test/Driver/fsanitize-merge.c
index 936da5bd34a80..93651248cb335 100644
--- a/clang/test/Driver/fsanitize-merge.c
+++ b/clang/test/Driver/fsanitize-merge.c
@@ -9,7 +9,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge=signed-integer-overflow 
-fsanitize-merge                                 %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-MERGE
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fsanitize-merge=bool                       
-fsanitize-merge=undefined                       %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-MERGE
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                             
-fsanitize-merge=undefined -fsanitize-merge=bool %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-MERGE
-// CHECK-UNDEFINED-MERGE: 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-MERGE: 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing arguments (-fsanitize-merge 
-fno-sanitize-merge=signed-integer-overflow) take precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                                       
                 -fno-sanitize-merge=signed-integer-overflow %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE2
@@ -19,7 +19,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge                         
-fsanitize-merge=undefined -fno-sanitize-merge=signed-integer-overflow %s -### 
2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE2
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge=signed-integer-overflow 
-fsanitize-merge           -fno-sanitize-merge=signed-integer-overflow %s -### 
2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE2
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge=signed-integer-overflow 
-fsanitize-merge=undefined -fno-sanitize-merge=signed-integer-overflow %s -### 
2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE2
-// CHECK-UNDEFINED-MERGE2: 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// CHECK-UNDEFINED-MERGE2: 
"-fsanitize-merge=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing -fno-sanitize-merge takes precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                            -fno-sanitize-merge        
                            %s -### 2>&1 | not FileCheck %s 
--check-prefix=CHECK-UNDEFINED-MERGE3
@@ -40,7 +40,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge                         
-fsanitize-merge=undefined -fno-sanitize-merge=alignment,null %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE4
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge=signed-integer-overflow 
-fsanitize-merge           -fno-sanitize-merge=alignment,null %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE4
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined -fno-sanitize-merge=signed-integer-overflow 
-fsanitize-merge=undefined -fno-sanitize-merge=alignment,null %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE4
-// CHECK-UNDEFINED-MERGE4: 
"-fsanitize-merge=array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-MERGE4: 
"-fsanitize-merge=array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
 
 // The trailing arguments (-fno-sanitize-merge 
-fsanitize-merge=alignment,null) take precedence
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-trap=undefined                                          
-fno-sanitize-merge           -fsanitize-merge=alignment,null %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED-MERGE5
diff --git a/clang/test/Driver/fsanitize-minimal-runtime.c 
b/clang/test/Driver/fsanitize-minimal-runtime.c
index 32714dfb806dd..60b834e9f4401 100644
--- a/clang/test/Driver/fsanitize-minimal-runtime.c
+++ b/clang/test/Driver/fsanitize-minimal-runtime.c
@@ -5,21 +5,21 @@
 // CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' 
not allowed with '-fsanitize=thread'
 
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UBSAN-MINIMAL
-// CHECK-UBSAN-MINIMAL: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-UBSAN-MINIMAL: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 // CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
 
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-minimal-runtime -fsanitize-handler-preserve-all-regs %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL-PRESERVE-X86-64
-// CHECK-UBSAN-MINIMAL-PRESERVE-X86-64: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-UBSAN-MINIMAL-PRESERVE-X86-64: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 // CHECK-UBSAN-MINIMAL-PRESERVE-X86-64: "-fsanitize-minimal-runtime"
 // CHECK-UBSAN-MINIMAL-PRESERVE-X86-64: "-fsanitize-handler-preserve-all-regs
 
 // RUN: %clang --target=aarch64-linux-gnu -fsanitize=undefined 
-fsanitize-minimal-runtime -fsanitize-handler-preserve-all-regs %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL-PRESERVE-AARCH64
-// CHECK-UBSAN-MINIMAL-PRESERVE-AARCH64: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-UBSAN-MINIMAL-PRESERVE-AARCH64: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 // CHECK-UBSAN-MINIMAL-PRESERVE-AARCH64: "-fsanitize-minimal-runtime"
 // CHECK-UBSAN-MINIMAL-PRESERVE-AARCH64: "-fsanitize-handler-preserve-all-regs
 
 // RUN: %clang --target=i386-linux-gnu -fsanitize=undefined 
-fsanitize-minimal-runtime -fsanitize-handler-preserve-all-regs %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL-PRESERVE-I386
-// CHECK-UBSAN-MINIMAL-PRESERVE-I386: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-UBSAN-MINIMAL-PRESERVE-I386: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 // CHECK-UBSAN-MINIMAL-PRESERVE-I386: "-fsanitize-minimal-runtime"
 // CHECK-UBSAN-MINIMAL-PRESERVE-I386-NOT: "-fsanitize-handler-preserve-all-regs
 
diff --git a/clang/test/Driver/fsanitize-recover.c 
b/clang/test/Driver/fsanitize-recover.c
index 2f2d793ddd379..dc15dbc975ce1 100644
--- a/clang/test/Driver/fsanitize-recover.c
+++ b/clang/test/Driver/fsanitize-recover.c
@@ -5,7 +5,7 @@
 // RUN: %clang --target=x86_64-linux-gnu %s -fsanitize=undefined 
-fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-NO-RECOVER-UBSAN
 // RUN: %clang --target=x86_64-linux-gnu %s -fsanitize=undefined 
-fno-sanitize-recover=all -fsanitize-recover=thread -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-NO-RECOVER-UBSAN
 // RUN: %clang --target=x86_64-linux-gnu %s -fsanitize=undefined 
-fsanitize-recover=all -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-NO-RECOVER-UBSAN
-// CHECK-RECOVER-UBSAN: 
"-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){16}"}}
+// CHECK-RECOVER-UBSAN: 
"-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|unaligned-pointer-subtraction),?){17}"}}
 // CHECK-NO-RECOVER-UBSAN-NOT: -fsanitize-recover
 
 // RUN: %clang --target=x86_64-linux-gnu %s -fsanitize=undefined 
-fno-sanitize-recover=all -fsanitize-recover=object-size,shift-base -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-PARTIAL-RECOVER
diff --git a/clang/test/Driver/fsanitize-skip-hot-cutoff.c 
b/clang/test/Driver/fsanitize-skip-hot-cutoff.c
index 313ec69ca74d1..b763838a551bd 100644
--- a/clang/test/Driver/fsanitize-skip-hot-cutoff.c
+++ b/clang/test/Driver/fsanitize-skip-hot-cutoff.c
@@ -1,6 +1,6 @@
 // -fsanitize-skip-hot-cutoff=undefined=0.5
 // RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-skip-hot-cutoff=undefined=0.5 %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SKIP-HOT-CUTOFF1
-// CHECK-SKIP-HOT-CUTOFF1: 
"-fsanitize-skip-hot-cutoff={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function)=0.5(0*),?){18}"}}
+// CHECK-SKIP-HOT-CUTOFF1: 
"-fsanitize-skip-hot-cutoff={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction)=0.5(0*),?){19}"}}
 
 // No-op: no sanitizers are specified
 // RUN: %clang -Werror --target=x86_64-linux-gnu 
-fsanitize-skip-hot-cutoff=undefined=0.5 %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SKIP-HOT-CUTOFF2
@@ -9,11 +9,11 @@
 
 // Enable undefined, then cancel out integer using a cutoff of 0.0
 // RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-skip-hot-cutoff=undefined=0.5,integer=0.0 %s -### 2>&1 | FileCheck 
%s --check-prefix=CHECK-SKIP-HOT-CUTOFF3
-// CHECK-SKIP-HOT-CUTOFF3: 
"-fsanitize-skip-hot-cutoff={{((unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function)=0.5(0*),?){14}"}}
+// CHECK-SKIP-HOT-CUTOFF3: 
"-fsanitize-skip-hot-cutoff={{((unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction)=0.5(0*),?){15}"}}
 
 // Enable undefined, then cancel out integer using a cutoff of 0.0, then 
re-enable signed-integer-overflow
 // RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-skip-hot-cutoff=undefined=0.5,integer=0.0,signed-integer-overflow=0.7
 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF4
-// CHECK-SKIP-HOT-CUTOFF4: 
"-fsanitize-skip-hot-cutoff={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function)=0.[57]0*,?){15}"}}
+// CHECK-SKIP-HOT-CUTOFF4: 
"-fsanitize-skip-hot-cutoff={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction)=0.[57]0*,?){16}"}}
 
 // Check that -fsanitize-skip-hot-cutoff=undefined=0.4 does not widen the set 
of -fsanitize=integer checks.
 // RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=integer 
-fsanitize-skip-hot-cutoff=undefined=0.4 %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SKIP-HOT-CUTOFF5
@@ -21,7 +21,7 @@
 
 // No-op: it's allowed for the user to specify a cutoff of 0.0, though the 
argument is not passed along by the driver.
 // RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined 
-fsanitize-skip-hot-cutoff=undefined=0.0 %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SKIP-HOT-CUTOFF6
-// CHECK-SKIP-HOT-CUTOFF6: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-SKIP-HOT-CUTOFF6: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 // CHECK-SKIP-HOT-CUTOFF6-NOT: "-fsanitize-skip-hot-cutoff"
 
 // Invalid: bad sanitizer
diff --git a/clang/test/Driver/fsanitize-trap.c 
b/clang/test/Driver/fsanitize-trap.c
index be13edeffdda0..6ee3480108f9d 100644
--- a/clang/test/Driver/fsanitize-trap.c
+++ b/clang/test/Driver/fsanitize-trap.c
@@ -5,6 +5,6 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize-undefined-trap-on-error 
-fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-TRAP
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize-trap 
-fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-UNDEFINED-TRAP
 // CHECK-UNDEFINED-TRAP-NOT: -fsanitize-recover
-// CHECK-UNDEFINED-TRAP: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
-// CHECK-UNDEFINED-TRAP: 
"-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
-// CHECK-UNDEFINED-TRAP2: 
"-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// CHECK-UNDEFINED-TRAP: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
+// CHECK-UNDEFINED-TRAP: 
"-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unaligned-pointer-subtraction,unreachable,vla-bound"
+// CHECK-UNDEFINED-TRAP2: 
"-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unaligned-pointer-subtraction,unreachable,vla-bound"
diff --git a/clang/test/Driver/fsanitize-undefined.c 
b/clang/test/Driver/fsanitize-undefined.c
index 13234a43e5074..e2e440ad6fa4a 100644
--- a/clang/test/Driver/fsanitize-undefined.c
+++ b/clang/test/Driver/fsanitize-undefined.c
@@ -1,11 +1,11 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UNDEFINED
-// CHECK-UNDEFINED: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
+// CHECK-UNDEFINED: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|unaligned-pointer-subtraction),?){19}"}}
 
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=thread,undefined 
-fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,builtin,enum 
%s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
-// CHECK-PARTIAL-UNDEFINED: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){14}"}}
+// CHECK-PARTIAL-UNDEFINED: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute|unaligned-pointer-subtraction),?){15}"}}
 
 // RUN: %clang --target=x86_64-apple-darwin10 -fsanitize=undefined %s -### 
2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
-// CHECK-UNDEFINED-DARWIN: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
+// CHECK-UNDEFINED-DARWIN: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|unaligned-pointer-subtraction),?){19}"}}
 
 // RUN: %clang --target=i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | 
FileCheck %s --check-prefixes=CHECK-UNDEFINED-WIN32,CHECK-UNDEFINED-MSVC
 // RUN: %clang --target=i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 
| FileCheck %s 
--check-prefixes=CHECK-UNDEFINED-WIN32,CHECK-UNDEFINED-WIN-CXX,CHECK-UNDEFINED-MSVC
@@ -16,8 +16,8 @@
 // CHECK-UNDEFINED-WIN64: 
"--dependent-lib={{[^"]*}}ubsan_standalone{{(-x86_64)?}}.lib"
 // CHECK-UNDEFINED-WIN64-MINGW: 
"--dependent-lib={{[^"]*}}libclang_rt.ubsan_standalone{{(-x86_64)?}}.a"
 // CHECK-UNDEFINED-WIN-CXX: 
"--dependent-lib={{[^"]*}}ubsan_standalone_cxx{{[^"]*}}.lib"
-// CHECK-UNDEFINED-MSVC-SAME: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
-// CHECK-UNDEFINED-WIN64-MINGW-SAME: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
+// CHECK-UNDEFINED-MSVC-SAME: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
+// CHECK-UNDEFINED-WIN64-MINGW-SAME: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|unaligned-pointer-subtraction),?){19}"}}
 
 // RUN: %clang --target=i386-pc-win32 -fsanitize-coverage=bb %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-COVERAGE-WIN32
 // CHECK-COVERAGE-WIN32: 
"--dependent-lib={{[^"]*}}ubsan_standalone{{(-i386)?}}.lib"
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index f6a82d899d5bf..ffa0bbc880879 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -340,7 +340,7 @@
 // CHECK-ASAN-IOS: -fsanitize=address
 
 // RUN: %clang --target=i386-pc-openbsd -fsanitize=undefined %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-UBSAN-OPENBSD
-// CHECK-UBSAN-OPENBSD: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
+// CHECK-UBSAN-OPENBSD: 
"-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|unaligned-pointer-subtraction|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
 
 // RUN: not %clang --target=i386-pc-openbsd -fsanitize=address %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-ASAN-OPENBSD
 // CHECK-ASAN-OPENBSD: unsupported option '-fsanitize=address' for target 
'i386-pc-openbsd'
@@ -562,4 +562,4 @@
 // CHECK-UBSAN-FUNCTION-TARGET-DAG: error: unsupported option 
'-fsanitize=function' for target 'x86_64-sie-ps5'
 // CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY-DAG: error: invalid argument 
'-fsanitize=function' not allowed with '-mexecute-only'
 // CHECK-UBSAN-FUNCTION-MPURE-CODE-DAG: error: invalid argument 
'-fsanitize=function' not allowed with '-mpure-code'
-// CHECK-UBSAN-UNDEFINED: 
"-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}}
+// CHECK-UBSAN-UNDEFINED: 
"-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unaligned-pointer-subtraction|unreachable|vla-bound),?){18}"}}
diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc 
b/compiler-rt/lib/ubsan/ubsan_checks.inc
index b1d09a9024e7e..a7c8b0ed2054d 100644
--- a/compiler-rt/lib/ubsan/ubsan_checks.inc
+++ b/compiler-rt/lib/ubsan/ubsan_checks.inc
@@ -26,6 +26,8 @@ UBSAN_CHECK(NullptrWithNonZeroOffset, 
"nullptr-with-nonzero-offset",
 UBSAN_CHECK(NullptrAfterNonZeroOffset, "nullptr-after-nonzero-offset",
             "pointer-overflow")
 UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow")
+UBSAN_CHECK(UnalignedPointerSubtraction, "unaligned-pointer-subtraction",
+            "unaligned-pointer-subtraction")
 UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
 UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment")
 UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp 
b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 2614d49bf238b..c9d5f55c8025f 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -855,6 +855,39 @@ void 
__ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
   Die();
 }
 
+static void
+handleUnalignedPointerSubtractionImpl(UnalignedPointerSubtractionData *Data,
+                                      ValueHandle Diff, ValueHandle EltSize,
+                                      ReportOptions Opts) {
+  SourceLocation Loc = Data->Loc.acquire();
+  ErrorType ET = ErrorType::UnalignedPointerSubtraction;
+
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
+  ScopedReport R(Opts, Loc, ET);
+
+  Diag(Loc, DL_Error, ET,
+       "pointer subtraction with byte distance %0 that is not a multiple of "
+       "the element size %1")
+      << Value(Data->Type, Diff) << (unsigned long long)EltSize;
+}
+
+void __ubsan::__ubsan_handle_unaligned_pointer_subtraction(
+    UnalignedPointerSubtractionData *Data, ValueHandle Diff,
+    ValueHandle EltSize) {
+  GET_REPORT_OPTIONS(false);
+  handleUnalignedPointerSubtractionImpl(Data, Diff, EltSize, Opts);
+}
+
+void __ubsan::__ubsan_handle_unaligned_pointer_subtraction_abort(
+    UnalignedPointerSubtractionData *Data, ValueHandle Diff,
+    ValueHandle EltSize) {
+  GET_REPORT_OPTIONS(true);
+  handleUnalignedPointerSubtractionImpl(Data, Diff, EltSize, Opts);
+  Die();
+}
+
 // Returns true if this is an artificial debug location created by the
 // LowerTypeTests pass (see createJumpTableDebugInfo in LLVM).
 static bool isArtificialStack(const SymbolizedStack *S) {
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.h 
b/compiler-rt/lib/ubsan/ubsan_handlers.h
index 521caa96bc771..c41235327206d 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.h
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.h
@@ -208,6 +208,15 @@ struct PointerOverflowData {
 RECOVERABLE(pointer_overflow, PointerOverflowData *Data, ValueHandle Base,
             ValueHandle Result)
 
+struct UnalignedPointerSubtractionData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+};
+
+RECOVERABLE(unaligned_pointer_subtraction,
+            UnalignedPointerSubtractionData *Data, ValueHandle Diff,
+            ValueHandle EltSize)
+
 /// \brief Known CFI check kinds.
 /// Keep in sync with the enum of the same name in CodeGenFunction.h
 enum CFITypeCheckKind : unsigned char {
diff --git a/compiler-rt/lib/ubsan/ubsan_interface.inc 
b/compiler-rt/lib/ubsan/ubsan_interface.inc
index 7a9cb9b336f6e..3dbfbb0a4d0d2 100644
--- a/compiler-rt/lib/ubsan/ubsan_interface.inc
+++ b/compiler-rt/lib/ubsan/ubsan_interface.inc
@@ -56,6 +56,8 @@ INTERFACE_FUNCTION(__ubsan_handle_sub_overflow)
 INTERFACE_FUNCTION(__ubsan_handle_sub_overflow_abort)
 INTERFACE_FUNCTION(__ubsan_handle_type_mismatch_v1)
 INTERFACE_FUNCTION(__ubsan_handle_type_mismatch_v1_abort)
+INTERFACE_FUNCTION(__ubsan_handle_unaligned_pointer_subtraction)
+INTERFACE_FUNCTION(__ubsan_handle_unaligned_pointer_subtraction_abort)
 INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive)
 INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive_abort)
 INTERFACE_WEAK_FUNCTION(__ubsan_default_options)
diff --git a/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp 
b/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
index e68e494564cc8..400f733cfa633 100644
--- a/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
+++ b/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
@@ -227,4 +227,5 @@ HANDLER(nonnull_return, "nonnull-return")
 HANDLER(nullability_arg, "nullability-arg")
 HANDLER(nullability_return, "nullability-return")
 HANDLER(pointer_overflow, "pointer-overflow")
+HANDLER(unaligned_pointer_subtraction, "unaligned-pointer-subtraction")
 HANDLER(cfi_check_fail, "cfi-check-fail")
diff --git 
a/compiler-rt/test/ubsan/TestCases/Pointer/unaligned-pointer-subtraction.c 
b/compiler-rt/test/ubsan/TestCases/Pointer/unaligned-pointer-subtraction.c
new file mode 100644
index 0000000000000..7eace9910881a
--- /dev/null
+++ b/compiler-rt/test/ubsan/TestCases/Pointer/unaligned-pointer-subtraction.c
@@ -0,0 +1,66 @@
+// Check that -fsanitize=unaligned-pointer-subtraction reports, at runtime, a
+// subtraction of two pointers whose byte distance is not an exact multiple of
+// the element size (i.e. the operands do not point into the same array).
+//
+// RUN: %clang -fsanitize=unaligned-pointer-subtraction %s -o %t
+// RUN: %run %t a 2>&1 | FileCheck %s --check-prefix=CONST
+// RUN: %run %t n 2>&1 | FileCheck %s --check-prefix=NEG
+// RUN: %run %t v 2>&1 | FileCheck %s --check-prefix=VLA
+// RUN: %run %t o 2>&1 | FileCheck %s --check-prefix=SAFE 
--implicit-check-not="runtime error:"
+//
+// Fatal (non-recoverable) mode aborts after the first report.
+// RUN: %clang -fsanitize=unaligned-pointer-subtraction \
+// RUN:     -fno-sanitize-recover=unaligned-pointer-subtraction %s -o %t.abort
+// RUN: not %run %t.abort a 2>&1 | FileCheck %s --check-prefix=ABORT
+
+#include <stdio.h>
+
+typedef struct {
+  int x, y;
+} A; // sizeof(A) == 8
+
+// Constant element size: byte distance 4 is not a multiple of 8.
+__attribute__((noinline)) long sub_const(int *p) {
+  return (A *)(p + 1) - (A *)p;
+}
+
+// Negative distance: exercises signed formatting of the reported distance.
+__attribute__((noinline)) long sub_negative(int *p) {
+  return (A *)p - (A *)(p + 1);
+}
+
+// VLA element type: the element size (sizeof(int) * n) is a runtime value.
+__attribute__((noinline)) long sub_vla(int n, int (*p)[n]) {
+  int(*q)[n] = (int(*)[n])((char *)p + 4);
+  return q - p;
+}
+
+// Valid subtraction: byte distance 8 is a multiple of 8, so no report.
+__attribute__((noinline)) long sub_ok(int *p) { return (A *)(p + 2) - (A *)p; }
+
+// CONST: runtime error: pointer subtraction with byte distance 4 that is not 
a multiple of the element size 8
+// NEG: runtime error: pointer subtraction with byte distance -4 that is not a 
multiple of the element size 8
+// VLA: runtime error: pointer subtraction with byte distance 4 that is not a 
multiple of the element size 12
+// ABORT: runtime error: pointer subtraction with byte distance 4 that is not 
a multiple of the element size 8
+// SAFE: r=1
+int main(int argc, char **argv) {
+  static int a[8];
+  char c = argc > 1 ? argv[1][0] : 'a';
+  long r = 0;
+  switch (c) {
+  case 'a':
+    r = sub_const(a);
+    break;
+  case 'n':
+    r = sub_negative(a);
+    break;
+  case 'v':
+    r = sub_vla(3, (int(*)[3])a);
+    break;
+  case 'o':
+    r = sub_ok(a);
+    break;
+  }
+  printf("r=%ld\n", r);
+  return 0;
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to