Skip to content

Commit d4d11fc

Browse files
committed
[CHERIoT] Add warning for common array offset patterns that generate out-of-bounds intermediate capabilities.
These aren't an issue on big CHERI where representable offsets usually extend well beyond bounds, but on CHERIoT our representation limits are quite tight, so this is a common source of bugs.
1 parent cf67262 commit d4d11fc

File tree

5 files changed

+146
-0
lines changed

5 files changed

+146
-0
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def CHERICapabilityToIntegerCast : DiagGroup<"capability-to-integer-cast">;
182182
def CheriPedantic : DiagGroup<"cheri-pedantic", [CHERICapabilityToIntegerCast, CHERIPrototypesStrict, CHERIProvenancePedantic]>;
183183
// Warnings/Errors for bugs in the MIPS/CHERI backend
184184
def MIPSCHERIBugs: DiagGroup<"mips-cheri-bugs">;
185+
def Cheriot : DiagGroup<"cheriot">;
185186

186187
// Generally useful CHERI errors
187188
def CHERIMissingCompartment: DiagGroup<"cheri-missing-compartment">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ def err_cheriot_invalid_sealing_key_type_name
219219
: Error<"the sealing key type name '%0' is not a valid identifier">;
220220
def warn_cheriot_use_of_builtin_sealing_key_type_no_compartment
221221
: Warning<"%0 used, but no compartment name given">,InGroup<CHERIMissingCompartment>,DefaultError;
222+
def warn_cheriot_array_offset
223+
: Warning<"offset pattern of %0 may create an invalid intermediate "
224+
"capability; consider reassociating the offsets together">,
225+
InGroup<Cheriot>;
222226

