lebedev.ri updated this revision to Diff 218519.
lebedev.ri added a comment.

Add test that show that `__builtin_offsetof()` / `((uintptr_t)(&(((S 
*)nullptr)->y)))` are already not sanitized.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D67122/new/

https://reviews.llvm.org/D67122

Files:
  clang/docs/ReleaseNotes.rst
  clang/docs/UndefinedBehaviorSanitizer.rst
  clang/include/clang/Basic/Sanitizers.def
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c
  clang/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c
  clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
  clang/test/CodeGen/catch-pointer-offsetting.c
  clang/test/CodeGen/catch-pointer-overflow.c
  clang/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp
  clang/test/Driver/fsanitize.c
  compiler-rt/lib/ubsan/ubsan_checks.inc
  compiler-rt/lib/ubsan/ubsan_handlers.cpp
  compiler-rt/test/ubsan/TestCases/Pointer/index-overflow.cpp
  
compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-constants.cpp
  
compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-summary.cpp
  
compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-variable.cpp
  compiler-rt/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
  compiler-rt/test/ubsan_minimal/TestCases/nullptr-and-nonzero-offset.c
  llvm/docs/ReleaseNotes.rst

Index: llvm/docs/ReleaseNotes.rst
===================================================================
--- llvm/docs/ReleaseNotes.rst
+++ llvm/docs/ReleaseNotes.rst
@@ -54,6 +54,20 @@
   ``bcmp`` pattern, and convert it into a call to ``bcmp`` (or ``memcmp``)
   function.
 
+* As per :ref:`LLVM Language Reference Manual <i_getelementptr>`,
+  ``getelementptr inbounds`` can not change the null status of a pointer,
+  meaning it can not produce non-null pointer given null base pointer, and
+  likewise given non-null base pointer it can not produce null pointer; if it
+  does, the result is a :ref:`poison value <poisonvalues>`.
+  Since `r369789 <https://reviews.llvm.org/rL369789>`_
+  (`D66608 <https://reviews.llvm.org/D66608>`_ ``[InstCombine] icmp eq/ne (gep
+  inbounds P, Idx..), null -> icmp eq/ne P, null``) LLVM uses that for
+  transformations. If the original source violates these requirements this
+  may result in code being miscompiled. If you are using clang front-end,
+  Undefined Behaviour Sanitizer ``-fsanitize=nullptr-and-nonzero-offset`` check
+  will catch such cases.
+
+
 Changes to the LLVM IR
 ----------------------
 
Index: compiler-rt/test/ubsan_minimal/TestCases/nullptr-and-nonzero-offset.c
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan_minimal/TestCases/nullptr-and-nonzero-offset.c
@@ -0,0 +1,23 @@
+// RUN: %clang -fsanitize=nullptr-and-nonzero-offset %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+  char *base, *result;
+
+  // CHECK-NOT: pointer-overflow
+
+  base = (char *)0;
+  result = base + 1;
+  // CHECK: pointer-overflow
+
+  // CHECK-NOT: pointer-overflow
+
+  base = (char *)1;
+  result = base - 1;
+  // CHECK: pointer-overflow
+
+  // CHECK-NOT: pointer-overflow
+
+  return 0;
+}
Index: compiler-rt/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
===================================================================
--- compiler-rt/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
+++ compiler-rt/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
@@ -12,7 +12,7 @@
   // CHECK: unsigned-index-expression.cpp:[[@LINE+1]]:16: runtime error: subtraction of unsigned offset from 0x{{.*}} overflowed to 0x{{.*}}
   char *q1 = p - neg_1;
 
-  // CHECK: unsigned-index-expression.cpp:[[@LINE+2]]:16: runtime error: pointer index expression with base 0x{{0*}} overflowed to 0x{{.*}}
+  // CHECK: unsigned-index-expression.cpp:[[@LINE+2]]:16: runtime error: applying non-zero offset {{.*}} to null pointer is undefined
   char *n = nullptr;
   char *q2 = n - 1ULL;
 
