Skip to content

Commit c163aae

Browse files
committed
[attributes] Add a facility for enforcing a Trusted Computing Base.
Introduce a function attribute 'enforce_tcb' that prevents the function from calling other functions without the same attribute. This allows isolating code that's considered to be somehow privileged so that it could not use its privileges to exhibit arbitrary behavior. Introduce an on-by-default warning '-Wtcb-enforcement' that warns about violations of the above rule. Introduce a function attribute 'enforce_tcb_leaf' that suppresses the new warning within the function it is attached to. Such leaf functions may implement common functionality between the trusted and the untrusted code but they require extra careful audit with respect to their capabilities. Differential Revision: https://reviews.llvm.org/D91898
1 parent 0073582 commit c163aae

File tree

10 files changed

+386
-0
lines changed

10 files changed

+386
-0
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,3 +3653,19 @@ def Builtin : InheritableAttr {
36533653
let SemaHandler = 0;
36543654
let Documentation = [Undocumented];
36553655
}
3656+
3657+
def EnforceTCB : InheritableAttr {
3658+
let Spellings = [Clang<"enforce_tcb">];
3659+
let Subjects = SubjectList<[Function]>;
3660+
let Args = [StringArgument<"TCBName">];
3661+
let Documentation = [EnforceTCBDocs];
3662+
bit InheritEvenIfAlreadyPresent = 1;
3663+
}
3664+
3665+
def EnforceTCBLeaf : InheritableAttr {
3666+
let Spellings = [Clang<"enforce_tcb_leaf">];
3667+
let Subjects = SubjectList<[Function]>;
3668+
let Args = [StringArgument<"TCBName">];
3669+
let Documentation = [EnforceTCBLeafDocs];
3670+
bit InheritEvenIfAlreadyPresent = 1;
3671+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5725,3 +5725,28 @@ Attribute docs`_, and `the GCC Inline docs`_.
57255725
}];
57265726
let Heading = "always_inline, __force_inline";
57275727
}
5728+
5729+
def EnforceTCBDocs : Documentation {
5730+
let Category = DocCatFunction;
5731+
let Content = [{
5732+
The ``enforce_tcb`` attribute can be placed on functions to enforce that a
5733+
trusted compute base (TCB) does not call out of the TCB. This generates a
5734+
warning every time a function not marked with an ``enforce_tcb`` attribute is
5735+
called from a function with the ``enforce_tcb`` attribute. A function may be a
5736+
part of multiple TCBs. Invocations through function pointers are currently
5737+
not checked. Builtins are considered to a part of every TCB.
5738+
5739+
- ``enforce_tcb(Name)`` indicates that this function is a part of the TCB named ``Name``
5740+
}];
5741+
}
5742+
5743+
def EnforceTCBLeafDocs : Documentation {
5744+
let Category = DocCatFunction;
5745+
let Content = [{
5746+
The ``enforce_tcb_leaf`` attribute satisfies the requirement enforced by
5747+
``enforce_tcb`` for the marked function to be in the named TCB but does not
5748+
continue to check the functions called from within the leaf function.
5749+
5750+
- ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name``
5751+
}];
5752+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11116,4 +11116,11 @@ def err_probability_not_constant_float : Error<
1111611116
def err_probability_out_of_range : Error<
1111711117
"probability argument to __builtin_expect_with_probability is outside the "
1111811118
"range [0.0, 1.0]">;
11119+
11120+
// TCB warnings
11121+
def err_tcb_conflicting_attributes : Error<
11122+
"attributes '%0(\"%2\")' and '%1(\"%2\")' are mutually exclusive">;
11123+
def warn_tcb_enforcement_violation : Warning<
11124+
"calling %0 is a violation of trusted computing base '%1'">,
11125+
InGroup<DiagGroup<"tcb-enforcement">>;
1111911126
} // end of sema component.

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,6 +3210,9 @@ class Sema final {
32103210
Decl *D, const WebAssemblyImportNameAttr &AL);
32113211
WebAssemblyImportModuleAttr *mergeImportModuleAttr(
32123212
Decl *D, const WebAssemblyImportModuleAttr &AL);
3213+
EnforceTCBAttr *mergeEnforceTCBAttr(Decl *D, const EnforceTCBAttr &AL);
3214+
EnforceTCBLeafAttr *mergeEnforceTCBLeafAttr(Decl *D,
3215+
const EnforceTCBLeafAttr &AL);
32133216

32143217
void mergeDeclAttributes(NamedDecl *New, Decl *Old,
32153218
AvailabilityMergeKind AMK = AMK_Redeclaration);
@@ -12427,6 +12430,8 @@ class Sema final {
1242712430
/// attempts to add itself into the container
1242812431
void CheckObjCCircularContainer(ObjCMessageExpr *Message);
1242912432

12433+
void CheckTCBEnforcement(const CallExpr *TheCall, const FunctionDecl *Callee);
12434+
1243012435
void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE);
1243112436
void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
1243212437
bool DeleteWasArrayForm);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 37 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"
@@ -4569,6 +4570,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
45694570
if (!FnInfo)
45704571
return false;
45714572

4573+
CheckTCBEnforcement(TheCall, FDecl);
4574+
45724575
CheckAbsoluteValueFunction(TheCall, FDecl);
45734576
CheckMaxUnsignedZero(TheCall, FDecl);
45744577

@@ -16059,3 +16062,37 @@ ExprResult Sema::SemaBuiltinMatrixColumnMajorStore(CallExpr *TheCall,
1605916062

1606016063
return CallResult;
1606116064
}
16065+
16066+
/// \brief Enforce the bounds of a TCB
16067+
/// CheckTCBEnforcement - Enforces that every function in a named TCB only
16068+
/// directly calls other functions in the same TCB as marked by the enforce_tcb
16069+
/// and enforce_tcb_leaf attributes.
16070+
void Sema::CheckTCBEnforcement(const CallExpr *TheCall,
16071+
const FunctionDecl *Callee) {
16072+
const FunctionDecl *Caller = getCurFunctionDecl();
16073+
16074+
// Calls to builtins are not enforced.
16075+
if (!Caller || !Caller->hasAttr<EnforceTCBAttr>() ||
16076+
Callee->getBuiltinID() != 0)
16077+
return;
16078+
16079+
// Search through the enforce_tcb and enforce_tcb_leaf attributes to find
16080+
// all TCBs the callee is a part of.
16081+
llvm::StringSet<> CalleeTCBs;
16082+
for_each(Callee->specific_attrs<EnforceTCBAttr>(),
16083+
[&](const auto *A) { CalleeTCBs.insert(A->getTCBName()); });
16084+
for_each(Callee->specific_attrs<EnforceTCBLeafAttr>(),
16085+
[&](const auto *A) { CalleeTCBs.insert(A->getTCBName()); });
16086+
16087+
// Go through the TCBs the caller is a part of and emit warnings if Caller
16088+
// is in a TCB that the Callee is not.
16089+
for_each(
16090+
Caller->specific_attrs<EnforceTCBAttr>(),
16091+
[&](const auto *A) {
16092+
StringRef CallerTCB = A->getTCBName();
16093+
if (CalleeTCBs.count(CallerTCB) == 0) {
16094+
Diag(TheCall->getExprLoc(), diag::warn_tcb_enforcement_violation)
16095+
<< Callee << CallerTCB;
16096+
}
16097+
});
16098+
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,10 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26122612
NewAttr = S.mergeImportModuleAttr(D, *IMA);
26132613
else if (const auto *INA = dyn_cast<WebAssemblyImportNameAttr>(Attr))
26142614
NewAttr = S.mergeImportNameAttr(D, *INA);
2615+
else if (const auto *TCBA = dyn_cast<EnforceTCBAttr>(Attr))
2616+
NewAttr = S.mergeEnforceTCBAttr(D, *TCBA);
2617+
else if (const auto *TCBLA = dyn_cast<EnforceTCBLeafAttr>(Attr))
2618+
NewAttr = S.mergeEnforceTCBLeafAttr(D, *TCBLA);
26152619
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
26162620
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
26172621

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
//===----------------------------------------------------------------------===//
@@ -8220,6 +8289,14 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
82208289
case ParsedAttr::AT_UseHandle:
82218290
handleHandleAttr<UseHandleAttr>(S, D, AL);
82228291
break;
8292+
8293+
case ParsedAttr::AT_EnforceTCB:
8294+
handleEnforceTCBAttr<EnforceTCBAttr, EnforceTCBLeafAttr>(S, D, AL);
8295+
break;
8296+
8297+
case ParsedAttr::AT_EnforceTCBLeaf:
8298+
handleEnforceTCBAttr<EnforceTCBLeafAttr, EnforceTCBAttr>(S, D, AL);
8299+
break;
82238300
}
82248301
}
82258302

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)