Skip to content

Commit f98c1fa

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 efff3ad commit f98c1fa

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
@@ -3444,6 +3444,9 @@ def err_attribute_only_once_per_parameter : Error<
34443444
"%0 attribute can only be applied once per parameter">;
34453445
def err_mismatched_uuid : Error<"uuid does not match previous declaration">;
34463446
def note_previous_uuid : Note<"previous uuid specified here">;
3447+
/* TO_UPSTREAM(BoundsSafety) ON */
3448+
def err_mismatched_alloc_size : Error<"'alloc_size' attribute does not match previous declaration">;
3449+
/* TO_UPSTREAM(BoundsSafety) OFF */
34473450
def warn_attribute_pointers_only : Warning<
34483451
"%0 attribute only applies to%select{| constant}1 pointer arguments">,
34493452
InGroup<IgnoredAttributes>;
@@ -12808,7 +12811,19 @@ let CategoryName = "BoundsSafety Pointer Attributes Issue" in {
1280812811
def err_bounds_safety_conflicting_pointer_attributes : Error<
1280912812
"%select{array|pointer}0 cannot have more than one %select{bound|type|count|end|terminator}1 attribute">;
1281012813
def note_bounds_safety_conflicting_pointer_attribute_args : Note<
12811-
"conflicting arguments for %select{count|end|terminator}0 were %1 and %2">;
12814+
"conflicting arguments for %select{end|terminator}0 were %1 and %2">;
12815+
def warn_bounds_safety_ignored_implicit_sized_by_or_null : Warning<
12816+
"implicit __sized_by_or_null attribute ignored because of explicit __sized_by">;
12817+
def note_bounds_safety_overriding_implicit_sized_by_or_null_silence : Note<
12818+
"add _Nonnull qualifier to return type to silence this warning">;
12819+
def note_bounds_safety_conflicting_count_attribute : Note<
12820+
"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)'">;
12821+
def note_bounds_safety_implicit_size_from_alloc_size_attr : Note<
12822+
"function return type implicitly __sized_by%select{|_or_null}0 because of the function's 'alloc_size' %select{and 'returns_nonnull' attributes|attribute}0">;
12823+
def err_invalid_return_type_for_alloc_size : Error<
12824+
"invalid return type %0 for function with alloc_size attribute; '__sized_by_or_null(%unquoted1)' or '__sized_by(%unquoted1)' required">;
12825+
def note_attribute_inherited : Note<
12826+
"attribute inherited from previous declaration here">;
1281212827
def warn_bounds_safety_duplicate_pointer_attributes : Warning<
1281312828
"%select{array|pointer}0 annotated with %select{__unsafe_indexable|__bidi_indexable|__indexable|__single|__terminated_by}1 "
1281412829
"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
@@ -5390,6 +5390,9 @@ class Sema final : public SemaBase {
53905390
MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI);
53915391
OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D,
53925392
const AttributeCommonInfo &CI);
5393+
/* TO_UPSTREAM(BoundsSafety) ON */
5394+
AllocSizeAttr *mergeAllocSizeAttr(NamedDecl *D, const AllocSizeAttr &ASA);
5395+
/* TO_UPSTREAM(BoundsSafety) OFF */
53935396
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL);
53945397
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D,
53955398
const InternalLinkageAttr &AL);
@@ -5485,6 +5488,12 @@ class Sema final : public SemaBase {
54855488

54865489
void DiagnoseUnknownAttribute(const ParsedAttr &AL);
54875490

5491+
/* TO_UPSTREAM(BoundsSafety) ON */
5492+
QualType
5493+
PostProcessBoundsSafetyAllocSizeAttribute(FunctionDecl *EnclosingDecl,
5494+
QualType FTy);
5495+
/* TO_UPSTREAM(BoundsSafety) OFF */
5496+
54885497
/// DeclClonePragmaWeak - clone existing decl (maybe definition),
54895498
/// \#pragma weak needs a non-definition decl and source may not have one.
54905499
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
@@ -541,6 +541,11 @@ void clang::FormatASTNodeDiagnosticArgument(
541541
}
542542
}
543543

544+
/* TO_UPSTREAM(BoundsSafety) ON */
545+
if (Modifier == "unquoted")
546+
NeedQuotes = false;
547+
/* TO_UPSTREAM(BoundsSafety) OFF */
548+
544549
if (NeedQuotes) {
545550
Output.insert(Output.begin()+OldEnd, '\'');
546551
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)