Index: compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-variable.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-variable.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-OK
+
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c   -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+// RUN: %clang -x c++ -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t I_AM_UB 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefix=CHECK-UB
+
+#include <stdint.h>
+#include <stdio.h>
+
+// Just so deduplication doesn't do anything.
+static char *getelementpointer_inbounds_v0(char *base, unsigned long offset) {
+  // Potentially UB.
+  return base + offset;
+}
+static char *getelementpointer_inbounds_v1(char *base, unsigned long offset) {
+  // Potentially UB.
+  return base + offset;
+}
+
+int main(int argc, char *argv[]) {
+  char *base;
+  unsigned long offset;
+
+  printf("Dummy\n");
+  // CHECK: Dummy
+
+  base = (char *)0;
+  offset = argc - 1;
+  (void)getelementpointer_inbounds_v0(base, offset);
+  // CHECK-UB: {{.*}}.cpp:[[@LINE-17]]:15: runtime error: applying non-zero offset 1 to null pointer is undefined
+
+  base = (char *)(intptr_t)(argc - 1);
+  offset = argc == 1 ? 0 : -(argc - 1);
+  (void)getelementpointer_inbounds_v1(base, offset);
+  // CHECK-UB: {{.*}}.cpp:[[@LINE-18]]:15: runtime error: subtracting integral value of non-null pointer 0x{{[0]*}}1 from pointer itself is undefined
+
+  return 0;
+}
Index: compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-summary.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-summary.cpp
@@ -0,0 +1,22 @@
+// RUN: %clangxx -fsanitize=nullptr-and-nonzero-offset %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
+// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
+
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+  char *base, *result;
+
+  base = (char *)0;
+  result = base + 1;
+  // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:17
+  // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: nullptr-with-nonzero-offset {{.*}}summary.cpp:[[@LINE-2]]:17
+
+  base = (char *)1;
+  result = base - 1;
+  // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:17
+  // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: nullptr-after-nonzero-offset {{.*}}summary.cpp:[[@LINE-2]]:17
+
+  return 0;
+}
Index: compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-constants.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan/TestCases/Pointer/nullptr-and-nonzero-offset-constants.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang   -x c   -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c   -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c   -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c   -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+
+// RUN: %clang   -x c++ -fsanitize=nullptr-and-nonzero-offset -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c++ -fsanitize=nullptr-and-nonzero-offset -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c++ -fsanitize=nullptr-and-nonzero-offset -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+// RUN: %clang   -x c++ -fsanitize=nullptr-and-nonzero-offset -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+  char *base, *result;
+
+  base = (char *)0;
+  result = base + 1;
+  // CHECK: {{.*}}.cpp:[[@LINE-1]]:17: runtime error: applying non-zero offset 1 to null pointer is undefined
+
+  base = (char *)1;
+  result = base - 1;
+  // CHECK: {{.*}}.cpp:[[@LINE-1]]:17: runtime error: subtracting integral value of non-null pointer 0x{{[0]*}}1 from pointer itself is undefined
+
+  return 0;
+}
Index: compiler-rt/test/ubsan/TestCases/Pointer/index-overflow.cpp
===================================================================
--- compiler-rt/test/ubsan/TestCases/Pointer/index-overflow.cpp
+++ compiler-rt/test/ubsan/TestCases/Pointer/index-overflow.cpp
@@ -1,7 +1,9 @@
 // RUN: %clangxx -fsanitize=pointer-overflow %s -o %t
-// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=ERR
+// RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=ERR2
+// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=ERR1
 // RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=SAFE
 // RUN: %run %t -1 2>&1 | FileCheck %s --check-prefix=SAFE
+// RUN: %run %t -2 2>&1 | FileCheck %s --check-prefix=SAFE
 
 #include <stdio.h>
 #include <stdint.h>
