]> granicus.if.org Git - clang/commitdiff
Implement jump scope SEHmantic analysis.
authorNico Weber <nicolasweber@gmx.de>
Tue, 3 Feb 2015 17:06:08 +0000 (17:06 +0000)
committerNico Weber <nicolasweber@gmx.de>
Tue, 3 Feb 2015 17:06:08 +0000 (17:06 +0000)
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).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@227982 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 5669415c26d9511da0b7db16f97704c4ab6d5a39..f9dd56f222804e4c8e897473a0dbfbda976fb716 100644 (file)
@@ -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<
index 9a9215174ba141cc936f4e75deead906120940d2..ac8733fe19cf13667ef3205d8e3bf3e8d851996e 100644 (file)
@@ -338,9 +338,35 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
     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;
index ac700999c284c790b6a7ead06862663599abe202..c7638aea106195ababb1d63150736a17ff546ab8 100644 (file)
@@ -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