Skip to content

Commit bc5f2d1

Browse files
author
Michael Benfield
committed
[clang] diagnose_as_builtin attribute for Fortify diagnosing like builtins.
Differential Revision: https://reviews.llvm.org/D112024
1 parent fd5e493 commit bc5f2d1

File tree

8 files changed

+319
-11
lines changed

8 files changed

+319
-11
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3865,6 +3865,14 @@ def ReleaseHandle : InheritableParamAttr {
38653865
let Documentation = [ReleaseHandleDocs];
38663866
}
38673867

3868+
def DiagnoseAsBuiltin : InheritableAttr {
3869+
let Spellings = [Clang<"diagnose_as_builtin">];
3870+
let Args = [DeclArgument<Function, "Function">,
3871+
VariadicUnsignedArgument<"ArgIndices">];
3872+
let Subjects = SubjectList<[Function]>;
3873+
let Documentation = [DiagnoseAsBuiltinDocs];
3874+
}
3875+
38683876
def Builtin : InheritableAttr {
38693877
let Spellings = [];
38703878
let Args = [UnsignedArgument<"ID">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5984,6 +5984,50 @@ attribute requires a string literal argument to identify the handle being releas
59845984
}];
59855985
}
59865986

5987+
def DiagnoseAsBuiltinDocs : Documentation {
5988+
let Category = DocCatFunction;
5989+
let Content = [{
5990+
The ``diagnose_as_builtin` attribute indicates that Fortify diagnostics are to
5991+
be applied to the declared function as if it were the function specified by the
5992+
attribute. The builtin function whose diagnostics are to be mimicked should be
5993+
given. In addition, the order in which arguments should be applied must also
5994+
be given.
5995+
5996+
For example, the attribute can be used as follows.
5997+
5998+
.. code-block:: c
5999+
6000+
__attribute__((diagnose_as_builtin(__builtin_memset, 3, 2, 1)))
6001+
void *mymemset(int n, int c, void *s) {
6002+
// ...
6003+
}
6004+
6005+
This indicates that calls to ``mymemset`` should be diagnosed as if they were
6006+
calls to ``__builtin_memset``. The arguments ``3, 2, 1`` indicate by index the
6007+
order in which arguments of ``mymemset`` should be applied to
6008+
``__builtin_memset``. The third argument should be applied first, then the
6009+
second, and then the first. Thus (when Fortify warnings are enabled) the call
6010+
``mymemset(n, c, s)`` will diagnose overflows as if it were the call
6011+
``__builtin_memset(s, c, n)``.
6012+
6013+
For variadic functions, the variadic arguments must come in the same order as
6014+
they would to the builtin function, after all normal arguments. For instance,
6015+
to diagnose a new function as if it were `sscanf`, we can use the attribute as
6016+
follows.
6017+
6018+
.. code-block:: c
6019+
__attribute__((diagnose_as_builtin(sscanf, 1, 2)))
6020+
int mysscanf(const char *str, const char *format, ...) {
6021+
// ...
6022+
}
6023+
6024+
Then the call `mysscanf("abc def", "%4s %4s", buf1, buf2)` will be diagnosed as
6025+
if it were the call `sscanf("abc def", "%4s %4s", buf1, buf2)`.
6026+
6027+
This attribute cannot be applied to non-static member functions.
6028+
}];
6029+
}
6030+
59876031
def ArmSveVectorBitsDocs : Documentation {
59886032
let Category = DocCatType;
59896033
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2956,6 +2956,17 @@ def err_attribute_invalid_argument : Error<
29562956
def err_attribute_wrong_number_arguments : Error<
29572957
"%0 attribute %plural{0:takes no arguments|1:takes one argument|"
29582958
":requires exactly %1 arguments}1">;
2959+
def err_attribute_wrong_number_arguments_for : Error <
2960+
"%0 attribute references function %1, which %plural{0:takes no arguments|1:takes one argument|"
2961+
":takes exactly %2 arguments}2">;
2962+
def err_attribute_bounds_for_function : Error<
2963+
"%0 attribute references parameter %1, but the function %2 has only %3 parameters">;
2964+
def err_attribute_no_member_function : Error<
2965+
"%0 attribute cannot be applied to non-static member functions">;
2966+
def err_attribute_parameter_types : Error<
2967+
"%0 attribute parameter types do not match: parameter %1 of function %2 has type %3, "
2968+
"but parameter %4 of function %5 has type %6">;
2969+
29592970
def err_attribute_too_many_arguments : Error<
29602971
"%0 attribute takes no more than %1 argument%s1">;
29612972
def err_attribute_too_few_arguments : Error<
@@ -3013,7 +3024,7 @@ def err_attribute_sizeless_type : Error<
30133024
"%0 attribute cannot be applied to sizeless type %1">;
30143025
def err_attribute_argument_n_type : Error<
30153026
"%0 attribute requires parameter %1 to be %select{int or bool|an integer "
3016-
"constant|a string|an identifier|a constant expression}2">;
3027+
"constant|a string|an identifier|a constant expression|a builtin function}2">;
30173028
def err_attribute_argument_type : Error<
30183029
"%0 attribute requires %select{int or bool|an integer "
30193030
"constant|a string|an identifier}1">;

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@ enum AttributeArgumentNType {
10971097
AANT_ArgumentString,
10981098
AANT_ArgumentIdentifier,
10991099
AANT_ArgumentConstantExpr,
1100+
AANT_ArgumentBuiltinFunction,
11001101
};
11011102

11021103
/// These constants match the enumerated choices of

clang/lib/Sema/SemaChecking.cpp

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,14 @@ class ScanfDiagnosticFormatHandler
446446
break;
447447
}
448448

449-
auto OptionalFW = FS.getFieldWidth();
450-
if (OptionalFW.getHowSpecified() !=
449+
analyze_format_string::OptionalAmount FW = FS.getFieldWidth();
450+
if (FW.getHowSpecified() !=
451451
analyze_format_string::OptionalAmount::HowSpecified::Constant)
452452
return true;
453453

454-
unsigned SourceSize = OptionalFW.getConstantAmount() + NulByte;
454+
unsigned SourceSize = FW.getConstantAmount() + NulByte;
455455

456-
auto DestSizeAPS = ComputeSizeArgument(FS.getArgIndex());
456+
Optional<llvm::APSInt> DestSizeAPS = ComputeSizeArgument(FS.getArgIndex());
457457
if (!DestSizeAPS)
458458
return true;
459459

@@ -652,20 +652,53 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
652652
isConstantEvaluated())
653653
return;
654654

655-
unsigned BuiltinID = FD->getBuiltinID(/*ConsiderWrappers=*/true);
655+
bool UseDABAttr = false;
656+
const FunctionDecl *UseDecl = FD;
657+
658+
const auto *DABAttr = FD->getAttr<DiagnoseAsBuiltinAttr>();
659+
if (DABAttr) {
660+
UseDecl = DABAttr->getFunction();
661+
assert(UseDecl && "Missing FunctionDecl in DiagnoseAsBuiltin attribute!");
662+
UseDABAttr = true;
663+
}
664+
665+
unsigned BuiltinID = UseDecl->getBuiltinID(/*ConsiderWrappers=*/true);
666+
656667
if (!BuiltinID)
657668
return;
658669

659670
const TargetInfo &TI = getASTContext().getTargetInfo();
660671
unsigned SizeTypeWidth = TI.getTypeWidth(TI.getSizeType());
661672

673+
auto TranslateIndex = [&](unsigned Index) -> Optional<unsigned> {
674+
// If we refer to a diagnose_as_builtin attribute, we need to change the
675+
// argument index to refer to the arguments of the called function. Unless
676+
// the index is out of bounds, which presumably means it's a variadic
677+
// function.
678+
if (!UseDABAttr)
679+
return Index;
680+
unsigned DABIndices = DABAttr->argIndices_size();
681+
unsigned NewIndex = Index < DABIndices
682+
? DABAttr->argIndices_begin()[Index]
683+
: Index - DABIndices + FD->getNumParams();
684+
if (NewIndex >= TheCall->getNumArgs())
685+
return llvm::None;
686+
return NewIndex;
687+
};
688+
662689
auto ComputeExplicitObjectSizeArgument =
663690
[&](unsigned Index) -> Optional<llvm::APSInt> {
691+
Optional<unsigned> IndexOptional = TranslateIndex(Index);
692+
if (!IndexOptional)
693+
return llvm::None;
694+
unsigned NewIndex = IndexOptional.getValue();
664695
Expr::EvalResult Result;
665-
Expr *SizeArg = TheCall->getArg(Index);
696+
Expr *SizeArg = TheCall->getArg(NewIndex);
666697
if (!SizeArg->EvaluateAsInt(Result, getASTContext()))
667698
return llvm::None;
668-
return Result.Val.getInt();
699+
llvm::APSInt Integer = Result.Val.getInt();
700+
Integer.setIsUnsigned(true);
701+
return Integer;
669702
};
670703

671704
auto ComputeSizeArgument = [&](unsigned Index) -> Optional<llvm::APSInt> {
@@ -680,7 +713,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
680713
BOSType = POS->getType();
681714
}
682715

683-
const Expr *ObjArg = TheCall->getArg(Index);
716+
Optional<unsigned> IndexOptional = TranslateIndex(Index);
717+
if (!IndexOptional)
718+
return llvm::None;
719+
unsigned NewIndex = IndexOptional.getValue();
720+
721+
const Expr *ObjArg = TheCall->getArg(NewIndex);
684722
uint64_t Result;
685723
if (!ObjArg->tryEvaluateObjectSize(Result, getASTContext(), BOSType))
686724
return llvm::None;
@@ -690,7 +728,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
690728
};
691729

692730
auto ComputeStrLenArgument = [&](unsigned Index) -> Optional<llvm::APSInt> {
693-
Expr *ObjArg = TheCall->getArg(Index);
731+
Optional<unsigned> IndexOptional = TranslateIndex(Index);
732+
if (!IndexOptional)
733+
return llvm::None;
734+
unsigned NewIndex = IndexOptional.getValue();
735+
736+
const Expr *ObjArg = TheCall->getArg(NewIndex);
694737
uint64_t Result;
695738
if (!ObjArg->tryEvaluateStrLen(Result, getASTContext()))
696739
return llvm::None;
@@ -898,7 +941,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
898941
}
899942

900943
if (!SourceSize || !DestinationSize ||
901-
SourceSize.getValue().ule(DestinationSize.getValue()))
944+
llvm::APSInt::compareValues(SourceSize.getValue(),
945+
DestinationSize.getValue()) <= 0)
902946
return;
903947

904948
StringRef FunctionName = GetFunctionName();

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,84 @@ class ArgumentDependenceChecker
10011001
};
10021002
}
10031003

1004+
static void handleDiagnoseAsBuiltinAttr(Sema &S, Decl *D,
1005+
const ParsedAttr &AL) {
1006+
const auto *DeclFD = cast<FunctionDecl>(D);
1007+
1008+
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(DeclFD))
1009+
if (!MethodDecl->isStatic()) {
1010+
S.Diag(AL.getLoc(), diag::err_attribute_no_member_function) << AL;
1011+
return;
1012+
}
1013+
1014+
auto DiagnoseType = [&](unsigned Index, AttributeArgumentNType T) {
1015+
SourceLocation Loc = [&]() {
1016+
auto Union = AL.getArg(Index - 1);
1017+
if (Union.is<Expr *>())
1018+
return Union.get<Expr *>()->getBeginLoc();
1019+
return Union.get<IdentifierLoc *>()->Loc;
1020+
}();
1021+
1022+
S.Diag(Loc, diag::err_attribute_argument_n_type) << AL << Index << T;
1023+
};
1024+
1025+
FunctionDecl *AttrFD = [&]() -> FunctionDecl * {
1026+
if (!AL.isArgExpr(0))
1027+
return nullptr;
1028+
auto *F = dyn_cast_or_null<DeclRefExpr>(AL.getArgAsExpr(0));
1029+
if (!F)
1030+
return nullptr;
1031+
return dyn_cast_or_null<FunctionDecl>(F->getFoundDecl());
1032+
}();
1033+
1034+
if (!AttrFD || !AttrFD->getBuiltinID(true)) {
1035+
DiagnoseType(1, AANT_ArgumentBuiltinFunction);
1036+
return;
1037+
}
1038+
1039+
if (AttrFD->getNumParams() != AL.getNumArgs() - 1) {
1040+
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments_for)
1041+
<< AL << AttrFD << AttrFD->getNumParams();
1042+
return;
1043+
}
1044+
1045+
SmallVector<unsigned, 8> Indices;
1046+
1047+
for (unsigned I = 1; I < AL.getNumArgs(); ++I) {
1048+
if (!AL.isArgExpr(I)) {
1049+
DiagnoseType(I + 1, AANT_ArgumentIntegerConstant);
1050+
return;
1051+
}
1052+
1053+
const Expr *IndexExpr = AL.getArgAsExpr(I);
1054+
uint32_t Index;
1055+
1056+
if (!checkUInt32Argument(S, AL, IndexExpr, Index, I + 1, false))
1057+
return;
1058+
1059+
if (Index > DeclFD->getNumParams()) {
1060+
S.Diag(AL.getLoc(), diag::err_attribute_bounds_for_function)
1061+
<< AL << Index << DeclFD << DeclFD->getNumParams();
1062+
return;
1063+
}
1064+
1065+
QualType T1 = AttrFD->getParamDecl(I - 1)->getType();
1066+
QualType T2 = DeclFD->getParamDecl(Index - 1)->getType();
1067+
1068+
if (T1.getCanonicalType().getUnqualifiedType() !=
1069+
T2.getCanonicalType().getUnqualifiedType()) {
1070+
S.Diag(IndexExpr->getBeginLoc(), diag::err_attribute_parameter_types)
1071+
<< AL << Index << DeclFD << T2 << I << AttrFD << T1;
1072+
return;
1073+
}
1074+
1075+
Indices.push_back(Index - 1);
1076+
}
1077+
1078+
D->addAttr(::new (S.Context) DiagnoseAsBuiltinAttr(
1079+
S.Context, AL, AttrFD, Indices.data(), Indices.size()));
1080+
}
1081+
10041082
static void handleDiagnoseIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
10051083
S.Diag(AL.getLoc(), diag::ext_clang_diagnose_if);
10061084

@@ -8159,6 +8237,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
81598237
case ParsedAttr::AT_DiagnoseIf:
81608238
handleDiagnoseIfAttr(S, D, AL);
81618239
break;
8240+
case ParsedAttr::AT_DiagnoseAsBuiltin:
8241+
handleDiagnoseAsBuiltinAttr(S, D, AL);
8242+
break;
81628243
case ParsedAttr::AT_NoBuiltin:
81638244
handleNoBuiltinAttr(S, D, AL);
81648245
break;

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
5858
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
5959
// CHECK-NEXT: Destructor (SubjectMatchRule_function)
60+
// CHECK-NEXT: DiagnoseAsBuiltin (SubjectMatchRule_function)
6061
// CHECK-NEXT: DisableSanitizerInstrumentation (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global)
6162
// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method)
6263
// CHECK-NEXT: EnableIf (SubjectMatchRule_function)

0 commit comments

Comments
 (0)