[clang] [clang][Sema] Fixes for %b printf extension handling (PR #120689)
Brad Smith via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 20 00:48:35 PST 2024
https://github.com/brad0 updated https://github.com/llvm/llvm-project/pull/120689
>From 67a87a492c0b84f4d76ae4818658969cd11a379e Mon Sep 17 00:00:00 2001
From: Brad Smith <brad at comstyle.com>
Date: Thu, 19 Dec 2024 21:35:57 -0500
Subject: [PATCH] [clang][Sema] Fixes for %b printf extension handling
The %b printf extension in the kernel is not fixed to a int type. On
sparc64 there are various %llb formats. Adjust the code to handle the
length specifiers and type check like it is used by the regular case.
---
clang/include/clang/AST/FormatString.h | 6 ++--
clang/lib/AST/FormatString.cpp | 9 ++++++
clang/lib/Sema/SemaChecking.cpp | 38 +++++++++++++++++-------
clang/test/Sema/format-strings-freebsd.c | 8 ++++-
4 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index a074dd23e2ad4c..1ff0d036b70d1a 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -240,8 +240,10 @@ class ConversionSpecifier {
bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) ||
kind == FreeBSDrArg || kind == FreeBSDyArg; }
- bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; }
- bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; }
+ bool isUIntArg() const { return (kind >= UIntArgBeg && kind <= UIntArgEnd) ||
+ kind == FreeBSDbArg; }
+ bool isAnyIntArg() const { return (kind >= IntArgBeg && kind <= UIntArgEnd) ||
+ kind == FreeBSDbArg; }
bool isDoubleArg() const {
return kind >= DoubleArgBeg && kind <= DoubleArgEnd;
}
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index e892c1592df986..422fee7ab5d857 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -879,6 +879,10 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target,
case ConversionSpecifier::XArg:
case ConversionSpecifier::nArg:
return true;
+ case ConversionSpecifier::FreeBSDbArg:
+ return Target.getTriple().isOSFreeBSD() ||
+ Target.getTriple().isPS() ||
+ Target.getTriple().isOSOpenBSD();
case ConversionSpecifier::FreeBSDrArg:
case ConversionSpecifier::FreeBSDyArg:
return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS();
@@ -919,6 +923,10 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target,
case ConversionSpecifier::ScanListArg:
case ConversionSpecifier::ZArg:
return true;
+ case ConversionSpecifier::FreeBSDbArg:
+ return Target.getTriple().isOSFreeBSD() ||
+ Target.getTriple().isPS() ||
+ Target.getTriple().isOSOpenBSD();
case ConversionSpecifier::FreeBSDrArg:
case ConversionSpecifier::FreeBSDyArg:
return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS();
@@ -1085,6 +1093,7 @@ bool FormatSpecifier::hasStandardLengthConversionCombination() const {
case ConversionSpecifier::uArg:
case ConversionSpecifier::xArg:
case ConversionSpecifier::XArg:
+ case ConversionSpecifier::FreeBSDbArg:
return false;
default:
return true;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e703a62ff9cf18..e50118762fff32 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7170,18 +7170,34 @@ bool CheckPrintfHandler::HandlePrintfSpecifier(
// Claim the second argument.
CoveredArgs.set(argIndex + 1);
- // Type check the first argument (int for %b, pointer for %D)
const Expr *Ex = getDataArg(argIndex);
- const analyze_printf::ArgType &AT =
- (CS.getKind() == ConversionSpecifier::FreeBSDbArg) ?
- ArgType(S.Context.IntTy) : ArgType::CPointerTy;
- if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
- << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
- << false << Ex->getSourceRange(),
- Ex->getBeginLoc(), /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
+ if (CS.getKind() == ConversionSpecifier::FreeBSDDArg) {
+ // Type check the first argument (pointer for %D)
+ const analyze_printf::ArgType &AT = ArgType::CPointerTy;
+ if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
+ << false << Ex->getSourceRange(),
+ Ex->getBeginLoc(), /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ } else {
+ // Check the length modifier for %b
+ if (!FS.hasValidLengthModifier(S.getASTContext().getTargetInfo(),
+ S.getLangOpts()))
+ HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_nonsensical_length);
+ else if (!FS.hasStandardLengthModifier())
+ HandleNonStandardLengthModifier(FS, startSpecifier, specifierLen);
+ else if (!FS.hasStandardLengthConversionCombination())
+ HandleInvalidLengthModifier(
+ FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_non_standard_conversion_spec);
+
+ // Type check the first argument of %b
+ if (!checkFormatExpr(FS, startSpecifier, specifierLen, Ex))
+ return false;
+ }
// Type check the second argument (char * for both %b and %D)
Ex = getDataArg(argIndex + 1);
diff --git a/clang/test/Sema/format-strings-freebsd.c b/clang/test/Sema/format-strings-freebsd.c
index 3d3ed6b40f48dc..3540dca2cfabcd 100644
--- a/clang/test/Sema/format-strings-freebsd.c
+++ b/clang/test/Sema/format-strings-freebsd.c
@@ -8,13 +8,19 @@ int freebsd_kernel_printf(const char *, ...) __attribute__((__format__(__freebsd
void check_freebsd_kernel_extensions(int i, long l, char *s, short h)
{
- // %b expects an int and a char *
+ // %b expects a char *
freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n"); // no-warning
freebsd_kernel_printf("reg=%b\n", l, "\10\2BITTWO\1BITONE\n"); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
freebsd_kernel_printf("reg=%b\n", i, l); // expected-warning{{format specifies type 'char *' but the argument has type 'long'}}
freebsd_kernel_printf("reg=%b\n", i); // expected-warning{{more '%' conversions than data arguments}}
freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n", l); // expected-warning{{data argument not used by format string}}
+ freebsd_kernel_printf("reg=%llb\n", i, "\10\2BITTWO\1BITONE\n"); // expected-warning{{format specifies type 'long long' but the argument has type 'int'}}
+ freebsd_kernel_printf("reg=%llb\n", ll, "\10\2BITTWO\1BITONE\n"); // no-warning
+ freebsd_kernel_printf("reg=%llb\n", ll, i); // expected-warning{{format specifies type 'char *' but the argument has type 'long long'}}
+ freebsd_kernel_printf("reg=%llb\n", ll); // expected-warning{{more '%' conversions than data arguments}}
+ freebsd_kernel_printf("reg=%llb\n", ll, "\10\2BITTWO\1BITONE\n", l); // expected-warning{{data argument not used by format string}}
+
// %D expects an unsigned char * and a char *
freebsd_kernel_printf("%6D", s, ":"); // no-warning
freebsd_kernel_printf("%6D", i, ":"); // expected-warning{{format specifies type 'void *' but the argument has type 'int'}}
More information about the cfe-commits
mailing list