@@ -9,7 +11,8 @@
 
 int main(int argc, char *argv[]) {
   // SAFE-NOT: runtime error
-  // ERR: runtime error: pointer index expression with base {{.*}} overflowed to
+  // ERR2: runtime error: pointer index expression with base {{.*}} overflowed to
+  // ERR1: runtime error: subtracting integral value of non-null pointer 0x{{.*}} from pointer itself is undefined
 
   char *p = (char *)(UINTPTR_MAX);
 
Index: compiler-rt/lib/ubsan/ubsan_handlers.cpp
===================================================================
--- compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -691,14 +691,30 @@
                                       ValueHandle Result,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  ErrorType ET = ErrorType::PointerOverflow;
+  ErrorType ET;
+
+  if (Base == 0 && Result != 0)
+    ET = ErrorType::NullptrWithNonZeroOffset;
+  else if (Base != 0 && Result == 0)
+    ET = ErrorType::NullptrAfterNonZeroOffset;
+  else
+    ET = ErrorType::PointerOverflow;
 
   if (ignoreReport(Loc, Opts, ET))
     return;
 
   ScopedReport R(Opts, Loc, ET);
 
-  if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
+  if (ET == ErrorType::NullptrWithNonZeroOffset) {
+    Diag(Loc, DL_Error, ET,
+         "applying non-zero offset %0 to null pointer is undefined")
+        << Result;
+  } else if (ET == ErrorType::NullptrAfterNonZeroOffset) {
+    Diag(Loc, DL_Error, ET,
+         "subtracting integral value of non-null pointer %0 from pointer "
+         "itself is undefined")
+        << (void *)Base;
+  } else if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
     if (Base > Result)
       Diag(Loc, DL_Error, ET,
            "addition of unsigned offset to %0 overflowed to %1")
Index: compiler-rt/lib/ubsan/ubsan_checks.inc
===================================================================
--- compiler-rt/lib/ubsan/ubsan_checks.inc
+++ compiler-rt/lib/ubsan/ubsan_checks.inc
@@ -18,6 +18,8 @@
 
 UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined")
 UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
+UBSAN_CHECK(NullptrWithNonZeroOffset, "nullptr-with-nonzero-offset", "nullptr-and-nonzero-offset")
+UBSAN_CHECK(NullptrAfterNonZeroOffset, "nullptr-after-nonzero-offset", "nullptr-and-nonzero-offset")
 UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow")
 UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
 UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment")
Index: clang/test/Driver/fsanitize.c
===================================================================
--- clang/test/Driver/fsanitize.c
+++ clang/test/Driver/fsanitize.c
@@ -4,15 +4,15 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
 // 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
 // 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|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){19}"}}
+// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,nullptr-and-nonzero-offset,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,nullptr-and-nonzero-offset,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
 
 // 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|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
+// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){20}"}}
 
 // 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|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
 
 // RUN: %clang -target i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32
 // RUN: %clang -target i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32 --check-prefix=CHECK-UNDEFINED-WIN-CXX
@@ -23,7 +23,7 @@
 // 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-WIN-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),?){17}"}}
+// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
 
 // 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"
@@ -33,6 +33,21 @@
 // RUN: %clang -target %itanium_abi_triple -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER -implicit-check-not="-fsanitize-address-use-after-scope"
 // CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){8}"}}
 
+// RUN: %clang -fsanitize=pointer-offsetting %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-pointer-offsetting,CHECK-pointer-offsetting-RECOVER
+// RUN: %clang -fsanitize=pointer-offsetting -fsanitize-recover=pointer-offsetting %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-pointer-offsetting,CHECK-pointer-offsetting-RECOVER
+// RUN: %clang -fsanitize=pointer-offsetting -fno-sanitize-recover=pointer-offsetting %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-pointer-offsetting,CHECK-pointer-offsetting-NORECOVER
+// RUN: %clang -fsanitize=pointer-offsetting -fsanitize-trap=pointer-offsetting %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-pointer-offsetting,CHECK-pointer-offsetting-TRAP
+// CHECK-pointer-offsetting: "-fsanitize={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-RECOVER: "-fsanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-RECOVER-NOT: "-fno-sanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-RECOVER-NOT: "-fsanitize-trap={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-NORECOVER-NOT: "-fno-sanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}} // ???
+// CHECK-pointer-offsetting-NORECOVER-NOT: "-fsanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-NORECOVER-NOT: "-fsanitize-trap={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-TRAP: "-fsanitize-trap={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-TRAP-NOT: "-fsanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+// CHECK-pointer-offsetting-TRAP-NOT: "-fno-sanitize-recover={{((nullptr-and-nonzero-offset|pointer-overflow),?){2}"}}
+
 // RUN: %clang -fsanitize=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
 // RUN: %clang -fsanitize=implicit-conversion -fsanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
 // RUN: %clang -fsanitize=implicit-conversion -fno-sanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-NORECOVER
@@ -88,7 +103,7 @@
 // CHECK-FNO-SANITIZE-ALL: "-fsanitize=thread"
 
 // 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|nullptr-and-nonzero-offset|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){15}"}}
 
 // RUN: %clang -fsanitize=shift -fno-sanitize=shift-base %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSANITIZE-SHIFT-PARTIAL
 // CHECK-FSANITIZE-SHIFT-PARTIAL: "-fsanitize=shift-exponent"
@@ -359,7 +374,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|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
 // CHECK-NO-RECOVER-UBSAN-NOT: sanitize-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
@@ -778,7 +793,7 @@
 // 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),?){17}"}}
+// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|nullptr-and-nonzero-offset|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
 // CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=function -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MINIMAL
Index: clang/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -x c++ -fsanitize=nullptr-and-nonzero-offset -fno-sanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+
+#include <stdint.h>
+
+struct S {
+  int x, y;
+};
+
+uintptr_t get_offset_of_y() {
+  // CHECK:      define i64 @{{.*}}() {{.*}} {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64)
+  // CHECK-NEXT: }
+  return ((uintptr_t)(&(((S *)nullptr)->y)));
+}
+
+uintptr_t get_offset_of_y_via_builtin() {
+  // CHECK:      define i64 @{{.*}}() {{.*}} {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i64 4
+  // CHECK-NEXT: }
+  return __builtin_offsetof(S, y);
+}
Index: clang/test/CodeGen/catch-pointer-overflow.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/catch-pointer-overflow.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
+// RUN: %clang_cc1 -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } }
+
+// We just don't know, can be bad, can be ok.
+char *var_var(char *base, unsigned long offset) {
+  // CHECK:                           define i8* @var_var(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[GEP_DID_NOT_OVERFLOW]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+#line 100
+  return base + offset;
+}
Index: clang/test/CodeGen/catch-pointer-offsetting.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/catch-pointer-offsetting.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset,pointer-overflow -fno-sanitize-recover=nullptr-and-nonzero-offset,pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset,pointer-overflow -fsanitize-recover=nullptr-and-nonzero-offset,pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset,pointer-overflow -fsanitize-trap=nullptr-and-nonzero-offset,pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } }
+
+// We just don't know, can be bad, can be ok.
+char *var_var(char *base, unsigned long offset) {
+  // CHECK:                           define i8* @var_var(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_IS_NULLPTR:.*]] = icmp eq i8* %[[BASE_RELOADED]], null, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NULLPTR]], %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+#line 100
+  return base + offset;
+}
Index: clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -0,0 +1,351 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset -fno-sanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset -fsanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset -fsanitize-trap=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// If the base pointer evaluates to a null pointer value, the only valid
+// pointer this inbounds GEP can produce is also a null pointer.
+// Likewise, if we have non-zero base pointer, we can not get null pointer
+// as a result, so the offset can not be -int(BasePtr).
+
+// So in other words, the offset can not change "null status" of the pointer,
+// as in if the pointer was null, it can not become non-null, and vice verse,
+// if it was non-null, it can not become null.
+
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_900:.*]] = {{.*}}, i32 900, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1100:.*]] = {{.*}}, i32 1100, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1200:.*]] = {{.*}}, i32 1200, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1300:.*]] = {{.*}}, i32 1300, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1500:.*]] = {{.*}}, i32 1500, i32 15 } }
+// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1600:.*]] = {{.*}}, i32 1600, i32 15 } }
+
+// We just don't know, can be bad, can be ok.
+char *var_var(char *base, unsigned long offset) {
+  // CHECK: define i8* @var_var(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_IS_NULLPTR:.*]] = icmp eq i8* %[[BASE_RELOADED]], null, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NULLPTR]], %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+#line 100
+  return base + offset;
+}
+
+// Offset is zero, so okay regardless of the pointer.
+char *var_zero_OK(char *base) {
+  // CHECK: define i8* @var_zero_OK(i8* %[[BASE:.*]])
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 0
+  // CHECK-NEXT: ret i8* %[[ADD_PTR]]
+  static const unsigned long offset = 0;
+#line 200
+  return base + offset;
+}
+
+// Bad if pointer is non-null.
+char *var_one(char *base) {
+  // CHECK:                           define i8* @var_one(i8* %[[BASE:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT:                        store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 1
+  // CHECK-SANITIZE-NEXT:               %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_IS_NULLPTR:.*]] = icmp eq i8* %[[BASE_RELOADED]], null, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NULLPTR]], %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+  static const unsigned long offset = 1;
+#line 300
+  return base + offset;
+}
+
+// Bad if pointer is one.
+char *var_allones(char *base) {
+  // CHECK:                           define i8* @var_allones(i8* %[[BASE:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[BASE_ADDR:.*]] = alloca i8*, align 8
+  // CHECK-NEXT:                        store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 -1
+  // CHECK-SANITIZE-NEXT:               %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], -1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BASE_IS_NULLPTR:.*]] = icmp eq i8* %[[BASE_RELOADED]], null, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NULLPTR]], %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+  static const unsigned long offset = -1;
+#line 400
+  return base + offset;
+}
+
+//------------------------------------------------------------------------------
+
+// Bad if offset is non-zero.
+char *nullptr_var(unsigned long offset) {
+  // CHECK:                           define i8* @nullptr_var(i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* null, i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 0, %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 true, %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 0, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 0, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+  static char *const base = (char *)0;
+#line 500
+  return base + offset;
+}
+
+// Pointer and offset are both zero, so all good.
+char *nullptr_zero_OK() {
+  // CHECK:      define i8* @nullptr_zero_OK()
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i8* null
+  static char *const base = (char *)0;
+  static const unsigned long offset = 0;
+#line 600
+  return base + offset;
+}
+
+// Null pointer and non-zero offset. Bad.
+char *nullptr_one_BAD() {
+  // CHECK:                           define i8* @nullptr_one_BAD()
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 icmp eq (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64), i64 0), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 0, i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 0, i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* null, i64 1)
+  static char *const base = (char *)0;
+  static const unsigned long offset = 1;
+#line 700
+  return base + offset;
+}
+
+// Null pointer and non-zero offset. Bad.
+char *nullptr_allones_BAD() {
+  // CHECK:                           define i8* @nullptr_allones_BAD()
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 icmp eq (i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1), i64 0), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 0, i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 0, i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* null, i64 -1)
+  static char *const base = (char *)0;
+  static const unsigned long offset = -1;
+#line 800
+  return base + offset;
+}
+
+//------------------------------------------------------------------------------
+
+// Pointer is one, bad if offset is all-ones.
+char *one_var(unsigned long offset) {
+  // CHECK:                           define i8* @one_var(i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* inttoptr (i64 1 to i8*), i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 1, %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 icmp eq (i8* inttoptr (i64 1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_900]] to i8*), i64 1, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_900]] to i8*), i64 1, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+  static char *const base = (char *)1;
+#line 900
+  return base + offset;
+}
+
+// Zero offset, all good.
+char *one_zero_OK() {
+  // CHECK:      define i8* @one_zero_OK()
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i8* inttoptr (i64 1 to i8*)
+  static char *const base = (char *)1;
+  static const unsigned long offset = 0;
+#line 1000
+  return base + offset;
+}
+
+// Both pointer and offset are one, OK, but we can't prove that at codegen.
+char *one_one_OK() {
+  // CHECK:                           define i8* @one_one_OK()
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 xor (i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp eq (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1100]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1100]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1)
+  static char *const base = (char *)1;
+  static const unsigned long offset = 1;
+#line 1100
+  return base + offset;
+}
+
+// Pointer is one, offset is all-ones. Bad.
+char *one_allones_BAD() {
+  // CHECK:                           define i8* @one_allones_BAD()
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 xor (i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp eq (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1200]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1200]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1)
+  static char *const base = (char *)1;
+  static const unsigned long offset = -1;
+#line 1200
+  return base + offset;
+}
+
+//------------------------------------------------------------------------------
+
+// Pointer is all-ones. Bad if offset is one.
+char *allones_var(unsigned long offset) {
+  // CHECK:                           define i8* @allones_var(i64 %[[OFFSET:.*]])
+  // CHECK-NEXT:                      [[ENTRY:.*]]:
+  // CHECK-NEXT:                        %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:                        store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:                        %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* inttoptr (i64 -1 to i8*), i64 %[[OFFSET_RELOADED]]
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP:.*]] = add i64 -1, %[[COMPUTED_OFFSET]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[COMPUTED_GEP_IS_NULL:.*]] = icmp eq i64 %[[COMPUTED_GEP]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT:               %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 icmp eq (i8* inttoptr (i64 -1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NULL]], !nosanitize
+  // CHECK-SANITIZE-NEXT:               br i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1300]] to i8*), i64 -1, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1300]] to i8*), i64 -1, i64 %[[COMPUTED_GEP]])
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* %[[ADD_PTR]]
+  static char *const base = (char *)-1;
+#line 1300
+  return base + offset;
+}
+
+// Offset is zero, all good.
+char *allones_zero_OK() {
+  // CHECK:      define i8* @allones_zero_OK()
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i8* inttoptr (i64 -1 to i8*)
+  static char *const base = (char *)-1;
+  static const unsigned long offset = 0;
+#line 1400
+  return base + offset;
+}
+
+// Pointer is all-ones, offset is one. Bad.
+char *allones_one_BAD() {
+  // CHECK: define i8* @allones_one_BAD()
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 xor (i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp eq (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1500]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1500]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1)
+  static char *const base = (char *)-1;
+  static const unsigned long offset = 1;
+#line 1500
+  return base + offset;
+}
+
+// All-ones pointer and offset. OK, but we can't prove that at codegen.
+char *allones_allones_OK() {
+  // CHECK: define i8* @allones_allones_OK()
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-SANITIZE-NEXT:               br i1 xor (i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp eq (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE:                  [[HANDLER_POINTER_OVERFLOW]]:
+  // CHECK-SANITIZE-NORECOVER-NEXT:     call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1600]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1))
+  // CHECK-SANITIZE-RECOVER-NEXT:       call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1600]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1))
+  // CHECK-SANITIZE-TRAP-NEXT:          call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT:   unreachable, !nosanitize
+  // CHECK-SANITIZE:                  [[CONT]]:
+  // CHECK-NEXT:                        ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1)
+  static char *const base = (char *)-1;
+  static const unsigned long offset = -1;
+#line 1600
+  return base + offset;
+}
Index: clang/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -x c   -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -x c   -fsanitize=nullptr-and-nonzero-offset -fno-sanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+
+// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -x c++ -fsanitize=nullptr-and-nonzero-offset -fno-sanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+
+#include <stdint.h>
+
+struct S {
+  int x, y;
+};
+
+uintptr_t get_offset_of_y() {
+  // CHECK:      define i64 @{{.*}}() {{.*}} {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   ret i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64)
+  // CHECK-NEXT: }
+  return ((uintptr_t)(&(((struct S *)0)->y)));
+}
Index: clang/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -fsanitize=nullptr-and-nonzero-offset -fsanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-INVALID-PTR
+// RUN: %clang_cc1 -fno-delete-null-pointer-checks -fsanitize=nullptr-and-nonzero-offset -fsanitize-recover=nullptr-and-nonzero-offset -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-VALID-PTR
+
+// CHECK-LABEL: @baseline
+char *baseline(char *base, unsigned long offset) {
+  // CHECK-NULL-IS-INVALID-PTR: call void @__ubsan_handle_pointer_overflow(
+  return base + offset;
+}
+
+// CHECK-LABEL: @blacklist_0
+__attribute__((no_sanitize("undefined"))) char *blacklist_0(char *base, unsigned long offset) {
+  return base + offset;
+}
+
+// CHECK-LABEL: @blacklist_1
+__attribute__((no_sanitize("pointer-offsetting"))) char *blacklist_1(char *base, unsigned long offset) {
+  return base + offset;
+}
+
+// CHECK-LABEL: @blacklist_2
+__attribute__((no_sanitize("nullptr-and-nonzero-offset"))) char *blacklist_2(char *base, unsigned long offset) {
+  return base + offset;
+}
+
+// CHECK-LABEL: @dont_ignore_volatile_ptrs
+char *volatile dont_ignore_volatile_ptrs(char *volatile base, unsigned long offset) {
+  // CHECK-NULL-IS-INVALID-PTR: call void @__ubsan_handle_pointer_overflow(
+  return base + offset;
+}
+
+// CHECK-LABEL: @dont_ignore_volatiles
+volatile char *dont_ignore_volatiles(volatile char *base, unsigned long offset) {
+  // CHECK-NULL-IS-INVALID-PTR: call void @__ubsan_handle_pointer_overflow(
+  return base + offset;
+}
+
+// CHECK-LABEL: @ignore_non_default_address_space
+__attribute__((address_space(1))) char *ignore_non_default_address_space(__attribute__((address_space(1))) char *base, unsigned long offset) {
+  return base + offset;
+}
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -4064,6 +4064,13 @@
   /// operation is a subtraction.
   enum { NotSubtraction = false, IsSubtraction = true };
 
+  /// Evaluate given GEPVal, which is either an inbounds GEP, or a constant,
+  /// and compute the total offset it applies from it's base pointer BasePtr.
+  /// Returns offset in bytes and a boolean flag whether an overflow happened
+  /// during evaluation.
+  std::pair<llvm::Value * /*TotalOffset*/, llvm::Value * /*OffsetOverflows*/>
+  EmitGEPOffsetInBytes(llvm::Value *BasePtr, llvm::Value *GEPVal);
+
   /// Same as IRBuilder::CreateInBoundsGEP, but additionally emits a check to
   /// detect undefined behavior when the pointer overflow sanitizer is enabled.
   /// \p SignedIndices indicates whether any of the GEP indices are signed.
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -27,6 +27,7 @@
 #include "clang/Basic/FixedPoint.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
@@ -4533,33 +4534,32 @@
   llvm_unreachable("Unhandled compound assignment operator");
 }
 
-Value *CodeGenFunction::EmitCheckedInBoundsGEP(Value *Ptr,
-                                               ArrayRef<Value *> IdxList,
-                                               bool SignedIndices,
-                                               bool IsSubtraction,
-                                               SourceLocation Loc,
-                                               const Twine &Name) {
-  Value *GEPVal = Builder.CreateInBoundsGEP(Ptr, IdxList, Name);
+std::pair<llvm::Value * /*TotalOffset*/, llvm::Value * /*OffsetOverflows*/>
+CodeGenFunction::EmitGEPOffsetInBytes(Value *BasePtr, Value *GEPVal) {
+  const auto &DL = CGM.getDataLayout();
 
-  // If the pointer overflow sanitizer isn't enabled, do nothing.
-  if (!SanOpts.has(SanitizerKind::PointerOverflow))
-    return GEPVal;
+  // The total (signed) byte offset for the GEP.
+  llvm::Value *TotalOffset = nullptr;
 
-  // If the GEP has already been reduced to a constant, leave it be.
-  if (isa<llvm::Constant>(GEPVal))
-    return GEPVal;
-
-  // Only check for overflows in the default address space.
-  if (GEPVal->getType()->getPointerAddressSpace())
-    return GEPVal;
+  // Was the GEP has already been reduced to a constant?
+  if (isa<llvm::Constant>(GEPVal)) {
+    // Compute the offset by casting both pointers to integers and subtracting:
+    // GEPVal = BasePtr + ptr(Offset) <--> Offset = int(GEPVal) - int(BasePtr)
+    Value *BasePtr_int =
+        Builder.CreatePtrToInt(BasePtr, DL.getIntPtrType(BasePtr->getType()));
+    Value *GEPVal_int =
+        Builder.CreatePtrToInt(GEPVal, DL.getIntPtrType(GEPVal->getType()));
+    TotalOffset = Builder.CreateSub(GEPVal_int, BasePtr_int);
+    return {TotalOffset, /*OffsetOverflows=*/Builder.getFalse()};
+  }
 
   auto *GEP = cast<llvm::GEPOperator>(GEPVal);
+  assert(GEP->getPointerOperand() == BasePtr &&
+         "BasePtr must be the the base of the GEP.");
   assert(GEP->isInBounds() && "Expected inbounds GEP");
 
-  SanitizerScope SanScope(this);
-  auto &VMContext = getLLVMContext();
-  const auto &DL = CGM.getDataLayout();
   auto *IntPtrTy = DL.getIntPtrType(GEP->getPointerOperandType());
+  auto &VMContext = getLLVMContext();
 
   // Grab references to the signed add/mul overflow intrinsics for intptr_t.
   auto *Zero = llvm::ConstantInt::getNullValue(IntPtrTy);
@@ -4568,8 +4568,6 @@
   auto *SMulIntrinsic =
       CGM.getIntrinsic(llvm::Intrinsic::smul_with_overflow, IntPtrTy);
 
-  // The total (signed) byte offset for the GEP.
-  llvm::Value *TotalOffset = nullptr;
   // The offset overflow flag - true if the total offset overflows.
   llvm::Value *OffsetOverflows = Builder.getFalse();
 
@@ -4627,41 +4625,101 @@
       TotalOffset = eval(BO_Add, TotalOffset, LocalOffset);
   }
 
-  // Common case: if the total offset is zero, don't emit a check.
-  if (TotalOffset == Zero)
+  return {TotalOffset, OffsetOverflows};
+}
+
+Value *
+CodeGenFunction::EmitCheckedInBoundsGEP(Value *Ptr, ArrayRef<Value *> IdxList,
+                                        bool SignedIndices, bool IsSubtraction,
+                                        SourceLocation Loc, const Twine &Name) {
+  Value *GEPVal = Builder.CreateInBoundsGEP(Ptr, IdxList, Name);
+
+  // If none of the appropriate sanitizers are enabled, do nothing.
+  if (!SanOpts.hasOneOf(SanitizerKind::PointerOffsetting))
     return GEPVal;
 
+  const auto &DL = CGM.getDataLayout();
+
+  SanitizerScope SanScope(this);
+  llvm::Type *PtrTy = Ptr->getType();
+  llvm::Type *IntPtrTy = DL.getIntPtrType(PtrTy);
+
+  // The total (signed) byte offset for the GEP.
+  llvm::Value *TotalOffset;
+  // The offset overflow flag - true if the total offset overflows.
+  llvm::Value *OffsetOverflows;
+
+  std::tie(TotalOffset, OffsetOverflows) = EmitGEPOffsetInBytes(Ptr, GEPVal);
+
+  llvm::Value *Zero = llvm::ConstantInt::getNullValue(IntPtrTy);
+
+  // Common case: if the total offset is zero, don't emit a check.
+  if (TotalOffset == Zero) {
+    assert(OffsetOverflows == Builder.getFalse() &&
+           "Assuming that whereever we constant-fold the offset to zero, "
+           "we also *know* we had no overflow along the way.");
+    return GEPVal;
+  }
+
   // Now that we've computed the total offset, add it to the base pointer (with
   // wrapping semantics).
-  auto *IntPtr = Builder.CreatePtrToInt(GEP->getPointerOperand(), IntPtrTy);
+  auto *IntPtr = Builder.CreatePtrToInt(Ptr, IntPtrTy);
   auto *ComputedGEP = Builder.CreateAdd(IntPtr, TotalOffset);
 
-  // The GEP is valid if:
-  // 1) The total offset doesn't overflow, and
-  // 2) The sign of the difference between the computed address and the base
-  // pointer matches the sign of the total offset.
-  llvm::Value *ValidGEP;
-  auto *NoOffsetOverflow = Builder.CreateNot(OffsetOverflows);
-  if (SignedIndices) {
-    auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr);
-    auto *PosOrZeroOffset = Builder.CreateICmpSGE(TotalOffset, Zero);
-    llvm::Value *NegValid = Builder.CreateICmpULT(ComputedGEP, IntPtr);
-    ValidGEP = Builder.CreateAnd(
-        Builder.CreateSelect(PosOrZeroOffset, PosOrZeroValid, NegValid),
-        NoOffsetOverflow);
-  } else if (!SignedIndices && !IsSubtraction) {
-    auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr);
-    ValidGEP = Builder.CreateAnd(PosOrZeroValid, NoOffsetOverflow);
-  } else {
-    auto *NegOrZeroValid = Builder.CreateICmpULE(ComputedGEP, IntPtr);
-    ValidGEP = Builder.CreateAnd(NegOrZeroValid, NoOffsetOverflow);
+  llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 2> Checks;
+
+  // Perform nullptr-and-offset check unless the nullptr is defined.
+  if (SanOpts.has(SanitizerKind::NullptrAndNonZeroOffset) &&
+      !NullPointerIsDefined(Builder.GetInsertBlock()->getParent(),
+                            PtrTy->getPointerAddressSpace())) {
+    // If the base pointer evaluates to a null pointer value, the only valid
+    // pointer this inbounds GEP can produce is also a null pointer, so the
+    // offset must also evaluate to zero.
+    // Likewise, if we have non-zero base pointer, we can not get null pointer
+    // as a result, so the offset can not be -intptr_t(BasePtr).
+    // In other words, both bointers are either null, or both are non-null,
+    // or the behaviour is undefined.
+    auto *BaseIsNullptr = Builder.CreateIsNull(Ptr);
+    auto *ResultIsNullptr = Builder.CreateIsNull(ComputedGEP);
+    Checks.emplace_back(Builder.CreateICmpEQ(BaseIsNullptr, ResultIsNullptr),
+                        SanitizerKind::NullptrAndNonZeroOffset);
   }
 
+  // Check for overflows unless the GEP got constant-folded,
+  // and only in the default address space
+  if (SanOpts.has(SanitizerKind::PointerOverflow) &&
+      !isa<llvm::Constant>(GEPVal) && PtrTy->getPointerAddressSpace() == 0) {
+    // The GEP is valid if:
+    // 1) The total offset doesn't overflow, and
+    // 2) The sign of the difference between the computed address and the base
+    // pointer matches the sign of the total offset.
+    llvm::Value *ValidGEP;
+    auto *NoOffsetOverflow = Builder.CreateNot(OffsetOverflows);
+    if (SignedIndices) {
+      auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr);
+      auto *PosOrZeroOffset = Builder.CreateICmpSGE(TotalOffset, Zero);
+      llvm::Value *NegValid = Builder.CreateICmpULT(ComputedGEP, IntPtr);
+      ValidGEP = Builder.CreateAnd(
+          Builder.CreateSelect(PosOrZeroOffset, PosOrZeroValid, NegValid),
+          NoOffsetOverflow);
+    } else if (!SignedIndices && !IsSubtraction) {
+      auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr);
+      ValidGEP = Builder.CreateAnd(PosOrZeroValid, NoOffsetOverflow);
+    } else {
+      auto *NegOrZeroValid = Builder.CreateICmpULE(ComputedGEP, IntPtr);
+      ValidGEP = Builder.CreateAnd(NegOrZeroValid, NoOffsetOverflow);
+    }
+    Checks.emplace_back(ValidGEP, SanitizerKind::PointerOverflow);
+  }
+
+  // Did we end up producing any checks?
+  if (Checks.empty())
+    return GEPVal;
+
   llvm::Constant *StaticArgs[] = {EmitCheckSourceLocation(Loc)};
   // Pass the computed GEP to the runtime to avoid emitting poisoned arguments.
   llvm::Value *DynamicArgs[] = {IntPtr, ComputedGEP};
