Skip to content

Commit 203285a

Browse files
committed
[X86_64] Fix edge cases
Fix edge cases that may reading past the end of the register save area. Such as: ``` struct { long long a; struct {} b; }; ```
1 parent a1d5fff commit 203285a

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3129,16 +3129,39 @@ RValue X86_64ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
31293129
auto TInfo = getContext().getTypeInfoInChars(Ty);
31303130
uint64_t TySize = TInfo.Width.getQuantity();
31313131
CharUnits TyAlign = TInfo.Align;
3132+
llvm::Type *CoTy = nullptr;
3133+
if (AI.isDirect())
3134+
CoTy = AI.getCoerceToType();
31323135

31333136
llvm::Value *GpOrFpOffset = neededInt ? gp_offset : fp_offset;
31343137
uint64_t Alignment = neededInt ? 8 : 16;
3135-
if (auto Offset = AI.getDirectOffset()) {
3138+
uint64_t RegSize = neededInt ? neededInt * 8 : 16;
3139+
// There are two cases require special handling:
3140+
// 1)
3141+
// ```
3142+
// struct {
3143+
// struct {} a[8];
3144+
// int b;
3145+
// };
3146+
// ```
3147+
// The lower 8 bytes of the structure are not stored,
3148+
// so an 8-byte offset is needed when accessing the structure.
3149+
// 2)
3150+
// ```
3151+
// struct {
3152+
// long long a;
3153+
// struct {} b;
3154+
// };
3155+
// ```
3156+
// The stored size of this structure is smaller than its actual size,
3157+
// which may lead to reading past the end of the register save area.
3158+
if (CoTy && (AI.getDirectOffset() == 8 || RegSize < TySize)) {
31363159
Address Tmp = CGF.CreateMemTemp(Ty);
3137-
llvm::Type *TyHi = AI.getCoerceToType();
31383160
llvm::Value *Addr =
31393161
CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, GpOrFpOffset);
3140-
llvm::Value *Src = CGF.Builder.CreateAlignedLoad(TyHi, Addr, TyAlign);
3141-
llvm::Value *PtrOffset = llvm::ConstantInt::get(CGF.Int32Ty, Offset);
3162+
llvm::Value *Src = CGF.Builder.CreateAlignedLoad(CoTy, Addr, TyAlign);
3163+
llvm::Value *PtrOffset =
3164+
llvm::ConstantInt::get(CGF.Int32Ty, AI.getDirectOffset());
31423165
Address Dst = Address(
31433166
CGF.Builder.CreateGEP(CGF.Int8Ty, Tmp.getBasePointer(), PtrOffset),
31443167
LTy, TyAlign);

clang/test/CodeGenCXX/x86_64-vaarg.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,51 @@ s5 f5(int z, ...) {
261261
__builtin_va_start(list, z);
262262
return __builtin_va_arg(list, s5);
263263
}
264+
265+
typedef struct {
266+
long long a;
267+
struct{} b;
268+
} s6;
269+
270+
// CHECK-LABEL: @_Z2f6iz(
271+
// CHECK-NEXT: entry:
272+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S7:%.*]], align 8
273+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
274+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
275+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S7]], align 8
276+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
277+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
278+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
279+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
280+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
281+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
282+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
283+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
284+
// CHECK: vaarg.in_reg:
285+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
286+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
287+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
288+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
289+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 0
290+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 8
291+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
292+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
293+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
294+
// CHECK: vaarg.in_mem:
295+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
296+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
297+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
298+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
299+
// CHECK-NEXT: br label [[VAARG_END]]
300+
// CHECK: vaarg.end:
301+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
302+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
303+
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[STRUCT_S7]], ptr [[RETVAL]], i32 0, i32 0
304+
// CHECK-NEXT: [[TMP5:%.*]] = load i64, ptr [[COERCE_DIVE]], align 8
305+
// CHECK-NEXT: ret i64 [[TMP5]]
306+
//
307+
s6 f6(int z, ...) {
308+
__builtin_va_list list;
309+
__builtin_va_start(list, z);
310+
return __builtin_va_arg(list, s6);
311+
}

0 commit comments

Comments
 (0)