Skip to content

Commit 7b8d848

Browse files
committed
[BoundsSafety] make alloc_size imply __sized_by_or_null return type
When -fbounds-safety is enabled, the semantics of the return value of functions is that of __sized_by_or_null. Unlike return types annotated with __sized_by_or_null, it is never included in the type system. Instead every relevant analysis and transformation has to specifically check for the alloc_size attribute. This patch infers a __sized_by_or_null return type for functions annotated with alloc_size. This enables us to remove those special cases and reduce complexity. rdar://118338657 rdar://91017404 rdar://11833865
1 parent 09bbceb commit 7b8d848

35 files changed

+1288
-149
lines changed

clang/docs/InternalsManual.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,17 @@ Description:
427427
This is useful when the argument could be a string in some cases, but
428428
another class in other cases, and it needs to be quoted consistently.
429429

430+
**"unquoted" format**
431+
432+
Example:
433+
``"conflicting attributes were '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0(%unquoted1)' and '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2(%unquoted3)'"``
434+
Class:
435+
``Expr, Decl``
436+
Description:
437+
This is a simple formatter which omits the single quotes from a class
438+
that would normally be printed quoted. This is useful when the diagnostic
439+
uses the argument to construct a larger type or expression.
440+
430441
.. _internals-producing-diag:
431442

432443
Producing the Diagnostic

clang/include/clang/AST/Attr.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,22 @@ class ParamIdx {
354354
assertComparable(I);
355355
return Idx == I.Idx;
356356
}
357+
/* TO_UPSTREAM(BoundsSafety) ON */
358+
/// Unlike operator== which requires isValid to be true for both objects,
359+
/// this relaxes that constraint and returns true when comparing two
360+
/// invalid objects. An invalid object does not equal any valid object.
361+
bool equals(const ParamIdx &I) const {
362+
assert(HasThis == I.HasThis &&
363+
"ParamIdx must be for the same function to be compared");
364+
if (isValid() != I.isValid())
365+
return false;
366+
if (!isValid()) {
367+
assert(!I.isValid());
368+
return true;
369+
}
370+
return Idx == I.Idx;
371+
}
372+
/* TO_UPSTREAM(BoundsSafety) OFF */
357373
bool operator!=(const ParamIdx &I) const {
358374
assertComparable(I);
359375
return Idx != I.Idx;

clang/include/clang/AST/Type.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,6 +2605,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
26052605
bool isBidiIndexablePointerType() const;
26062606
bool isUnspecifiedPointerType() const;
26072607
bool isSafePointerType() const;
2608+
bool isImplicitSafePointerType() const;
26082609
/* TO_UPSTREAM(BoundsSafety) OFF */
26092610
bool isSignableType(const ASTContext &Ctx) const;
26102611
bool isSignablePointerType() const;
@@ -8839,6 +8840,19 @@ inline bool Type::isSafePointerType() const {
88398840
isValueTerminatedType());
88408841
}
88418842