-  EmitCheck(std::make_pair(ValidGEP, SanitizerKind::PointerOverflow),
-            SanitizerHandler::PointerOverflow, StaticArgs, DynamicArgs);
+  EmitCheck(Checks, SanitizerHandler::PointerOverflow, StaticArgs, DynamicArgs);
 
   return GEPVal;
 }
Index: clang/include/clang/Basic/Sanitizers.def
===================================================================
--- clang/include/clang/Basic/Sanitizers.def
+++ clang/include/clang/Basic/Sanitizers.def
@@ -94,7 +94,10 @@
 SANITIZER_GROUP("nullability", Nullability,
                 NullabilityArg | NullabilityAssign | NullabilityReturn)
 SANITIZER("object-size", ObjectSize)
+SANITIZER("nullptr-and-nonzero-offset", NullptrAndNonZeroOffset)
 SANITIZER("pointer-overflow", PointerOverflow)
+SANITIZER_GROUP("pointer-offsetting", PointerOffsetting,
+                NullptrAndNonZeroOffset | PointerOverflow)
 SANITIZER("return", Return)
 SANITIZER("returns-nonnull-attribute", ReturnsNonnullAttribute)
 SANITIZER("shift-base", ShiftBase)
@@ -135,9 +138,9 @@
                 Alignment | Bool | Builtin | ArrayBounds | Enum |
                     FloatCastOverflow |
                     IntegerDivideByZero | NonnullAttribute | Null | ObjectSize |
