Skip to content

Commit f43535f

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; }; ``` and ``` struct { long long a; int b; }; ```
1 parent 328ae60 commit f43535f

File tree

2 files changed

+178
-4
lines changed

2 files changed

+178
-4
lines changed

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3129,16 +3129,47 @@ 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 = AI.getCoerceToType();
3133+
uint64_t RegSize = 0;
3134+
// Obtain the actual register storage size.
3135+
if (CoTy->isStructTy()) {
3136+
auto ST = cast<llvm::StructType>(CoTy);
3137+
for (unsigned i = 0; i < ST->getNumElements(); ++i) {
3138+
auto elementType = ST->getElementType(i);
3139+
RegSize += CGF.CGM.getDataLayout().getTypeAllocSize(elementType);
3140+
}
3141+
} else {
3142+
RegSize = CGF.CGM.getDataLayout().getTypeAllocSize(CoTy);
3143+
}
31323144

31333145
llvm::Value *GpOrFpOffset = neededInt ? gp_offset : fp_offset;
31343146
uint64_t Alignment = neededInt ? 8 : 16;
3135-
if (auto Offset = AI.getDirectOffset()) {
3147+
// There are two cases require special handling:
3148+
// 1)
3149+
// ```
3150+
// struct {
3151+
// struct {} a[8];
3152+
// int b;
3153+
// };
3154+
// ```
3155+
// The lower 8 bytes of the structure are not stored,
3156+
// so an 8-byte offset is needed when accessing the structure.
3157+
// 2)
3158+
// ```
3159+
// struct {
3160+
// long long a;
3161+
// int b;
3162+
// };
3163+
// ```
3164+
// The stored size of this structure is smaller than its actual size,
3165+
// which may lead to reading past the end of the register save area.
3166+
if (AI.isDirect() && RegSize < TySize) {
31363167
Address Tmp = CGF.CreateMemTemp(Ty);
3137-
llvm::Type *TyHi = AI.getCoerceToType();
31383168
llvm::Value *Addr =
31393169
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);
3170+
llvm::Value *Src = CGF.Builder.CreateAlignedLoad(CoTy, Addr, TyAlign);
3171+
llvm::Value *PtrOffset =
3172+
llvm::ConstantInt::get(CGF.Int32Ty, AI.getDirectOffset());
31423173
Address Dst = Address(
31433174
CGF.Builder.CreateGEP(CGF.Int8Ty, Tmp.getBasePointer(), PtrOffset),
31443175
LTy, TyAlign);

clang/test/CodeGenCXX/x86_64-vaarg.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,146 @@ s5 f5(int z, ...) {
261261
__builtin_va_start(list, z);
262262
return __builtin_va_arg(list, s5);
263263
}
264+
265+
typedef struct {
266+
double a;
267+
struct{} b;
268+
} s6;
269+
270+
// CHECK-LABEL: @_Z2f6iz(
271+
// CHECK-NEXT: entry:
272+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S6:%.*]], 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_S6]], 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: [[FP_OFFSET_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 1
281+
// CHECK-NEXT: [[FP_OFFSET:%.*]] = load i32, ptr [[FP_OFFSET_P]], align 4
282+
// CHECK-NEXT: [[FITS_IN_FP:%.*]] = icmp ule i32 [[FP_OFFSET]], 160
283+
// CHECK-NEXT: br i1 [[FITS_IN_FP]], 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 [[FP_OFFSET]]
288+
// CHECK-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8
289+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 0
290+
// CHECK-NEXT: store double [[TMP2]], ptr [[TMP3]], align 8
291+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[FP_OFFSET]], 16
292+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[FP_OFFSET_P]], align 4
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_S6]], ptr [[RETVAL]], i32 0, i32 0
304+
// CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[COERCE_DIVE]], align 8
305+
// CHECK-NEXT: ret double [[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+
}
312+
313+
typedef struct {
314+
long long a;
315+
struct{} b;
316+
} s7;
317+
318+
// CHECK-LABEL: @_Z2f7iz(
319+
// CHECK-NEXT: entry:
320+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S7:%.*]], align 8
321+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
322+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
323+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S7]], align 8
324+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
325+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
326+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
327+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
328+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
329+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
330+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
331+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
332+
// CHECK: vaarg.in_reg:
333+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
334+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
335+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
336+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
337+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 0
338+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 8
339+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
340+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
341+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
342+
// CHECK: vaarg.in_mem:
343+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
344+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
345+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
346+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
347+
// CHECK-NEXT: br label [[VAARG_END]]
348+
// CHECK: vaarg.end:
349+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
350+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
351+
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[STRUCT_S7]], ptr [[RETVAL]], i32 0, i32 0
352+
// CHECK-NEXT: [[TMP5:%.*]] = load i64, ptr [[COERCE_DIVE]], align 8
353+
// CHECK-NEXT: ret i64 [[TMP5]]
354+
//
355+
s7 f7(int z, ...) {
356+
__builtin_va_list list;
357+
__builtin_va_start(list, z);
358+
return __builtin_va_arg(list, s7);
359+
}
360+
361+
typedef struct {
362+
long long a;
363+
int b;
364+
} s8;
365+
366+
// CHECK-LABEL: @_Z2f8iz(
367+
// CHECK-NEXT: entry:
368+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S8:%.*]], align 8
369+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
370+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
371+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S8]], align 8
372+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
373+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
374+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
375+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
376+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
377+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
378+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 32
379+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
380+
// CHECK: vaarg.in_reg:
381+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
382+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
383+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
384+
// CHECK-NEXT: [[TMP2:%.*]] = load { i64, i32 }, ptr [[TMP1]], align 8
385+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 0
386+
// CHECK-NEXT: store { i64, i32 } [[TMP2]], ptr [[TMP3]], align 8
387+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 16
388+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
389+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
390+
// CHECK: vaarg.in_mem:
391+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
392+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
393+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
394+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
395+
// CHECK-NEXT: br label [[VAARG_END]]
396+
// CHECK: vaarg.end:
397+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
398+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
399+
// CHECK-NEXT: [[TMP5:%.*]] = load { i64, i32 }, ptr [[RETVAL]], align 8
400+
// CHECK-NEXT: ret { i64, i32 } [[TMP5]]
401+
//
402+
s8 f8(int z, ...) {
403+
__builtin_va_list list;
404+
__builtin_va_start(list, z);
405+
return __builtin_va_arg(list, s8);
406+
}

0 commit comments

Comments
 (0)