8843+
/* TO_UPSTREAM(BoundsSafety) ON */
8844+
inline bool Type::isImplicitSafePointerType() const {
8845+
if (auto AT = this->getAs<AttributedType>()) {
8846+
if (AT->getAttrKind() == attr::PtrAutoAttr) {
8847+
assert(isSafePointerType());
8848+
return true;
8849+
}
8850+
return AT->getEquivalentType()->isImplicitSafePointerType();
8851+
}
8852+
return false;
8853+
}
8854+
/* TO_UPSTREAM(BoundsSafety) OFF */
8855+
88428856
inline bool Type::isPointerTypeWithBounds() const {
88438857
const auto *PT = dyn_cast<PointerType>(CanonicalType);
88448858
return PT && PT->getPointerAttributes().hasUpperBound();

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,9 @@ def err_attribute_only_once_per_parameter : Error<
34393439
"%0 attribute can only be applied once per parameter">;
34403440
def err_mismatched_uuid : Error<"uuid does not match previous declaration">;
34413441
def note_previous_uuid : Note<"previous uuid specified here">;
3442+
/* TO_UPSTREAM(BoundsSafety) ON */
3443+
def err_mismatched_alloc_size : Error<"'alloc_size' attribute does not match previous declaration">;
3444+
/* TO_UPSTREAM(BoundsSafety) OFF */
34423445
def warn_attribute_pointers_only : Warning<
34433446
"%0 attribute only applies to%select{| constant}1 pointer arguments">,
34443447
InGroup<IgnoredAttributes>;
@@ -12789,7 +12792,19 @@ let CategoryName = "BoundsSafety Pointer Attributes Issue" in {
1278912792
def err_bounds_safety_conflicting_pointer_attributes : Error<
1279012793
"%select{array|pointer}0 cannot have more than one %select{bound|type|count|end|terminator}1 attribute">;
1279112794
def note_bounds_safety_conflicting_pointer_attribute_args : Note<
12792-
"conflicting arguments for %select{count|end|terminator}0 were %1 and %2">;
12795+
"conflicting arguments for %select{end|terminator}0 were %1 and %2">;
12796+
def warn_bounds_safety_ignored_implicit_sized_by_or_null : Warning<
12797+
"implicit __sized_by_or_null attribute ignored because of explicit __sized_by">;
12798+
def note_bounds_safety_overriding_implicit_sized_by_or_null_silence : Note<
12799+
"add _Nonnull qualifier to return type to silence this warning">;
12800+
def note_bounds_safety_conflicting_count_attribute : Note<
12801+
"conflicting attributes were '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0(%unquoted1)' and '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2(%unquoted3)'">;
12802+
def note_bounds_safety_implicit_size_from_alloc_size_attr : Note<
12803+
"function return type implicitly __sized_by%select{|_or_null}0 because of the function's 'alloc_size' %select{and 'returns_nonnull' attributes|attribute}0">;
12804+
def err_invalid_return_type_for_alloc_size : Error<
12805+
"invalid return type %0 for function with alloc_size attribute; '__sized_by_or_null(%unquoted1)' or '__sized_by(%unquoted1)' required">;
12806+
def note_attribute_inherited : Note<
12807+
"attribute inherited from previous declaration here">;
1279312808
def warn_bounds_safety_duplicate_pointer_attributes : Warning<
1279412809
"%select{array|pointer}0 annotated with %select{__unsafe_indexable|__bidi_indexable|__indexable|__single|__terminated_by}1 "
1279512810
"multiple times. Annotate only once to remove this warning">, InGroup<BoundsSafetyRedundantAttribute>;

clang/include/clang/Sema/Attr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ inline const ParmVarDecl *getFunctionOrMethodParam(const Decl *D,
8080
return nullptr;
8181
}
8282

83+
/* TO_UPSTREAM(BoundsSafety) ON */
84+
inline ParmVarDecl *getFunctionOrMethodParam(Decl *D, unsigned Idx) {
85+
return const_cast<ParmVarDecl *>(
86+
getFunctionOrMethodParam(const_cast<const Decl *>(D), Idx));
87+
}
88+
/* TO_UPSTREAM(BoundsSafety) OFF */
89+
8390
inline QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) {
8491
if (const FunctionType *FnTy = D->getFunctionType())
8592
return cast<FunctionProtoType>(FnTy)->getParamType(Idx);

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,10 @@ class ParsedAttributes : public ParsedAttributesView {
962962
void takeAllFrom(ParsedAttributes &Other) {
963963
assert(&Other != this &&
964964
"ParsedAttributes can't take attributes from itself");
965-
addAll(Other.begin(), Other.end());
965+
/* TO_UPSTREAM(BoundsSafety) ON
966+
* Upstream uses addAll() instead, but that changes the attribute order. */
967+
addAllAtEnd(Other.begin(), Other.end());
968+
/* TO_UPSTREAM(BoundsSafety) OFF */
966969
Other.clearListOnly();
967970
pool.takeAllFrom(Other.pool);
968971
}

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5380,6 +5380,9 @@ class Sema final : public SemaBase {
53805380
MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI);
53815381
OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D,
53825382
const AttributeCommonInfo &CI);
5383+
/* TO_UPSTREAM(BoundsSafety) ON */
5384+
AllocSizeAttr *mergeAllocSizeAttr(NamedDecl *D, const AllocSizeAttr &ASA);
5385+
/* TO_UPSTREAM(BoundsSafety) OFF */
53835386
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL);
53845387
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D,
53855388
const InternalLinkageAttr &AL);
@@ -5475,6 +5478,12 @@ class Sema final : public SemaBase {
54755478

54765479
void DiagnoseUnknownAttribute(const ParsedAttr &AL);
54775480

5481+
/* TO_UPSTREAM(BoundsSafety) ON */
5482+
QualType
5483+
PostProcessBoundsSafetyAllocSizeAttribute(FunctionDecl *EnclosingDecl,
5484+
QualType FTy);
5485+
/* TO_UPSTREAM(BoundsSafety) OFF */
5486+
54785487
/// DeclClonePragmaWeak - clone existing decl (maybe definition),
54795488
/// \#pragma weak needs a non-definition decl and source may not have one.
54805489
NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II,

clang/lib/AST/ASTDiagnostic.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,11 @@ void clang::FormatASTNodeDiagnosticArgument(
519519
}
520520
}
521521

522+
/* TO_UPSTREAM(BoundsSafety) ON */
523+
if (Modifier == "unquoted")
524+
NeedQuotes = false;
525+
/* TO_UPSTREAM(BoundsSafety) OFF */
526+
522527
if (NeedQuotes) {
523528
Output.insert(Output.begin()+OldEnd, '\'');
524529
Output.push_back('\'');

clang/lib/Sema/BoundsSafetySuggestions.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -490,22 +490,6 @@ bool UnsafeOperationVisitor::FindSingleEntity(
490490
// Don't support indirect calls for now.
491491
return false;
492492
}
493-
if (DirectCallDecl->hasAttr<AllocSizeAttr>() &&
494-
IsReallySinglePtr(DirectCallDecl->getReturnType())) {
495-
// Functions declared like
496-
// void * custom_malloc(size_t s) __attribute__((alloc_size(1)))
497-
//
498-
// are currently are annotated as returning `void *__single` rather
499-
// than `void *__sized_by(s)`. To make the right thing happen at call
500-
// sites `BoundsSafetyPointerPromotionExpr` is used to generate a pointer
501-
// with the appropriate bounds from the `void *__single`. For functions
502-
// like this the warning needs to be suppressed because from the user's
503-
// perspective the returned value is not actually __single.
504-
//
505-
// This code path can be deleted once allocating functions are properly
506-
// annotated with __sized_by_or_null. rdar://117114186
507-
return false;
508-
}
509493

510494
assert(IsReallySinglePtr(DirectCallDecl->getReturnType()));
511495

0 commit comments

Comments
 (0)