-                    PointerOverflow | Return | ReturnsNonnullAttribute | Shift |
-                    SignedIntegerOverflow | Unreachable | VLABound | Function |
-                    Vptr)
+                    PointerOffsetting | Return | ReturnsNonnullAttribute |
+                    Shift | SignedIntegerOverflow | Unreachable | VLABound |
+                    Function | Vptr)
 
 // -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
 SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
Index: clang/docs/UndefinedBehaviorSanitizer.rst
===================================================================
--- clang/docs/UndefinedBehaviorSanitizer.rst
+++ clang/docs/UndefinedBehaviorSanitizer.rst
@@ -129,6 +129,9 @@
      invalid pointers. These checks are made in terms of
      ``__builtin_object_size``, and consequently may be able to detect more
      problems at higher optimization levels.
+  -  ``-fsanitize=nullptr-and-nonzero-offset``: Applying non-zero offset to a
+     pointer, where either the original pointer is ``nullptr``, or the computed
+     pointer is ``nullptr``.
   -  ``-fsanitize=pointer-overflow``: Performing pointer arithmetic which
      overflows.
   -  ``-fsanitize=return``: In C++, reaching the end of a
@@ -171,6 +174,8 @@
      ``implicit-conversion``, and the ``nullability-*`` group of checks.
   -  ``-fsanitize=undefined-trap``: Deprecated alias of
      ``-fsanitize=undefined``.
