Skip to content

Commit a238174

Browse files
ahatanakahatanaka
authored andcommitted
[os_log] Fix a CodeGen crash that occurs when arguments of struct, class, or complex types are passed to _builtin_os_log_format (llvm#158744)
This change fixes the crash in clang's CodeGen by erroring out in Sema if those arguments are passed. rdar://139824423
1 parent d113e76 commit a238174

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10461,6 +10461,9 @@ def warn_format_conversion_argument_type_mismatch : Warning<
1046110461
"format specifies type %0 but the argument has "
1046210462
"%select{type|underlying type}2 %1">,
1046310463
InGroup<Format>;
10464+
def err_format_conversion_argument_type_mismatch : Error<
10465+
"format specifies type %0 but the argument has "
10466+
"%select{type|underlying type}2 %1">;
1046410467
def warn_format_conversion_argument_type_mismatch_pedantic : Extension<
1046510468
warn_format_conversion_argument_type_mismatch.Summary>,
1046610469
InGroup<FormatPedantic>;
@@ -10509,6 +10512,8 @@ def warn_printf_asterisk_missing_arg : Warning<
1050910512
def warn_printf_asterisk_wrong_type : Warning<
1051010513
"field %select{width|precision}0 should have type %1, but argument has type %2">,
1051110514
InGroup<Format>;
10515+
def err_printf_asterisk_wrong_type : Error<
10516+
"field %select{width|precision}0 should have type %1, but argument has type %2">;
1051210517
def warn_printf_nonsensical_optional_amount: Warning<
1051310518
"%select{field width|precision}0 used with '%1' conversion specifier, resulting in undefined behavior">,
1051410519
InGroup<Format>;

clang/lib/Sema/SemaChecking.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7479,6 +7479,14 @@ void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) {
74797479
S.Diag(getLocationOfByte(MaskType.data()), diag::err_invalid_mask_type_size);
74807480
}
74817481

7482+
// Error out if struct or complex type argments are passed to os_log.
7483+
static bool isInvalidOSLogArgTypeForCodeGen(FormatStringType FSType,
7484+
QualType T) {
7485+
if (FSType != FormatStringType::OSLog)
7486+
return false;
7487+
return T->isRecordType() || T->isComplexType();
7488+
}
7489+
74827490
bool CheckPrintfHandler::HandleAmount(
74837491
const analyze_format_string::OptionalAmount &Amt, unsigned k,
74847492
const char *startSpecifier, unsigned specifierLen) {
@@ -7511,11 +7519,14 @@ bool CheckPrintfHandler::HandleAmount(
75117519
assert(AT.isValid());
75127520

75137521
if (!AT.matchesType(S.Context, T)) {
7514-
EmitFormatDiagnostic(S.PDiag(diag::warn_printf_asterisk_wrong_type)
7515-
<< k << AT.getRepresentativeTypeName(S.Context)
7516-
<< T << Arg->getSourceRange(),
7522+
unsigned DiagID = isInvalidOSLogArgTypeForCodeGen(FSType, T)
7523+
? diag::err_printf_asterisk_wrong_type
7524+
: diag::warn_printf_asterisk_wrong_type;
7525+
EmitFormatDiagnostic(S.PDiag(DiagID)
7526+
<< k << AT.getRepresentativeTypeName(S.Context)
7527+
<< T << Arg->getSourceRange(),
75177528
getLocationOfByte(Amt.getStart()),
7518-
/*IsStringLocation*/true,
7529+
/*IsStringLocation*/ true,
75197530
getSpecifierRange(startSpecifier, specifierLen));
75207531
// Don't do any more checking. We will just emit
75217532
// spurious errors.
@@ -8570,7 +8581,9 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
85708581
Diag = diag::warn_format_conversion_argument_type_mismatch_confusion;
85718582
break;
85728583
case ArgType::NoMatch:
8573-
Diag = diag::warn_format_conversion_argument_type_mismatch;
8584+
Diag = isInvalidOSLogArgTypeForCodeGen(FSType, ExprTy)
8585+
? diag::err_format_conversion_argument_type_mismatch
8586+
: diag::warn_format_conversion_argument_type_mismatch;
85748587
break;
85758588
}
85768589

clang/test/SemaObjC/os_log.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_cc1 -verify %s
2+
3+
struct S {
4+
int a[4];
5+
};
6+
7+
struct S s;
8+
_Complex float cf;
9+
10+
void test_builtin_os_log_invalid_arg(void *buf) {
11+
__builtin_os_log_format(buf, "%*.*f", s, 5, 1.3); // expected-error {{field width should have type 'int', but argument has type 'struct S'}}
12+
__builtin_os_log_format(buf, "%*.*f", 1, s, 1.3); // expected-error {{field precision should have type 'int', but argument has type 'struct S'}}
13+
__builtin_os_log_format(buf, "%*.*f", 1, 5, s); // expected-error {{format specifies type 'double' but the argument has type 'struct S'}}
14+
15+
__builtin_os_log_format(buf, "%*.*f", cf, 5, 1.3); // expected-error {{field width should have type 'int', but argument has type '_Complex float'}}
16+
__builtin_os_log_format(buf, "%*.*f", 1, cf, 1.3); // expected-error {{field precision should have type 'int', but argument has type '_Complex float'}}
17+
__builtin_os_log_format(buf, "%*.*f", 1, 5, cf); // expected-error {{format specifies type 'double' but the argument has type '_Complex float'}}
18+
19+
__builtin_os_log_format(buf, "%*.*f", (void *)0, 5, 1.3); // expected-warning {{field width should have type 'int', but argument has type 'void *'}}
20+
__builtin_os_log_format(buf, "%*.*f", 1, (void *)0, 1.3); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
21+
__builtin_os_log_format(buf, "%*.*f", 1, 5, (void *)0); // expected-warning {{format specifies type 'double' but the argument has type 'void *'}}
22+
}

0 commit comments

Comments
 (0)