Skip to content

Commit 6a76fcc

Browse files
authored
Merge pull request #2335 from haoNoQ/enforce-tcb-cherrypick
Cherry-pick the enforce_tcb attribute.
2 parents e14551b + fe96ada commit 6a76fcc

File tree

11 files changed

+389
-0
lines changed

11 files changed

+389
-0
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3713,3 +3713,19 @@ def Builtin : InheritableAttr {
37133713
let SemaHandler = 0;
37143714
let Documentation = [Undocumented];
37153715
}
3716+
3717+
def EnforceTCB : InheritableAttr {
3718+
let Spellings = [Clang<"enforce_tcb">];
3719+
let Subjects = SubjectList<[Function]>;
3720+
let Args = [StringArgument<"TCBName">];
3721+
let Documentation = [EnforceTCBDocs];
3722+
bit InheritEvenIfAlreadyPresent = 1;
3723+
}
3724+
3725+
def EnforceTCBLeaf : InheritableAttr {
3726+
let Spellings = [Clang<"enforce_tcb_leaf">];
3727+
let Subjects = SubjectList<[Function]>;
3728+
let Args = [StringArgument<"TCBName">];
3729+
let Documentation = [EnforceTCBLeafDocs];
3730+
bit InheritEvenIfAlreadyPresent = 1;
3731+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5884,3 +5884,28 @@ Attribute docs`_, and `the GCC Inline docs`_.
58845884
}];
58855885
let Heading = "always_inline, __force_inline";
58865886
}
5887+
5888+
def EnforceTCBDocs : Documentation {
5889+
let Category = DocCatFunction;
5890+
let Content = [{
5891+
The ``enforce_tcb`` attribute can be placed on functions to enforce that a
5892+
trusted compute base (TCB) does not call out of the TCB. This generates a
5893+
warning every time a function not marked with an ``enforce_tcb`` attribute is
5894+
called from a function with the ``enforce_tcb`` attribute. A function may be a
5895+
part of multiple TCBs. Invocations through function pointers are currently
5896+
not checked. Builtins are considered to a part of every TCB.
5897+
5898+
- ``enforce_tcb(Name)`` indicates that this function is a part of the TCB named ``Name``
5899+
}];
5900+
}
5901+
5902+
def EnforceTCBLeafDocs : Documentation {
5903+
let Category = DocCatFunction;
5904+
let Content = [{
5905+
The ``enforce_tcb_leaf`` attribute satisfies the requirement enforced by
5906+
``enforce_tcb`` for the marked function to be in the named TCB but does not
5907+
continue to check the functions called from within the leaf function.
5908+
5909+
- ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name``
5910+
}];
5911+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11209,4 +11209,11 @@ def err_probability_not_constant_float : Error<
1120911209
def err_probability_out_of_range : Error<
1121011210
"probability argument to __builtin_expect_with_probability is outside the "
1121111211
"range [0.0, 1.0]">;
11212+
11213+
// TCB warnings
11214+
def err_tcb_conflicting_attributes : Error<
11215+
"attributes '%0(\"%2\")' and '%1(\"%2\")' are mutually exclusive">;
11216+
def warn_tcb_enforcement_violation : Warning<
11217+
"calling %0 is a violation of trusted computing base '%1'">,
11218+
InGroup<DiagGroup<"tcb-enforcement">>;
1121211219
} // end of sema component.

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3213,6 +3213,9 @@ class Sema final {
32133213
Decl *D, const WebAssemblyImportNameAttr &AL);
32143214
WebAssemblyImportModuleAttr *mergeImportModuleAttr(
32153215
Decl *D, const WebAssemblyImportModuleAttr &AL);
3216+
EnforceTCBAttr *mergeEnforceTCBAttr(Decl *D, const EnforceTCBAttr &AL);
3217+
EnforceTCBLeafAttr *mergeEnforceTCBLeafAttr(Decl *D,
3218+
const EnforceTCBLeafAttr &AL);
32163219

32173220
void mergeDeclAttributes(NamedDecl *New, Decl *Old,
32183221
AvailabilityMergeKind AMK = AMK_Redeclaration);
@@ -12465,6 +12468,8 @@ class Sema final {
1246512468
/// attempts to add itself into the container
1246612469
void CheckObjCCircularContainer(ObjCMessageExpr *Message);
1246712470

12471+
void CheckTCBEnforcement(const CallExpr *TheCall, const FunctionDecl *Callee);
12472+
1246812473
void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE);
1246912474
void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
1247012475
bool DeleteWasArrayForm);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#include "llvm/ADT/SmallString.h"
7676
#include "llvm/ADT/SmallVector.h"
7777
#include "llvm/ADT/StringRef.h"
78+
#include "llvm/ADT/StringSet.h"
7879
#include "llvm/ADT/StringSwitch.h"
7980
#include "llvm/ADT/Triple.h"
8081
#include "llvm/Support/AtomicOrdering.h"
@@ -4895,6 +4896,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
48954896
if (!FnInfo)
48964897
return false;
48974898

4899+
CheckTCBEnforcement(TheCall, FDecl);
4900+
48984901
CheckAbsoluteValueFunction(TheCall, FDecl);
48994902
CheckMaxUnsignedZero(TheCall, FDecl);
49004903

@@ -16388,3 +16391,38 @@ ExprResult Sema::SemaBuiltinMatrixColumnMajorStore(CallExpr *TheCall,
1638816391

1638916392
return CallResult;
1639016393
}
16394+
16395+
/// \brief Enforce the bounds of a TCB
16396+
/// CheckTCBEnforcement - Enforces that every function in a named TCB only
16397+
/// directly calls other functions in the same TCB as marked by the enforce_tcb
16398+
/// and enforce_tcb_leaf attributes.
16399+
void Sema::CheckTCBEnforcement(const CallExpr *TheCall,
16400+
const FunctionDecl *Callee) {
16401+
const FunctionDecl *Caller = getCurFunctionDecl();
16402+
16403+
// Calls to builtins are not enforced.
16404+
if (!Caller || !Caller->hasAttr<EnforceTCBAttr>() ||
16405+
Callee->getBuiltinID() != 0)
16406+
return;
16407+
16408+
// Search through the enforce_tcb and enforce_tcb_leaf attributes to find
16409+
// all TCBs the callee is a part of.
16410+
llvm::StringSet<> CalleeTCBs;
16411+
for_each(Callee->specific_attrs<EnforceTCBAttr>(),
16412+
[&](const auto *A) { CalleeTCBs.insert(A->getTCBName()); });
16413+
for_each(Callee->specific_attrs<EnforceTCBLeafAttr>(),
16414+
[&](const auto *A) { CalleeTCBs.insert(A->getTCBName()); });
16415+
16416+
// Go through the TCBs the caller is a part of and emit warnings if Caller
16417+
// is in a TCB that the Callee is not.
16418+
for_each(
16419+
Caller->specific_attrs<EnforceTCBAttr>(),
16420+
[&](const auto *A) {
16421+
StringRef CallerTCB = A->getTCBName();
16422+
if (CalleeTCBs.count(CallerTCB) == 0) {
16423+
this->Diag(TheCall->getExprLoc(),
16424+
diag::warn_tcb_enforcement_violation) << Callee
16425+
<< CallerTCB;
16426+
}
16427+
});
16428+
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,10 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26162616
NewAttr = S.mergeImportModuleAttr(D, *IMA);
26172617
else if (const auto *INA = dyn_cast<WebAssemblyImportNameAttr>(Attr))
26182618
NewAttr = S.mergeImportNameAttr(D, *INA);
2619+
else if (const auto *TCBA = dyn_cast<EnforceTCBAttr>(Attr))
2620+
NewAttr = S.mergeEnforceTCBAttr(D, *TCBA);
2621+
else if (const auto *TCBLA = dyn_cast<EnforceTCBLeafAttr>(Attr))
2622+
NewAttr = S.mergeEnforceTCBLeafAttr(D, *TCBLA);
26192623
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
26202624
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
26212625

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7517,6 +7517,75 @@ static void handleCFGuardAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
75177517
D->addAttr(::new (S.Context) CFGuardAttr(S.Context, AL, Arg));
75187518
}
75197519

7520+
7521+
template <typename AttrTy>
7522+
static const AttrTy *findEnforceTCBAttrByName(Decl *D, StringRef Name) {
7523+
auto Attrs = D->specific_attrs<AttrTy>();
7524+
auto I = llvm::find_if(Attrs,
7525+
[Name](const AttrTy *A) {
7526+
return A->getTCBName() == Name;
7527+
});
7528+
return I == Attrs.end() ? nullptr : *I;
7529+
}
7530+
7531+
template <typename AttrTy, typename ConflictingAttrTy>
7532+
static void handleEnforceTCBAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
7533+
StringRef Argument;
7534+
if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument))
7535+
return;
7536+
7537+
// A function cannot be have both regular and leaf membership in the same TCB.
7538+
if (const ConflictingAttrTy *ConflictingAttr =
7539+
findEnforceTCBAttrByName<ConflictingAttrTy>(D, Argument)) {
7540+
// We could attach a note to the other attribute but in this case
7541+
// there's no need given how the two are very close to each other.
7542+
S.Diag(AL.getLoc(), diag::err_tcb_conflicting_attributes)
7543+
<< AL.getAttrName()->getName() << ConflictingAttr->getAttrName()->getName()
7544+
<< Argument;
7545+
7546+
// Error recovery: drop the non-leaf attribute so that to suppress
7547+
// all future warnings caused by erroneous attributes. The leaf attribute
7548+
// needs to be kept because it can only suppresses warnings, not cause them.
7549+
D->dropAttr<EnforceTCBAttr>();
7550+
return;
7551+
}
7552+
7553+
D->addAttr(AttrTy::Create(S.Context, Argument, AL));
7554+
}
7555+
7556+
template <typename AttrTy, typename ConflictingAttrTy>
7557+
static AttrTy *mergeEnforceTCBAttrImpl(Sema &S, Decl *D, const AttrTy &AL) {
7558+
// Check if the new redeclaration has different leaf-ness in the same TCB.
7559+
StringRef TCBName = AL.getTCBName();
7560+
if (const ConflictingAttrTy *ConflictingAttr =
7561+
findEnforceTCBAttrByName<ConflictingAttrTy>(D, TCBName)) {
7562+
S.Diag(ConflictingAttr->getLoc(), diag::err_tcb_conflicting_attributes)
7563+
<< ConflictingAttr->getAttrName()->getName()
7564+
<< AL.getAttrName()->getName() << TCBName;
7565+
7566+
// Add a note so that the user could easily find the conflicting attribute.
7567+
S.Diag(AL.getLoc(), diag::note_conflicting_attribute);
7568+
7569+
// More error recovery.
7570+
D->dropAttr<EnforceTCBAttr>();
7571+
return nullptr;
7572+
}
7573+
7574+
ASTContext &Context = S.getASTContext();
7575+
return ::new(Context) AttrTy(Context, AL, AL.getTCBName());
7576+
}
7577+
7578+
EnforceTCBAttr *Sema::mergeEnforceTCBAttr(Decl *D, const EnforceTCBAttr &AL) {
7579+
return mergeEnforceTCBAttrImpl<EnforceTCBAttr, EnforceTCBLeafAttr>(
7580+
*this, D, AL);
7581+
}
7582+
7583+
EnforceTCBLeafAttr *Sema::mergeEnforceTCBLeafAttr(
7584+
Decl *D, const EnforceTCBLeafAttr &AL) {
7585+
return mergeEnforceTCBAttrImpl<EnforceTCBLeafAttr, EnforceTCBAttr>(
7586+
*this, D, AL);
7587+
}
7588+
75207589
//===----------------------------------------------------------------------===//
75217590
// Top Level Sema Entry Points
75227591
//===----------------------------------------------------------------------===//
@@ -8223,6 +8292,14 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
82238292
case ParsedAttr::AT_UseHandle:
82248293
handleHandleAttr<UseHandleAttr>(S, D, AL);
82258294
break;
8295+
8296+
case ParsedAttr::AT_EnforceTCB:
8297+
handleEnforceTCBAttr<EnforceTCBAttr, EnforceTCBLeafAttr>(S, D, AL);
8298+
break;
8299+
8300+
case ParsedAttr::AT_EnforceTCBLeaf:
8301+
handleEnforceTCBAttr<EnforceTCBLeafAttr, EnforceTCBAttr>(S, D, AL);
8302+
break;
82268303
}
82278304
}
82288305

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
// CHECK-NEXT: Destructor (SubjectMatchRule_function)
5959
// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method)
6060
// CHECK-NEXT: EnableIf (SubjectMatchRule_function)
61+
// CHECK-NEXT: EnforceTCB (SubjectMatchRule_function)
62+
// CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function)
6163
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
6264
// CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
6365
// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
[[clang::enforce_tcb("oops")]] int wrong_subject_type; // expected-warning{{'enforce_tcb' attribute only applies to functions}}
4+
5+
void no_arguments() __attribute__((enforce_tcb)); // expected-error{{'enforce_tcb' attribute takes one argument}}
6+
7+
void too_many_arguments() __attribute__((enforce_tcb("test", 12))); // expected-error{{'enforce_tcb' attribute takes one argument}}
8+
9+
void wrong_argument_type() __attribute__((enforce_tcb(12))); // expected-error{{'enforce_tcb' attribute requires a string}}
10+
11+
[[clang::enforce_tcb_leaf("oops")]] int wrong_subject_type_leaf; // expected-warning{{'enforce_tcb_leaf' attribute only applies to functions}}
12+
13+
void no_arguments_leaf() __attribute__((enforce_tcb_leaf)); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}}
14+
15+
void too_many_arguments_leaf() __attribute__((enforce_tcb_leaf("test", 12))); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}}
16+
void wrong_argument_type_leaf() __attribute__((enforce_tcb_leaf(12))); // expected-error{{'enforce_tcb_leaf' attribute requires a string}}
17+
18+
void foo();
19+
20+
__attribute__((enforce_tcb("x")))
21+
__attribute__((enforce_tcb_leaf("x"))) // expected-error{{attributes 'enforce_tcb_leaf("x")' and 'enforce_tcb("x")' are mutually exclusive}}
22+
void both_tcb_and_tcb_leaf() {
23+
foo(); // no-warning
24+
}
25+
26+
__attribute__((enforce_tcb_leaf("x"))) // expected-note{{conflicting attribute is here}}
27+
void both_tcb_and_tcb_leaf_on_separate_redeclarations();
28+
__attribute__((enforce_tcb("x"))) // expected-error{{attributes 'enforce_tcb("x")' and 'enforce_tcb_leaf("x")' are mutually exclusive}}
29+
void both_tcb_and_tcb_leaf_on_separate_redeclarations() {
30+
// Error recovery: no need to emit a warning when we didn't
31+
// figure out our attributes to begin with.
32+
foo(); // no-warning
33+
}
34+
35+
__attribute__((enforce_tcb_leaf("x")))
36+
__attribute__((enforce_tcb("x"))) // expected-error{{attributes 'enforce_tcb("x")' and 'enforce_tcb_leaf("x")' are mutually exclusive}}
37+
void both_tcb_and_tcb_leaf_opposite_order() {
38+
foo(); // no-warning
39+
}
40+
41+
__attribute__((enforce_tcb("x"))) // expected-note{{conflicting attribute is here}}
42+
void both_tcb_and_tcb_leaf_on_separate_redeclarations_opposite_order();
43+
__attribute__((enforce_tcb_leaf("x"))) // expected-error{{attributes 'enforce_tcb_leaf("x")' and 'enforce_tcb("x")' are mutually exclusive}}
44+
void both_tcb_and_tcb_leaf_on_separate_redeclarations_opposite_order() {
45+
foo(); // no-warning
46+
}
47+
48+
__attribute__((enforce_tcb("x")))
49+
__attribute__((enforce_tcb_leaf("y"))) // no-error
50+
void both_tcb_and_tcb_leaf_but_different_identifiers() {
51+
foo(); // expected-warning{{calling 'foo' is a violation of trusted computing base 'x'}}
52+
}
53+
__attribute__((enforce_tcb_leaf("x")))
54+
__attribute__((enforce_tcb("y"))) // no-error
55+
void both_tcb_and_tcb_leaf_but_different_identifiers_opposite_order() {
56+
foo(); // expected-warning{{calling 'foo' is a violation of trusted computing base 'y'}}
57+
}
58+
59+
__attribute__((enforce_tcb("x")))
60+
void both_tcb_and_tcb_leaf_but_different_identifiers_on_separate_redeclarations();
61+
__attribute__((enforce_tcb_leaf("y"))) // no-error
62+
void both_tcb_and_tcb_leaf_but_different_identifiers_on_separate_redeclarations() {
63+
foo(); // expected-warning{{calling 'foo' is a violation of trusted computing base 'x'}}
64+
}
65+
66+
__attribute__((enforce_tcb_leaf("x")))
67+
void both_tcb_and_tcb_leaf_but_different_identifiers_on_separate_redeclarations_opposite_order();
68+
__attribute__((enforce_tcb("y")))
69+
void both_tcb_and_tcb_leaf_but_different_identifiers_on_separate_redeclarations_opposite_order() {
70+
foo(); // expected-warning{{calling 'foo' is a violation of trusted computing base 'y'}}
71+
}
72+
73+
__attribute__((enforce_tcb("y")))
74+
__attribute__((enforce_tcb("x")))
75+
__attribute__((enforce_tcb_leaf("x"))) // expected-error{{attributes 'enforce_tcb_leaf("x")' and 'enforce_tcb("x")' are mutually exclusive}}
76+
void error_recovery_over_individual_tcbs() {
77+
// FIXME: Ideally this should warn. The conflict between attributes
78+
// for TCB "x" shouldn't affect the warning about TCB "y".
79+
foo(); // no-warning
80+
}

clang/test/Sema/attr-enforce-tcb.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
#define PLACE_IN_TCB(NAME) __attribute__ ((enforce_tcb(NAME)))
4+
#define PLACE_IN_TCB_LEAF(NAME) __attribute__ ((enforce_tcb_leaf(NAME)))
5+
6+
void foo1 (void) PLACE_IN_TCB("bar");
7+
void foo2 (void) PLACE_IN_TCB("bar");
8+
void foo3 (void); // not in any TCB
9+
void foo4 (void) PLACE_IN_TCB("bar2");
10+
void foo5 (void) PLACE_IN_TCB_LEAF("bar");
11+
void foo6 (void) PLACE_IN_TCB("bar2") PLACE_IN_TCB("bar");
12+
void foo7 (void) PLACE_IN_TCB("bar3");
13+
void foo8 (void) PLACE_IN_TCB("bar") PLACE_IN_TCB("bar2");
14+
void foo9 (void);
15+
16+
void foo1() {
17+
foo2(); // OK - function in same TCB
18+
foo3(); // expected-warning {{calling 'foo3' is a violation of trusted computing base 'bar'}}
19+
foo4(); // expected-warning {{calling 'foo4' is a violation of trusted computing base 'bar'}}
20+
foo5(); // OK - in leaf node
21+
foo6(); // OK - in multiple TCBs, one of which is the same
22+
foo7(); // expected-warning {{calling 'foo7' is a violation of trusted computing base 'bar'}}
23+
(void) __builtin_clz(5); // OK - builtins are excluded
24+
}
25+
26+
// Normal use without any attributes works
27+
void foo3() {
28+
foo9(); // no-warning
29+
}
30+
31+
void foo5() {
32+
// all calls should be okay, function in TCB leaf
33+
foo2(); // no-warning
34+
foo3(); // no-warning
35+
foo4(); // no-warning
36+
}
37+
38+
void foo6() {
39+
foo1(); // expected-warning {{calling 'foo1' is a violation of trusted computing base 'bar2'}}
40+
foo4(); // expected-warning {{calling 'foo4' is a violation of trusted computing base 'bar'}}
41+
foo8(); // no-warning
42+
foo7(); // #1
43+
// expected-warning@#1 {{calling 'foo7' is a violation of trusted computing base 'bar2'}}
44+
// expected-warning@#1 {{calling 'foo7' is a violation of trusted computing base 'bar'}}
45+
}
46+
47+
// Ensure that attribute merging works as expected across redeclarations.
48+
void foo10() PLACE_IN_TCB("bar");
49+
void foo10() PLACE_IN_TCB("bar2");
50+
void foo10() PLACE_IN_TCB("bar3");
51+
void foo10() {
52+
foo1(); // #2
53+
// expected-warning@#2 {{calling 'foo1' is a violation of trusted computing base 'bar2'}}
54+
// expected-warning@#2 {{calling 'foo1' is a violation of trusted computing base 'bar3'}}
55+
foo3(); // #3
56+
// expected-warning@#3 {{calling 'foo3' is a violation of trusted computing base 'bar'}}
57+
// expected-warning@#3 {{calling 'foo3' is a violation of trusted computing base 'bar2'}}
58+
// expected-warning@#3 {{calling 'foo3' is a violation of trusted computing base 'bar3'}}
59+
foo4(); // #4
60+
// expected-warning@#4 {{calling 'foo4' is a violation of trusted computing base 'bar'}}
61+
// expected-warning@#4 {{calling 'foo4' is a violation of trusted computing base 'bar3'}}
62+
foo7(); // #5
63+
// expected-warning@#5 {{calling 'foo7' is a violation of trusted computing base 'bar'}}
64+
// expected-warning@#5 {{calling 'foo7' is a violation of trusted computing base 'bar2'}}
65+
}

0 commit comments

Comments
 (0)