+  -  ``-fsanitize=pointer-offsetting``: Enables ``nullptr-and-nonzero-offset``
+     and ``pointer-overflow``.
   -  ``-fsanitize=implicit-integer-truncation``: Catches lossy integral
      conversions. Enables ``implicit-signed-integer-truncation`` and
      ``implicit-unsigned-integer-truncation``.
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -56,7 +56,15 @@
 Non-comprehensive list of changes in this release
 -------------------------------------------------
 
-- ...
+* As per C++ Standard ``[expr.add]``, applying non-zero offset to ``nullptr``
+  (or making non-``nullptr`` a ``nullptr``, by subtracting pointer's integral
+  value from the pointer itself) is undefined behaviour.
+  Since `r369789 <https://reviews.llvm.org/rL369789>`_
+  (`D66608 <https://reviews.llvm.org/D66608>`_ ``[InstCombine] icmp eq/ne (gep
+  inbounds P, Idx..), null -> icmp eq/ne P, null``) LLVM middle-end uses those
+  guarantees for transformations. If the source contains such UB's, said code
+  may now be miscompiled. Undefined Behaviour Sanitizer has gained
+  ``-fsanitize=nullptr-and-nonzero-offset`` check that will catch this UB.
 
 
 New Compiler Flags
@@ -203,7 +211,41 @@
 Undefined Behavior Sanitizer (UBSan)
 ------------------------------------
 
