|
| 1 | +//@ add-minicore |
| 2 | +// ignore-tidy-linelength |
| 3 | +//@ only-pauthtest |
| 4 | +// Run it at O0, so that the compiler doesn't optimise the calls away. |
| 5 | +//@ revisions: DISC NO_DISC |
| 6 | + |
| 7 | +//@ [DISC] needs-llvm-components: aarch64 |
| 8 | +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 |
| 9 | +//@ [NO_DISC] needs-llvm-components: aarch64 |
| 10 | +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 |
| 11 | + |
| 12 | +// Test generation of function-pointer type discriminators. The discriminator values were obtained |
| 13 | +// from Clang by compiling equivalent C code (included). Both compilers must generate identical |
| 14 | +// values. |
| 15 | +// |
| 16 | +// Make sure that the compiler can see through chains of nested structs, both when used as globals, |
| 17 | +// arguments and returns. |
| 18 | +// |
| 19 | +// Equivalent C: |
| 20 | +// |
| 21 | +// #include <stdio.h> |
| 22 | +// |
| 23 | +// typedef int (*L0)(int); |
| 24 | +// typedef int (*L1)(L0); |
| 25 | +// typedef int (*L2)(L1); |
| 26 | +// typedef int (*L3)(L2); |
| 27 | +// typedef int (*L4)(L3); |
| 28 | +// typedef L0 (*DeepRet)(void); |
| 29 | +// |
| 30 | +// int callback_i32(int x) { return x + 1; } |
| 31 | +// |
| 32 | +// int dummy_l1(L0 cb) { return cb(5); } |
| 33 | +// int dummy_l2(L1 cb) { return cb(callback_i32); } |
| 34 | +// int dummy_l3(L2 cb) { return cb(dummy_l1); } |
| 35 | +// int dummy_l4(L3 cb) { return cb(dummy_l2); } |
| 36 | +// |
| 37 | +// L0 returned_fn(void) { return callback_i32; } |
| 38 | +// |
| 39 | +// DeepRet f_deep(L4 cb) { |
| 40 | +// cb(dummy_l3); |
| 41 | +// return returned_fn; |
| 42 | +// } |
| 43 | +// |
| 44 | +// DeepRet (*T_DEEP)(L4) = f_deep; |
| 45 | +// |
| 46 | +// int main(void) { |
| 47 | +// DeepRet ret_fn; |
| 48 | +// L0 inner_fn; |
| 49 | +// int result; |
| 50 | +// |
| 51 | +// ret_fn = T_DEEP(dummy_l4); |
| 52 | +// inner_fn = ret_fn(); |
| 53 | +// result = inner_fn(42); |
| 54 | +// |
| 55 | +// printf("result = %d\n", result); |
| 56 | +// |
| 57 | +// return 0; |
| 58 | +// } |
| 59 | + |
| 60 | +#![feature(no_core, lang_items)] |
| 61 | +#![no_std] |
| 62 | +#![no_core] |
| 63 | +#![crate_type = "lib"] |
| 64 | +extern crate minicore; |
| 65 | +use minicore::hint::black_box; |
| 66 | + |
| 67 | +// Nested fn ptr chain. |
| 68 | +type L0 = extern "C" fn(i32) -> i32; |
| 69 | +type L1 = extern "C" fn(L0) -> i32; |
| 70 | +type L2 = extern "C" fn(L1) -> i32; |
| 71 | +type L3 = extern "C" fn(L2) -> i32; |
| 72 | +type L4 = extern "C" fn(L3) -> i32; |
| 73 | +// Function returning fn ptr. |
| 74 | +type DeepRet = extern "C" fn() -> L0; |
| 75 | + |
| 76 | +#[used] |
| 77 | +// DISC: @{{.*}}T_DEEP = constant ptr ptrauth (ptr @{{.*}}f_deep, i32 0, i64 1059), align 8 |
| 78 | +// NO_DISC: @{{.*}}T_DEEP = constant ptr ptrauth (ptr @{{.*}}f_deep, i32 0), align 8 |
| 79 | +static T_DEEP: unsafe extern "C" fn(L4) -> DeepRet = f_deep; |
| 80 | + |
| 81 | +// Leaf callback. |
| 82 | +// CHECK-LABEL: callback_i32 |
| 83 | +pub extern "C" fn callback_i32(x: i32) -> i32 { |
| 84 | + x |
| 85 | +} |
| 86 | + |
| 87 | +// Dummy chain impl. |
| 88 | +// CHECK-LABEL: dummy_l1 |
| 89 | +// CHECK: (ptr [[CB:%.*]]) |
| 90 | +pub extern "C" fn dummy_l1(cb: L0) -> i32 { |
| 91 | + // DISC: call i32 [[CB]](i32 5) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] |
| 92 | + // NO_DISC: call i32 [[CB]](i32 5) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 93 | + cb(5) |
| 94 | +} |
| 95 | +// CHECK-LABEL: dummy_l2 |
| 96 | +// CHECK: (ptr [[CB:%.*]]) |
| 97 | +pub extern "C" fn dummy_l2(cb: L1) -> i32 { |
| 98 | + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] |
| 99 | + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}callback_i32, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 100 | + cb(callback_i32) |
| 101 | +} |
| 102 | +// CHECK-LABEL: dummy_l3 |
| 103 | +// CHECK: (ptr [[CB:%.*]]) |
| 104 | +pub extern "C" fn dummy_l3(cb: L2) -> i32 { |
| 105 | + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l1, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] |
| 106 | + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l1, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 107 | + cb(dummy_l1) |
| 108 | +} |
| 109 | +// CHECK-LABEL: dummy_l4 |
| 110 | +// CHECK: (ptr [[CB:%.*]]) |
| 111 | +pub extern "C" fn dummy_l4(cb: L3) -> i32 { |
| 112 | + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l2, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] |
| 113 | + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l2, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 114 | + cb(dummy_l2) |
| 115 | +} |
| 116 | +// Return fn impl. |
| 117 | +// CHECK-LABEL: returned_fn |
| 118 | +pub extern "C" fn returned_fn() -> L0 { |
| 119 | + // DISC: ret ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981) |
| 120 | + // NO_DISC: ret ptr ptrauth (ptr @{{.*}}callback_i32, i32 0) |
| 121 | + return callback_i32; |
| 122 | +} |
| 123 | +// Entry point to the chain, takes L4 and returns function returning fn ptr. |
| 124 | +// CHECK-LABEL: f_deep |
| 125 | +// CHECK: (ptr [[CB:%.*]]) |
| 126 | +pub extern "C" fn f_deep(cb: L4) -> DeepRet { |
| 127 | + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l3, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] |
| 128 | + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l3, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 129 | + cb(dummy_l3); |
| 130 | + // DISC: ret ptr ptrauth (ptr @{{.*}}returned_fn, i32 0, i64 34128) |
| 131 | + // NO_DISC: ret ptr ptrauth (ptr @{{.*}}returned_fn, i32 0) |
| 132 | + return returned_fn; |
| 133 | +} |
| 134 | + |
| 135 | +// CHECK-LABEL: main |
| 136 | +pub fn main() { |
| 137 | + unsafe { |
| 138 | + // DISC: [[RET_FN:%.*]] = call ptr ptrauth (ptr @{{.*}}f_deep, i32 0, i64 1059)(ptr ptrauth (ptr @{{.*}}dummy_l4, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 1059) ] |
| 139 | + // NO_DISC: [[RET_FN:%.*]] = call ptr ptrauth (ptr @{{.*}}f_deep, i32 0)(ptr ptrauth (ptr @{{.*}}dummy_l4, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 140 | + let ret_fn: DeepRet = T_DEEP(dummy_l4); |
| 141 | + // DISC: [[INNER_FN:%.*]] = call ptr [[RET_FN]]() {{.*}} [ "ptrauth"(i32 0, i64 34128) ] |
| 142 | + // NO_DISC: [[INNER_FN:%.*]] = call ptr [[RET_FN]]() {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 143 | + let inner_fn: L0 = ret_fn(); |
| 144 | + // DISC: call i32 [[INNER_FN]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] |
| 145 | + // NO_DISC: call i32 [[INNER_FN]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 0) ] |
| 146 | + let result = inner_fn(42); |
| 147 | + |
| 148 | + black_box(result); |
| 149 | + } |
| 150 | +} |
0 commit comments