[CMSE] Clear padding bits of struct/unions/fp16 passed by value
When passing a value of a struct/union type from secure to non-secure
state (that is returning from a CMSE entry function or passing an
argument to CMSE-non-secure call), there is a potential sensitive
information leak via the padding bits in the structure. It is not
possible in the general case to ensure those bits are cleared by using
Standard C/C++.
This patch makes the compiler emit code to clear such padding
bits. Since type information is lost in LLVM IR, the code generation
is done by Clang.
For each interesting record type, we build a bitmask, in which all the
bits, corresponding to user declared members, are set. Values of
record types are returned by coercing them to an integer. After the
coercion, the coerced value is masked (with bitwise AND) and then
returned by the function. In a similar manner, values of record types
are passed as arguments by coercing them to an array of integers, and
the coerced values themselves are masked.
For union types, we effectively clear only bits, which aren't part of
any member, since we don't know which is the currently active one.
The compiler will issue a warning, whenever a union is passed to
non-secure state.
Values of half-precision floating-point types are passed in the least
significant bits of a 32-bit register (GPR or FPR) with the most
significant bits unspecified. Since this is also a potential leak of
sensitive information, this patch also clears those unspecified bits.
Differential Revision: https://reviews.llvm.org/D76369
2020-04-28 23:27:52 +08:00
|
|
|
// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
|
|
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-NOPT,CHECK-SOFT
|
|
|
|
// RUN: %clang_cc1 -triple thumbebv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
|
2021-01-26 12:44:41 +08:00
|
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-SOFT
|
[CMSE] Clear padding bits of struct/unions/fp16 passed by value
When passing a value of a struct/union type from secure to non-secure
state (that is returning from a CMSE entry function or passing an
argument to CMSE-non-secure call), there is a potential sensitive
information leak via the padding bits in the structure. It is not
possible in the general case to ensure those bits are cleared by using
Standard C/C++.
This patch makes the compiler emit code to clear such padding
bits. Since type information is lost in LLVM IR, the code generation
is done by Clang.
For each interesting record type, we build a bitmask, in which all the
bits, corresponding to user declared members, are set. Values of
record types are returned by coercing them to an integer. After the
coercion, the coerced value is masked (with bitwise AND) and then
returned by the function. In a similar manner, values of record types
are passed as arguments by coercing them to an array of integers, and
the coerced values themselves are masked.
For union types, we effectively clear only bits, which aren't part of
any member, since we don't know which is the currently active one.
The compiler will issue a warning, whenever a union is passed to
non-secure state.
Values of half-precision floating-point types are passed in the least
significant bits of a 32-bit register (GPR or FPR) with the most
significant bits unspecified. Since this is also a potential leak of
sensitive information, this patch also clears those unspecified bits.
Differential Revision: https://reviews.llvm.org/D76369
2020-04-28 23:27:52 +08:00
|
|
|
// RUN: %clang_cc1 -triple thumbv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
|
|
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-OPT,CHECK-SOFT
|
|
|
|
// RUN: %clang_cc1 -triple thumbebv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
|
|
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-BE-OPT,CHECK-SOFT
|
|
|
|
// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm %s -o - \
|
|
|
|
// RUN: -mfloat-abi hard | \
|
|
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-NOPT,CHECK-HARD
|
|
|
|
|
|
|
|
|
|
|
|
// : Memory layout | Mask
|
|
|
|
// LE: .......1 ........ ........ ........ | 0x00000001/1
|
|
|
|
// BE: 1....... ........ ........ ........ | 0x80000000/-2147483648
|
|
|
|
typedef struct T0 {
|
|
|
|
int a : 1, : 31;
|
|
|
|
} T0;
|
|
|
|
|
|
|
|
T0 t0;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T0 f0() { return t0; }
|
|
|
|
// CHECK: define {{.*}} @f0()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 1
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -2147483648
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: ......1. ........ ........ ........ 0x00000002/2
|
|
|
|
// BE: .1...... ........ ........ ........ 0x40000000/1073741824
|
|
|
|
typedef struct T1 {
|
|
|
|
int : 1, a : 1, : 30;
|
|
|
|
} T1;
|
|
|
|
|
|
|
|
T1 t1;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T1 f1() { return t1; }
|
|
|
|
// CHECK: define {{.*}} @f1()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 2
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 1073741824
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: ........ .......1 ........ ........ 0x00000100/256
|
|
|
|
// BE: ........ 1....... ........ ........ 0x00800000/8388608
|
|
|
|
typedef struct T2 {
|
|
|
|
int : 8, a : 1, : 23;
|
|
|
|
} T2;
|
|
|
|
|
|
|
|
T2 t2;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T2 f2() { return t2; }
|
|
|
|
// CHECK: define {{.*}} @f2()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 256
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 8388608
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: ........ .....1.. ........ ........ 0x00000400/1024
|
|
|
|
// BE: ........ ..1..... ........ ........ 0x00200000/2097152
|
|
|
|
typedef struct T3 {
|
|
|
|
int : 10, a : 1;
|
|
|
|
} T3;
|
|
|
|
|
|
|
|
T3 t3;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T3 f3() { return t3; }
|
|
|
|
// CHECK: define {{.*}} @f3()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 1024
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2097152
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 ........ ........ ........ 0x000000ff/255
|
|
|
|
// BE: 11111111 ........ ........ ........ 0xff000000/-16777216
|
|
|
|
typedef struct T4 {
|
|
|
|
int a : 8, : 24;
|
|
|
|
} T4;
|
|
|
|
|
|
|
|
T4 t4;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T4 f4() { return t4; }
|
|
|
|
// CHECK: define {{.*}} @f4()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 255
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -16777216
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 1111111. .......1 ........ ........ 0x000001fe/510
|
|
|
|
// BE: .1111111 1....... ........ ........ 0x7f800000/2139095040
|
|
|
|
typedef struct T5 {
|
|
|
|
int : 1, a : 8, : 23;
|
|
|
|
} T5;
|
|
|
|
|
|
|
|
T5 t5;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T5 f5() { return t5; }
|
|
|
|
// CHECK: define {{.*}} @f5()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 510
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2139095040
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 1111111. 11111111 ........ ........ 0x0000fffe/65534
|
|
|
|
// BE: .1111111 11111111 ........ ........ 0x7fff0000/2147418112
|
|
|
|
typedef struct T6 {
|
|
|
|
int : 1, a : 15, : 16;
|
|
|
|
} T6;
|
|
|
|
|
|
|
|
T6 t6;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T6 f6() { return t6; }
|
|
|
|
// CHECK: define {{.*}} @f6()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 65534
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2147418112
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 1111111. 11111111 .......1 ........ 0x0001fffe/131070
|
|
|
|
// BE: .1111111 11111111 1....... ........ 0x7fff8000/2147450880
|
|
|
|
typedef struct T7 {
|
|
|
|
int : 1, a : 16, : 15;
|
|
|
|
} T7;
|
|
|
|
|
|
|
|
T7 t7;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T7 f7() { return t7; }
|
|
|
|
// CHECK: define {{.*}} @f7()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 131070
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2147450880
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 111111.. 11111111 11111111 0xfffffcff/-769
|
|
|
|
// BE: 11111111 ..111111 11111111 11111111 0xff3fffff/-12582913
|
|
|
|
typedef struct T8 {
|
|
|
|
struct T80 {
|
|
|
|
char a;
|
|
|
|
char : 2, b : 6;
|
|
|
|
} a;
|
|
|
|
short b;
|
|
|
|
} T8;
|
|
|
|
|
|
|
|
T8 t8;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T8 f8() { return t8; }
|
|
|
|
// CHECK: define {{.*}} @f8()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, -769
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -12582913
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: ......11 ..111111 ...11111 ........ 0x001f3f03/2047747
|
|
|
|
// BE: 11...... 111111.. 11111... ........ 0xc0fcf800/-1057163264
|
|
|
|
typedef struct T9 {
|
|
|
|
struct T90 {
|
|
|
|
char a : 2;
|
|
|
|
char : 0;
|
|
|
|
short b : 6;
|
|
|
|
} a;
|
|
|
|
int b : 5;
|
|
|
|
} T9;
|
|
|
|
|
|
|
|
T9 t9;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T9 f9() { return t9; }
|
|
|
|
// CHECK: define {{.*}} @f9()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 2047747
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -1057163264
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
T9 f91() { return t9; }
|
|
|
|
// CHECK: define {{.*}} @f91()
|
|
|
|
// CHECK: %[[R:.*]] = load i32
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 ........ 11111111 11111111 0xffff00ff/-65281
|
|
|
|
// BE: 11111111 ........ 11111111 11111111 0xff00ffff/16711681
|
|
|
|
typedef struct T10 {
|
|
|
|
char a;
|
|
|
|
short b;
|
|
|
|
} T10;
|
|
|
|
|
|
|
|
T10 t10;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T10 f10() { return t10; }
|
|
|
|
// CHECK: define {{.*}} @f10()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, -65281
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -16711681
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
|
|
|
|
// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
|
|
|
|
typedef struct T11 {
|
|
|
|
short a;
|
|
|
|
char b;
|
|
|
|
} T11;
|
|
|
|
|
|
|
|
T11 t11;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T11 f11() { return t11; }
|
|
|
|
// CHECK: define {{.*}} @f11()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 16777215
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -256
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
|
|
|
|
// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
|
|
|
|
typedef struct T12 {
|
|
|
|
char a[3];
|
|
|
|
} T12;
|
|
|
|
|
|
|
|
T12 t12;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T12 f12() { return t12; }
|
|
|
|
// CHECK: define {{.*}} @f12()
|
|
|
|
// CHECK-LE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T12* @t12
|
|
|
|
// CHECK-LE-OPT: %[[R:.*]] = zext i24 %[[V0]] to i32
|
|
|
|
// CHECK-LE-NOPT: %[[R:.*]] = and i32 %{{.*}}, 16777215
|
|
|
|
|
|
|
|
// CHECK-BE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T12* @t12
|
|
|
|
// CHECK-BE-OPT: %[[V1:.*]] = zext i24 %[[V0]] to i32
|
|
|
|
// CHECK-BE-OPT: %[[R:.*]] = shl nuw i32 %[[V1]], 8
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
|
|
|
|
// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
|
|
|
|
typedef struct __attribute__((packed)) T13 {
|
|
|
|
char a;
|
|
|
|
short b;
|
|
|
|
} T13;
|
|
|
|
|
|
|
|
T13 t13;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T13 f13() { return t13; }
|
|
|
|
// CHECK: define {{.*}} @f13()
|
|
|
|
// CHECK-LE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T13* @t13
|
|
|
|
// CHECK-LE-OPT: %[[R:.*]] = zext i24 %[[V0]] to i32
|
|
|
|
// CHECK-LE-NOPT: %[[R:.*]] = and i32 %{{.*}}, 16777215
|
|
|
|
|
|
|
|
// CHECK-BE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T13* @t13
|
|
|
|
// CHECK-BE-OPT: %[[V1:.*]] = zext i24 %[[V0]] to i32
|
|
|
|
// CHECK-BE-OPT: %[[R:.*]] = shl nuw i32 %[[V1]], 8
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
typedef struct __attribute__((packed)) T14 {
|
|
|
|
short a;
|
|
|
|
short b;
|
|
|
|
} T14;
|
|
|
|
|
|
|
|
T14 t14;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T14 f14() { return t14; }
|
|
|
|
// CHECK: define {{.*}} @f14()
|
2022-01-20 18:22:20 +08:00
|
|
|
// CHECK: [[R:%.*]] = load
|
|
|
|
// CHECK-LE-NOPT-NEXT: [[AND:%.+]] = and i32 [[R]], -1
|
|
|
|
// CHECK-BE-NOPT-NEXT: [[AND:%.+]] = and i32 [[R]], -1
|
|
|
|
// CHECK_NEXT: ret i32 [[AND]]
|
[CMSE] Clear padding bits of struct/unions/fp16 passed by value
When passing a value of a struct/union type from secure to non-secure
state (that is returning from a CMSE entry function or passing an
argument to CMSE-non-secure call), there is a potential sensitive
information leak via the padding bits in the structure. It is not
possible in the general case to ensure those bits are cleared by using
Standard C/C++.
This patch makes the compiler emit code to clear such padding
bits. Since type information is lost in LLVM IR, the code generation
is done by Clang.
For each interesting record type, we build a bitmask, in which all the
bits, corresponding to user declared members, are set. Values of
record types are returned by coercing them to an integer. After the
coercion, the coerced value is masked (with bitwise AND) and then
returned by the function. In a similar manner, values of record types
are passed as arguments by coercing them to an array of integers, and
the coerced values themselves are masked.
For union types, we effectively clear only bits, which aren't part of
any member, since we don't know which is the currently active one.
The compiler will issue a warning, whenever a union is passed to
non-secure state.
Values of half-precision floating-point types are passed in the least
significant bits of a 32-bit register (GPR or FPR) with the most
significant bits unspecified. Since this is also a potential leak of
sensitive information, this patch also clears those unspecified bits.
Differential Revision: https://reviews.llvm.org/D76369
2020-04-28 23:27:52 +08:00
|
|
|
|
|
|
|
// LE: 1111..11 1111..11 11111111 11111111 0xfffff3f3/-3085
|
|
|
|
// BE: 11..1111 11..1111 11111111 11111111 0xcfcfffff/-808452097
|
|
|
|
typedef struct T17 {
|
|
|
|
struct T170 {
|
|
|
|
char a : 2;
|
|
|
|
char : 2, b : 4;
|
|
|
|
} a[2];
|
|
|
|
char b[2];
|
|
|
|
char c[];
|
|
|
|
} T17;
|
|
|
|
|
|
|
|
T17 t17;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T17 f17() { return t17; }
|
|
|
|
// CHECK: define {{.*}} @f17()
|
|
|
|
// CHECK-LE: %[[R:.*]] = and i32 {{.*}}, -3085
|
|
|
|
// CHECK-BE: %[[R:.*]] = and i32 {{.*}}, -808452097
|
|
|
|
// CHECK: ret i32 %[[R]]
|
|
|
|
|
|
|
|
typedef struct T21 {
|
|
|
|
float a;
|
|
|
|
} T21;
|
|
|
|
|
|
|
|
T21 t21;
|
|
|
|
__attribute__((cmse_nonsecure_entry)) T21 f21() { return t21; }
|
|
|
|
// CHECK: define {{.*}} @f21()
|
|
|
|
// CHECK-SOFT: ret i32
|
|
|
|
// CHECK-HARD: ret %struct.T21
|
|
|
|
|
|
|
|
__attribute__((cmse_nonsecure_entry)) float f22() { return 1.0f; }
|
|
|
|
// CHECK: define {{.*}} @f22()
|
|
|
|
// CHECK: ret float
|