-- ...
+- * ``nullptr-and-nonzero-offset`` check was added to catch the cases where
+    a non-zero offset being applied, either to a ``nullptr``, or the result
+    of applying of the offset is a ``nullptr``.
+    As per C++ Standard ``[expr.add]`` that is undefined behaviour.
+
+    .. code-block:: c++
+
+      #include <cstdint> // for intptr_t
+
+      static char *getelementpointer_inbounds(char *base, unsigned long offset) {
+        // Potentially UB.
+        return base + offset;
+      }
+
+      char *getelementpointer_unsafe(char *base, unsigned long offset) {
+        // Always apply offset. UB if base is ``nullptr`` and ``offset`` is not
+        // zero, or if ``base`` is non-``nullptr`` and ``offset`` is
+        // ``-reinterpret_cast<intptr_t>(base)``.
+        return getelementpointer_inbounds(base, offset);
+      }
+
+      char *getelementpointer_safe(char *base, unsigned long offset) {
+        // Cast pointer to integer, perform usual arithmetic addition,
+        // and cast to pointer. This is legal.
+        char *computed =
+            reinterpret_cast<char *>(reinterpret_cast<intptr_t>(base) + offset);
+        // If either the pointer becomes non-``nullptr``, or becomes
+        // ``nullptr``, we must use ``computed`` result.
+        if (((base == nullptr) && (computed != nullptr)) ||
+            ((base != nullptr) && (computed == nullptr)))
+          return computed;
+        // Else we can use ``getelementpointer_inbounds()``.
+        return getelementpointer_inbounds(base, offset);
+      }
+
 
 Core Analysis Improvements
 ==========================
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to