r227982 - Implement jump scope SEHmantic analysis.

Nico Weber nicolasweber at gmx.de
Tue Feb 3 09:06:09 PST 2015


Author: nico
Date: Tue Feb  3 11:06:08 2015
New Revision: 227982

URL: http://llvm.org/viewvc/llvm-project?rev=227982&view=rev
Log:
Implement jump scope SEHmantic analysis.

Thou shall not jump into SEH blocks. Jumping out of SEH __try and __excepts
is A-ok. Jumping out of __finally blocks is B-ok (msvc doesn't error about it,
but warns that it has undefined behavior).

I've checked that clang's behavior with this patch matches msvc's behavior.
We don't have the warning on jumping out of a __finally yet, see the FIXME
in the test. clang also currently crashes on codegen for a jump out of a
__finally block, see PR22414 comment 7.

I also added a few tests for the interaction of indirect jumps and SEH blocks.
MSVC doesn't support indirect jumps, so there's no way to know if clang behave
the same way as msvc here.  clang's behavior with this patch does make sense
to me, but maybe it could be argued that it should be more permissive (see
FIXME in the indirect jump tests -- shout if you have an opinion on this).

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/JumpDiagnostics.cpp
    cfe/trunk/test/SemaCXX/scope-check.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=227982&r1=227981&r2=227982&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Feb  3 11:06:08 2015
@@ -4291,6 +4291,12 @@ def note_protected_by_cxx_try : Note<
   "jump bypasses initialization of try block">;
 def note_protected_by_cxx_catch : Note<
   "jump bypasses initialization of catch block">;
+def note_protected_by_seh_try : Note<
+  "jump bypasses initialization of __try block">;
+def note_protected_by_seh_except : Note<
+  "jump bypasses initialization of __except block">;
+def note_protected_by_seh_finally : Note<
+  "jump bypasses initialization of __finally block">;
 def note_protected_by___block : Note<
   "jump bypasses setup of __block variable">;
 def note_protected_by_objc_ownership : Note<
@@ -4323,6 +4329,12 @@ def note_exits_cxx_try : Note<
   "jump exits try block">;
 def note_exits_cxx_catch : Note<
   "jump exits catch block">;
+def note_exits_seh_try : Note<
+  "jump exits __try block">;
+def note_exits_seh_except : Note<
+  "jump exits __except block">;
+def note_exits_seh_finally : Note<
+  "jump exits __finally block">;
 def note_exits_objc_autoreleasepool : Note<
   "jump exits autoreleasepool block">;
 def note_exits_objc_ownership : Note<

Modified: cfe/trunk/lib/Sema/JumpDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/JumpDiagnostics.cpp?rev=227982&r1=227981&r2=227982&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/JumpDiagnostics.cpp (original)
+++ cfe/trunk/lib/Sema/JumpDiagnostics.cpp Tue Feb  3 11:06:08 2015
@@ -338,9 +338,35 @@ void JumpScopeChecker::BuildScopeInforma
     return;
   }
 
-  case Stmt::SEHTryStmtClass:
-    // FIXME: Implement jump diagnostics for bad SEH jumps.
-    break;
+  case Stmt::SEHTryStmtClass: {
+    SEHTryStmt *TS = cast<SEHTryStmt>(S);
+    unsigned newParentScope;
+    Scopes.push_back(GotoScope(ParentScope,
+                               diag::note_protected_by_seh_try,
+                               diag::note_exits_seh_try,
+                               TS->getSourceRange().getBegin()));
+    if (Stmt *TryBlock = TS->getTryBlock())
+      BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1));
+
+    // Jump from __except or __finally into the __try are not allowed either.
+    if (SEHExceptStmt *Except = TS->getExceptHandler()) {
+      Scopes.push_back(GotoScope(ParentScope,
+                                 diag::note_protected_by_seh_except,
+                                 diag::note_exits_seh_except,
+                                 Except->getSourceRange().getBegin()));
+      BuildScopeInformation(Except->getBlock(), 
+                            (newParentScope = Scopes.size()-1));
+    } else if (SEHFinallyStmt *Finally = TS->getFinallyHandler()) {
+      Scopes.push_back(GotoScope(ParentScope,
+                                 diag::note_protected_by_seh_finally,
+                                 diag::note_exits_seh_finally,
+                                 Finally->getSourceRange().getBegin()));
+      BuildScopeInformation(Finally->getBlock(), 
+                            (newParentScope = Scopes.size()-1));
+    }
+
+    return;
+  }
 
   default:
     break;

Modified: cfe/trunk/test/SemaCXX/scope-check.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/scope-check.cpp?rev=227982&r1=227981&r2=227982&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/scope-check.cpp (original)
+++ cfe/trunk/test/SemaCXX/scope-check.cpp Tue Feb  3 11:06:08 2015
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fcxx-exceptions %s -Wno-unreachable-code
-// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fcxx-exceptions -std=gnu++11 %s -Wno-unreachable-code
+// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fcxx-exceptions -fms-extensions %s -Wno-unreachable-code
+// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fcxx-exceptions -fms-extensions -std=gnu++11 %s -Wno-unreachable-code
 
 namespace testInvalid {
 Invalid inv; // expected-error {{unknown type name}}
@@ -441,3 +441,192 @@ namespace test_recovery {
     }
   }
 }
