llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Bruno Cardoso Lopes (bcardosolopes)
<details>
<summary>Changes</summary>
…varargs
arrangeFreeFunctionLikeCall added getNumPassObjectSizeParams(proto) to the
extra-slot count it passed to RequiredArgs::getFromProtoWithExtraSlots, which
adds that same count itself. The pass_object_size slots were therefore counted
twice, and the resulting signature claimed more required arguments than the
CallArgList held.
requiredArguments() builds an ArrayRef of getNumRequiredArgs() entries over the
trailing-object array, so the over-count read past the end and handed a null
QualType to convertType -- a segfault in release, an assertion in +asserts. The
call had to supply zero variadic arguments for the counts to disagree; one or
more explicit varargs padded the array back over the required count and hid it.
```
extern int f(const char *__attribute__((pass_object_size(1))) s, ...);
int main(void) { f("x"); return 0; } // crashed in every -fclangir mode
```
Classic CodeGen passes only the genuine prefix count (0 here) to
RequiredArgs::forPrototypePlus; match that. Every other CIR caller of
getFromProtoWithExtraSlots already passes a prefix count, so this was the only
double-count.
Recent glibc declares the fortified printf family for clang exactly this way,
so on such a glibc any printf("...") with a constant format string and no
arguments crashed the compiler -- 9 of 515 coreutils TUs, per issue #<!--
-->214442.
Also assert the invariant in CIRGenFunctionInfo::create, so a future
miscomputation fails there instead of as a null QualType deep in convertType.
Fixes #<!-- -->214442.
---
Full diff: https://github.com/llvm/llvm-project/pull/214928.diff
2 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+14-2)
- (modified) clang/test/CIR/CodeGen/pass-object-size.c (+48)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 940294c923179..d3b3a8a471dbf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -43,6 +43,14 @@ CIRGenFunctionInfo *CIRGenFunctionInfo::create(
fi->required = required;
fi->numArgs = argTypes.size();
+ // requiredArguments() reads getNumRequiredArgs() entries out of the
+ // trailing-object array, so a signature that claims more required arguments
+ // than we have types for reads past the end and hands a null QualType to
+ // convertType.
+ assert((!required.allowsOptionalArgs() ||
+ required.getNumRequiredArgs() <= fi->numArgs) &&
+ "more required arguments than argument types");
+
fi->getArgTypes()[0] = resultType;
std::copy(argTypes.begin(), argTypes.end(), fi->argTypesBegin());
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
@@ -930,9 +938,13 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule
&cgm,
RequiredArgs required = RequiredArgs::All;
if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
- unsigned numExtraSlots = getNumPassObjectSizeParams(proto);
+ // A free function call has no extra prefix arguments. Note that
+ // getFromProtoWithExtraSlots already accounts for the prototype's
+ // pass_object_size parameters; adding them here too would double-count
+ // them and make the signature claim more required arguments than `args`
+ // actually holds.
if (proto->isVariadic())
- required = RequiredArgs::getFromProtoWithExtraSlots(proto,
numExtraSlots);
+ required = RequiredArgs::getFromProtoWithExtraSlots(proto, 0);
} else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
cast<FunctionNoProtoType>(fnType)))
cgm.errorNYI("call to function without a prototype");
diff --git a/clang/test/CIR/CodeGen/pass-object-size.c
b/clang/test/CIR/CodeGen/pass-object-size.c
index 23b95e8f38951..c6166bc992fae 100644
--- a/clang/test/CIR/CodeGen/pass-object-size.c
+++ b/clang/test/CIR/CodeGen/pass-object-size.c
@@ -63,3 +63,51 @@ void test_vla(int n) {
// OGCG: call void @b(ptr noundef %[[VLA]], i64 noundef %[[SIZE1]])
// OGCG: %[[SIZE2:.*]] = call i64 @llvm.objectsize.i64.p0(ptr %[[VLA]], i1
true, i1 true, i1 false)
// OGCG: call void @e(ptr noundef %[[VLA]], i64 noundef %[[SIZE2]])
+
+// A pass_object_size parameter on a variadic callee occupies one of the
+// signature's required argument slots. Calling such a function with no
+// variadic arguments must still work -- this is how glibc declares the
+// fortified printf family for clang, so printf("hello") hits it.
+
+void v(void *__attribute__((pass_object_size(0))), ...);
+
+void test_variadic_no_varargs(void) {
+ int a;
+ v(&a);
+}
+
+// CIR: cir.func private @v(!cir.ptr<!void> {llvm.noundef}, !u64i
{llvm.noundef}, ...)
+
+// CIR: cir.func {{.*}} @test_variadic_no_varargs()
+// CIR: %[[ALLOCA:.*]] = cir.alloca {{.*}} : !cir.ptr<!s32i>
+// CIR: %[[CAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!s32i> ->
!cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<4> : !u64i
+// CIR: cir.call @v(%[[CAST]], %[[SIZE]]) : (!cir.ptr<!void> {{.*}}, !u64i
{{.*}}) -> ()
+
+// LLVM: define dso_local void @test_variadic_no_varargs()
+// LLVM: %[[ALLOCA:.*]] = alloca i32
+// LLVM: call void (ptr, i64, ...) @v(ptr noundef %[[ALLOCA]], i64 noundef 4)
+
+// OGCG: define dso_local void @test_variadic_no_varargs()
+// OGCG: %[[A:.*]] = alloca i32
+// OGCG: call void (ptr, i64, ...) @v(ptr noundef %[[A]], i64 noundef 4)
+
+void test_variadic_with_varargs(void) {
+ int a;
+ v(&a, 1);
+}
+
+// CIR: cir.func {{.*}} @test_variadic_with_varargs()
+// CIR: %[[ALLOCA:.*]] = cir.alloca {{.*}} : !cir.ptr<!s32i>
+// CIR: %[[CAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!s32i> ->
!cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<4> : !u64i
+// CIR: %[[ARG:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.call @v(%[[CAST]], %[[SIZE]], %[[ARG]]) : (!cir.ptr<!void>
{{.*}}, !u64i {{.*}}, !s32i {{.*}}) -> ()
+
+// LLVM: define dso_local void @test_variadic_with_varargs()
+// LLVM: %[[ALLOCA:.*]] = alloca i32
+// LLVM: call void (ptr, i64, ...) @v(ptr noundef %[[ALLOCA]], i64 noundef
4, i32 noundef 1)
+
+// OGCG: define dso_local void @test_variadic_with_varargs()
+// OGCG: %[[A:.*]] = alloca i32
+// OGCG: call void (ptr, i64, ...) @v(ptr noundef %[[A]], i64 noundef 4, i32
noundef 1)
``````````
</details>
https://github.com/llvm/llvm-project/pull/214928
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits