Skip to content

Commit 9d72318

Browse files
authored
[-Wunsafe-buffer-usage] Resolve functional conflict in checking safe span constructions (#10251)
There is a bounds-attr-based safe pattern for span construction that is not in upstream. This commit resolves a functional conflict caused by an upstream change due to such a difference. (rdar://146976681)
1 parent 8a3e336 commit 9d72318

File tree

1 file changed

+56
-49
lines changed

1 file changed

+56
-49
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,16 +1103,20 @@ static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) {
11031103
// 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
11041104
// `n`
11051105
// 5. `std::span<T>{any, 0}`
1106-
// 6. `std::span<T>{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`)
1107-
// pointer OR `std::span<char>{(char*)p, n}`, where `p` is a __sized_by(`n`)
1108-
// pointer.
1109-
// 7. `std::span<char>{p, strlen(p)}` or `std::span<wchar_t>{p, wcslen(p)}`
1110-
// 8. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
1106+
// 6. `std::span<char>{p, strlen(p)}` or `std::span<wchar_t>{p, wcslen(p)}`
1107+
// 7. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
11111108
// `f` is a function with attribute `alloc_size(N, M)`;
11121109
// `args` represents the list of arguments;
11131110
// `N, M` are parameter indexes to the allocating element number and size.
11141111
// Sometimes, there is only one parameter index representing the total
11151112
// size.
1113+
// TO_UPSTREAM(BoundsSafetyInterop) ON
1114+
// Interop Pattern:
1115+
// `std::span<T>{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`)
1116+
// pointer OR `std::span<char>{(char*)p, n}`, where `p` is a
1117+
// __sized_by(`n`) pointer. (This pattern is not in upstream, so try it
1118+
// last to avoid possible conflicts.)
1119+
// // TO_UPSTREAM(BoundsSafetyInterop) OFF
11161120
AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
11171121
assert(Node.getNumArgs() == 2 &&
11181122
"expecting a two-parameter std::span constructor");
@@ -1184,6 +1188,52 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
11841188
}
11851189

11861190
// Check form 6:
1191+
if (const auto *Call = dyn_cast<CallExpr>(Arg1);
1192+
Call && Call->getNumArgs() == 1) {
1193+
if (const FunctionDecl *FD = Call->getDirectCallee();
1194+
FD && FD->getIdentifier()) {
1195+
StringRef FName = FD->getName();
1196+
const Expr *CallArg = Call->getArg(0)->IgnoreParenImpCasts();
1197+
1198+
// TODO: we can re-use `LibcFunNamePrefixSuffixParser` to support more
1199+
// variants, e.g., `strlen_s`
1200+
return (FName == "strlen" || FName == "wcslen") &&
1201+
AreSameDRE(Arg0, CallArg);
1202+
}
1203+
}
1204+
1205+
// Check form 7:
1206+
if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
1207+
if (!CCast->getType()->isPointerType())
1208+
return false;
1209+
1210+
QualType PteTy = CCast->getType()->getPointeeType();
1211+
1212+
if (!(PteTy->isConstantSizeType() && Ctx.getTypeSizeInChars(PteTy).isOne()))
1213+
return false;
1214+
1215+
if (const auto *Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
1216+
if (const FunctionDecl *FD = Call->getDirectCallee())
1217+
if (auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
1218+
const Expr *EleSizeExpr =
1219+
Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
1220+
// NumElemIdx is invalid if AllocSizeAttr has 1 argument:
1221+
ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
1222+
1223+
if (!NumElemIdx.isValid())
1224+
return areEqualIntegers(Arg1, EleSizeExpr, Ctx);
1225+
1226+
const Expr *NumElesExpr = Call->getArg(NumElemIdx.getASTIndex());
1227+
1228+
if (auto BO = dyn_cast<BinaryOperator>(Arg1))
1229+
return areEqualIntegralBinaryOperators(BO, NumElesExpr, BO_Mul,
1230+
EleSizeExpr, Ctx);
1231+
}
1232+
}
1233+
}
1234+
1235+
/* TO_UPSTREAM(BoundsSafetyInterop) ON */
1236+
// Check interop pattern:
11871237
bool isArg0CastToBytePtrType = false;
11881238

11891239
if (auto *CE = dyn_cast<CastExpr>(Arg0)) {
@@ -1229,50 +1279,7 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
12291279
/*DependentValues=*/nullptr,
12301280
Finder->getASTContext());
12311281
}
1232-
// Check form 7:
1233-
if (const auto *Call = dyn_cast<CallExpr>(Arg1);
1234-
Call && Call->getNumArgs() == 1) {
1235-
if (const FunctionDecl *FD = Call->getDirectCallee();
1236-
FD && FD->getIdentifier()) {
1237-
StringRef FName = FD->getName();
1238-
const Expr *CallArg = Call->getArg(0)->IgnoreParenImpCasts();
1239-
1240-
// TODO: we can re-use `LibcFunNamePrefixSuffixParser` to support more
1241-
// variants, e.g., `strlen_s`
1242-
return (FName == "strlen" || FName == "wcslen") &&
1243-
AreSameDRE(Arg0, CallArg);
1244-
}
1245-
}
1246-
1247-
// Check form 8:
1248-
if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
1249-
if (!CCast->getType()->isPointerType())
1250-
return false;
1251-
1252-
QualType PteTy = CCast->getType()->getPointeeType();
1253-
1254-
if (!(PteTy->isConstantSizeType() && Ctx.getTypeSizeInChars(PteTy).isOne()))
1255-
return false;
1256-
1257-
if (const auto *Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
1258-
if (const FunctionDecl *FD = Call->getDirectCallee())
1259-
if (auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
1260-
const Expr *EleSizeExpr =
1261-
Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
1262-
// NumElemIdx is invalid if AllocSizeAttr has 1 argument:
1263-
ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
1264-
1265-
if (!NumElemIdx.isValid())
1266-
return areEqualIntegers(Arg1, EleSizeExpr, Ctx);
1267-
1268-
const Expr *NumElesExpr = Call->getArg(NumElemIdx.getASTIndex());
1269-
1270-
if (auto BO = dyn_cast<BinaryOperator>(Arg1))
1271-
return areEqualIntegralBinaryOperators(BO, NumElesExpr, BO_Mul,
1272-
EleSizeExpr, Ctx);
1273-
}
1274-
}
1275-
}
1282+
/* TO_UPSTREAM(BoundsSafetyInterop) OFF */
12761283
return false;
12771284
}
12781285

0 commit comments

Comments
 (0)