+
+namespace seh {
+
+// Jumping into SEH try blocks is not permitted.
+
+void jump_into_except() {
+  goto into_try_except_try; // expected-error {{cannot jump from this goto statement to its label}}
+  __try { // expected-note {{jump bypasses initialization of __try block}}
+  into_try_except_try:
+    ;
+  } __except(0) {
+  }
+
+  goto into_try_except_except; // expected-error {{cannot jump from this goto statement to its label}}
+  __try {
+  } __except(0) { // expected-note {{jump bypasses initialization of __except block}}
+  into_try_except_except:
+    ;
+  }
+}
+
+void jump_into_finally() {
+  goto into_try_except_try; // expected-error {{cannot jump from this goto statement to its label}}
+  __try { // expected-note {{jump bypasses initialization of __try block}}
+  into_try_except_try:
+    ;
+  } __finally {
+  }
+
+  goto into_try_except_finally; // expected-error {{cannot jump from this goto statement to its label}}
+  __try {
+  } __finally { // expected-note {{jump bypasses initialization of __finally block}}
+  into_try_except_finally:
+    ;
+  }
+}
+
+// Jumping out of SEH try blocks ok in general. (Jumping out of a __finally
+// has undefined behavior.)
+
+void jump_out_of_except() {
+  __try {
+    goto out_of_except_try;
+  } __except(0) {
+  }
+out_of_except_try:
+  ;
+
+  __try {
+  } __except(0) {
+    goto out_of_except_except;
+  }
+out_of_except_except:
+  ;
+}
+
+void jump_out_of_finally() {
+  __try {
+  goto out_of_finally_try;
+  } __finally {
+  }
+out_of_finally_try:
+  ;
+
+  __try {
+  } __finally {
+    // FIXME: This should warn that jumping out of __finally has undefined
+    // behavior.
+    // FIXME: Once that warns, check that
+    //   __try { __try {} __finally { __leave; } } __except (0) {}
+    // warns in the same way.
+    goto out_of_finally_finally;
+  }
+out_of_finally_finally:
+  ;
+}
+
+// Jumping between protected scope and handler is not permitted.
+
+void jump_try_except() {
+  __try {
+    goto from_try_to_except; // expected-error {{cannot jump from this goto statement to its label}}
+  } __except(0) { // expected-note {{jump bypasses initialization of __except block}}
+  from_try_to_except:
+    ;
+  }
+
+  __try { // expected-note {{jump bypasses initialization of __try block}}
+  from_except_to_try:
+    ;
+  } __except(0) {
+    goto from_except_to_try; // expected-error {{cannot jump from this goto statement to its label}}
+  }
+}
+
+void jump_try_finally() {
+  __try {
+    goto from_try_to_finally; // expected-error {{cannot jump from this goto statement to its label}}
+  } __finally { // expected-note {{jump bypasses initialization of __finally block}}
+  from_try_to_finally:
+    ;
+  }
+
+  __try { // expected-note {{jump bypasses initialization of __try block}}
+  from_finally_to_try:
+    ;
+  } __finally {
+    goto from_finally_to_try; // expected-error {{cannot jump from this goto statement to its label}}
+  }
+}
+
+void nested() {
+  // These are not permitted.
+  __try {
+    __try {
+    } __finally {
+      goto outer_except; // expected-error {{cannot jump from this goto statement to its label}}
+    }
+  } __except(0) { // expected-note {{jump bypasses initialization of __except bloc}}
+  outer_except:
+    ;
+  }
+
+  __try {
+    __try{
+    } __except(0) {
+      goto outer_finally; // expected-error {{cannot jump from this goto statement to its label}}
+    }
+  } __finally { // expected-note {{jump bypasses initialization of __finally bloc}}
+  outer_finally:
+    ;
+  }
+
+  // These are permitted.
+  __try {
+    __try {
+    } __finally {
+      // FIXME: This should warn that jumping out of __finally has undefined
+      // behavior.
+      goto after_outer_except;
+    }
+  } __except(0) {
+  }
+after_outer_except:
+  ;
+
+  __try {
+    __try{
+    } __except(0) {
+      goto after_outer_finally;
+    }
+  } __finally {
+  }
+after_outer_finally:
+  ;
+}
+
+// This section is academic, as MSVC doesn't support indirect gotos.
+
+void indirect_jumps(void **ip) {
+  static void *ips[] = { &&l };
+
+  __try { // expected-note {{jump exits __try block}}
+    // FIXME: Should this be allowed? Jumping out of the guarded section of a
+    // __try/__except doesn't require unwinding.
+    goto *ip; // expected-error {{cannot jump from this indirect goto statement to one of its possible targets}}
+  } __except(0) {
+  }
+
+  __try {
+  } __except(0) { // expected-note {{jump exits __except block}}
+    // FIXME: What about here?
+    goto *ip; // expected-error {{cannot jump from this indirect goto statement to one of its possible targets}}
+  }
+
+  __try { // expected-note {{jump exits __try block}}
+    goto *ip; // expected-error {{cannot jump from this indirect goto statement to one of its possible targets}}
+  } __finally {
+  }
+
+  __try {
+  } __finally { // expected-note {{jump exits __finally block}}
+    goto *ip; // expected-error {{cannot jump from this indirect goto statement to one of its possible targets}}
+  }
+l: // expected-note 4 {{possible target of indirect goto statement}}
+  ;
+}
+
+} // namespace seh





More information about the cfe-commits mailing list