223227
// C99 variable-length arrays
224228
def ext_vla : Extension<"variable length arrays are a C99 feature">,

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,9 @@ void Clang::AddRISCVTargetArgs(const ArgList &Args,
20682068
CmdArgs.push_back("-target-abi");
20692069
CmdArgs.push_back(ABIName.data());
20702070

2071+
if (ABIName == "cheriot" || ABIName == "cheriot-baremetal")
2072+
CmdArgs.push_back("-Wcheriot");
2073+
20712074
if (Arg *A = Args.getLastArg(options::OPT_G)) {
20722075
CmdArgs.push_back("-msmall-data-limit");
20732076
CmdArgs.push_back(A->getValue());

clang/lib/Sema/SemaExpr.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10790,6 +10790,94 @@ QualType Sema::CheckSizelessVectorOperands(ExprResult &LHS, ExprResult &RHS,
1079010790
return QualType();
1079110791
}
1079210792

10793+
static void checkStaticOOBCapOffset(Sema &S, ExprResult &LHS, ExprResult &RHS,
10794+
BinaryOperator::Opcode OuterOpcode,
10795+
SourceLocation OuterLoc) {
10796+
auto getConstantAddend = [&](const Expr *E, llvm::APSInt &Value) {
10797+
Expr::EvalResult Result;
10798+
if (!E->EvaluateAsInt(Result, S.Context))
10799+
return false;
10800+
Value = Result.Val.getInt();
10801+
return true;
10802+
};
10803+
10804+
if (OuterOpcode != BO_Add && OuterOpcode != BO_Sub)
10805+
return;
10806+
10807+
// Simple arr + cst or arr - cst
10808+
if (const auto *Decay = dyn_cast<ImplicitCastExpr>(LHS.get())) {
10809+
if (Decay->getCastKind() == CK_ArrayToPointerDecay) {
10810+
const auto *CAT =
10811+
dyn_cast<ConstantArrayType>(Decay->getSubExpr()->getType());
10812+
if (CAT) {
10813+
const int64_t ArrayElts = CAT->getSExtSize();
10814+
10815+
llvm::APSInt AddendInt;
10816+
if (getConstantAddend(RHS.get(), AddendInt)) {
10817+
const int64_t Addend = AddendInt.getSExtValue();
10818+
10819+
// Warn on obviously out-of-bounds offsets.
10820+
if ((OuterOpcode == BO_Sub && Addend > 0) ||
10821+
(OuterOpcode == BO_Add && Addend < 0) ||
10822+
(OuterOpcode == BO_Add && Addend > ArrayElts) ||
10823+
(OuterOpcode == BO_Sub && Addend < -ArrayElts)) {
10824+
S.Diag(OuterLoc, diag::warn_cheriot_array_offset)
10825+
<< Decay->getSubExpr();
10826+
return;
10827+
}
10828+
}
10829+
}
10830+
}
10831+
}
10832+
10833+
// Nested (array + X) + constant
10834+
const auto *InnerBO = dyn_cast<BinaryOperator>(LHS.get());
10835+
if (!InnerBO)
10836+
return;
10837+
if (InnerBO->getOpcode() != BO_Add && InnerBO->getOpcode() != BO_Sub)
10838+
return;
10839+
10840+
const auto *Decay = dyn_cast<ImplicitCastExpr>(InnerBO->getLHS());
10841+
if (!Decay || Decay->getCastKind() != CK_ArrayToPointerDecay)
10842+
return;
10843+
const auto *CAT = dyn_cast<ConstantArrayType>(Decay->getSubExpr()->getType());
10844+
if (!CAT)
10845+
return;
10846+
int64_t ArrayElts = CAT->getSExtSize();
10847+
10848+
llvm::APSInt OuterAddendInt;
10849+
llvm::APSInt InnerAddendInt;
10850+
bool OuterAddendConstant = getConstantAddend(RHS.get(), OuterAddendInt);
10851+
bool InnerAddendConstant =
10852+
getConstantAddend(InnerBO->getRHS(), InnerAddendInt);
10853+
10854+
if (OuterAddendConstant && InnerAddendConstant)
10855+
return;
10856+
10857+
// RHS of outer add is constant
10858+
if (OuterAddendConstant) {
10859+
const int64_t Addend = OuterAddendInt.getSExtValue();
10860+
if (Addend >= ArrayElts) {
10861+
S.Diag(InnerBO->getExprLoc(), diag::warn_cheriot_array_offset)
10862+
<< Decay->getSubExpr();
10863+
}
10864+
return;
10865+
}
10866+
10867+
// RHS is not constant, but the inner RHS is constant
10868+
if (!InnerAddendConstant)
10869+
return;
10870+
int64_t Addend = InnerAddendInt.getSExtValue();
10871+
10872+
// Only warn when we have (arr + cst) + X and cst is equal to array size,
10873+
// because all other cases were already handled when checking the inner
10874+
// expression.
10875+
if (OuterOpcode == BO_Add && InnerBO->getOpcode() == BO_Add &&
10876+
Addend == ArrayElts)
10877+
S.Diag(InnerBO->getExprLoc(), diag::warn_cheriot_array_offset)
10878+
<< Decay->getSubExpr();
10879+
}
10880+
1079310881
// checkArithmeticNull - Detect when a NULL constant is used improperly in an
1079410882
// expression. These are mainly cases where the null pointer is used as an
1079510883
// integer instead of a pointer.
@@ -11424,6 +11512,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
1142411512
// note that we bias towards the LHS being the pointer.
1142511513
Expr *PExp = LHS.get(), *IExp = RHS.get();
1142611514

11515+
if (LHS.get()->getType()->isCHERICapabilityType(Context))
11516+
checkStaticOOBCapOffset(*this, LHS, RHS, BO_Add, Loc);
11517+
1142711518
// Addition is not allowed on sealed pointers.
1142811519
if (PExp->getType()->isCHERISealedCapabilityType(Context) &&
1142911520
!isUnevaluatedContext())
@@ -11555,6 +11646,8 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
1155511646
return InvalidOperands(Loc, LHS, RHS);
1155611647
if (RHS.get()->getType()->isCHERISealedCapabilityType(Context))
1155711648
return InvalidOperands(Loc, LHS, RHS);
11649+
if (LHS.get()->getType()->isCHERICapabilityType(Context))
11650+
checkStaticOOBCapOffset(*this, LHS, RHS, BO_Sub, Loc);
1155811651
}
1155911652

1156011653
// Diagnose bad cases where we step over interface counts.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %riscv32_cheri_cc1 "-triple" "riscv32cheriot-unknown-unknown" "-target-abi" "cheriot" -verify %s
2+
3+
void bar(void*);
4+
5+
void foo(int i) {
6+
int buf[4];
7+
bar(buf + i + 1);
8+
bar(buf + i + 4); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
9+
bar(buf + i + 5); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
10+
bar(buf + i - 1);
11+
bar(buf + i - 4); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
12+
bar(buf + i - 5); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
13+
bar(buf - i + 1);
14+
bar(buf - i + 4); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
15+
bar(buf - i + 5); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
16+
bar(buf - i - 1);
17+
bar(buf - i - 4); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
18+
bar(buf - i - 5); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
19+
20+
bar(buf + 1 + i);
21+
bar(buf + 4 + i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
22+
bar(buf + 5 + i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
23+
bar(buf - 1 + i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
24+
bar(buf - 4 + i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
25+
bar(buf - 5 + i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
26+
bar(buf + 1 - i);
27+
bar(buf + 4 - i);
28+
bar(buf + 5 - i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
29+
bar(buf - 1 - i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
30+
bar(buf - 4 - i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
31+
bar(buf - 5 - i); // expected-warning{{offset pattern of 'buf' may create an invalid intermediate capability; consider reassociating the offsets together}}
32+
33+
bar(buf + 0 + 1);
34+
bar(buf + 0 + 4);
35+
bar(buf + 0 + 5);
36+
bar(buf + 0 - 1);
37+
bar(buf + 0 - 4);
38+
bar(buf + 0 - 5);
39+
bar(buf - 0 + 1);
40+
bar(buf - 0 + 4);
41+
bar(buf - 0 + 5);
42+
bar(buf - 0 - 1);
43+
bar(buf - 0 - 4);
44+
bar(buf - 0 - 5);
45+
}

0 commit comments

Comments
 (0)