@@ -418,8 +418,16 @@ static int decode_instructions(struct objtool_file *file)
418
418
return -1 ;
419
419
}
420
420
421
- sym_for_each_insn (file , func , insn )
421
+ sym_for_each_insn (file , func , insn ) {
422
422
insn -> func = func ;
423
+ if (insn -> type == INSN_ENDBR ) {
424
+ if (insn -> offset == insn -> func -> offset ) {
425
+ file -> nr_endbr ++ ;
426
+ } else {
427
+ file -> nr_endbr_int ++ ;
428
+ }
429
+ }
430
+ }
423
431
}
424
432
}
425
433
@@ -1171,6 +1179,19 @@ static void add_retpoline_call(struct objtool_file *file, struct instruction *in
1171
1179
1172
1180
annotate_call_site (file , insn , false);
1173
1181
}
1182
+
1183
+ static bool same_function (struct instruction * insn1 , struct instruction * insn2 )
1184
+ {
1185
+ return insn1 -> func -> pfunc == insn2 -> func -> pfunc ;
1186
+ }
1187
+
1188
+ static bool is_first_func_insn (struct instruction * insn )
1189
+ {
1190
+ return insn -> offset == insn -> func -> offset ||
1191
+ (insn -> type == INSN_ENDBR &&
1192
+ insn -> offset == insn -> func -> offset + insn -> len );
1193
+ }
1194
+
1174
1195
/*
1175
1196
* Find the destination instructions for all jumps.
1176
1197
*/
@@ -1251,8 +1272,8 @@ static int add_jump_destinations(struct objtool_file *file)
1251
1272
insn -> func -> cfunc = insn -> jump_dest -> func ;
1252
1273
insn -> jump_dest -> func -> pfunc = insn -> func ;
1253
1274
1254
- } else if (insn -> jump_dest -> func -> pfunc != insn -> func -> pfunc &&
1255
- insn -> jump_dest -> offset == insn -> jump_dest -> func -> offset ) {
1275
+ } else if (! same_function ( insn , insn -> jump_dest ) &&
1276
+ is_first_func_insn ( insn -> jump_dest ) ) {
1256
1277
/* internal sibling call (without reloc) */
1257
1278
add_call_dest (file , insn , insn -> jump_dest -> func , true);
1258
1279
}
@@ -1842,6 +1863,16 @@ static int read_unwind_hints(struct objtool_file *file)
1842
1863
1843
1864
insn -> hint = true;
1844
1865
1866
+ if (ibt && hint -> type == UNWIND_HINT_TYPE_REGS_PARTIAL ) {
1867
+ struct symbol * sym = find_symbol_by_offset (insn -> sec , insn -> offset );
1868
+
1869
+ if (sym && sym -> bind == STB_GLOBAL &&
1870
+ insn -> type != INSN_ENDBR && !insn -> noendbr ) {
1871
+ WARN_FUNC ("UNWIND_HINT_IRET_REGS without ENDBR" ,
1872
+ insn -> sec , insn -> offset );
1873
+ }
1874
+ }
1875
+
1845
1876
if (hint -> type == UNWIND_HINT_TYPE_FUNC ) {
1846
1877
insn -> cfi = & func_cfi ;
1847
1878
continue ;
@@ -1883,6 +1914,9 @@ static int read_noendbr_hints(struct objtool_file *file)
1883
1914
return -1 ;
1884
1915
}
1885
1916
1917
+ if (insn -> type == INSN_ENDBR )
1918
+ WARN_FUNC ("ANNOTATE_NOENDBR on ENDBR" , insn -> sec , insn -> offset );
1919
+
1886
1920
insn -> noendbr = 1 ;
1887
1921
}
1888
1922
@@ -2122,6 +2156,9 @@ static int decode_sections(struct objtool_file *file)
2122
2156
if (ret )
2123
2157
return ret ;
2124
2158
2159
+ /*
2160
+ * Must be before read_unwind_hints() since that needs insn->noendbr.
2161
+ */
2125
2162
ret = read_noendbr_hints (file );
2126
2163
if (ret )
2127
2164
return ret ;
@@ -3063,6 +3100,111 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
3063
3100
return next_insn_same_sec (file , insn );
3064
3101
}
3065
3102
3103
+ static struct instruction *
3104
+ validate_ibt_reloc (struct objtool_file * file , struct reloc * reloc )
3105
+ {
3106
+ struct instruction * dest ;
3107
+ struct section * sec ;
3108
+ unsigned long off ;
3109
+
3110
+ sec = reloc -> sym -> sec ;
3111
+ off = reloc -> sym -> offset ;
3112
+
3113
+ if ((reloc -> sec -> base -> sh .sh_flags & SHF_EXECINSTR ) &&
3114
+ (reloc -> type == R_X86_64_PC32 || reloc -> type == R_X86_64_PLT32 ))
3115
+ off += arch_dest_reloc_offset (reloc -> addend );
3116
+ else
3117
+ off += reloc -> addend ;
3118
+
3119
+ dest = find_insn (file , sec , off );
3120
+ if (!dest )
3121
+ return NULL ;
3122
+
3123
+ if (dest -> type == INSN_ENDBR )
3124
+ return NULL ;
3125
+
3126
+ if (reloc -> sym -> static_call_tramp )
3127
+ return NULL ;
3128
+
3129
+ return dest ;
3130
+ }
3131
+
3132
+ static void warn_noendbr (const char * msg , struct section * sec , unsigned long offset ,
3133
+ struct instruction * dest )
3134
+ {
3135
+ WARN_FUNC ("%srelocation to !ENDBR: %s+0x%lx" , sec , offset , msg ,
3136
+ dest -> func ? dest -> func -> name : dest -> sec -> name ,
3137
+ dest -> func ? dest -> offset - dest -> func -> offset : dest -> offset );
3138
+ }
3139
+
3140
+ static void validate_ibt_dest (struct objtool_file * file , struct instruction * insn ,
3141
+ struct instruction * dest )
3142
+ {
3143
+ if (dest -> func && dest -> func == insn -> func ) {
3144
+ /*
3145
+ * Anything from->to self is either _THIS_IP_ or IRET-to-self.
3146
+ *
3147
+ * There is no sane way to annotate _THIS_IP_ since the compiler treats the
3148
+ * relocation as a constant and is happy to fold in offsets, skewing any
3149
+ * annotation we do, leading to vast amounts of false-positives.
3150
+ *
3151
+ * There's also compiler generated _THIS_IP_ through KCOV and
3152
+ * such which we have no hope of annotating.
3153
+ *
3154
+ * As such, blanket accept self-references without issue.
3155
+ */
3156
+ return ;
3157
+ }
3158
+
3159
+ if (dest -> noendbr )
3160
+ return ;
3161
+
3162
+ warn_noendbr ("" , insn -> sec , insn -> offset , dest );
3163
+ }
3164
+
3165
+ static void validate_ibt_insn (struct objtool_file * file , struct instruction * insn )
3166
+ {
3167
+ struct instruction * dest ;
3168
+ struct reloc * reloc ;
3169
+
3170
+ switch (insn -> type ) {
3171
+ case INSN_CALL :
3172
+ case INSN_CALL_DYNAMIC :
3173
+ case INSN_JUMP_CONDITIONAL :
3174
+ case INSN_JUMP_UNCONDITIONAL :
3175
+ case INSN_JUMP_DYNAMIC :
3176
+ case INSN_JUMP_DYNAMIC_CONDITIONAL :
3177
+ case INSN_RETURN :
3178
+ /*
3179
+ * We're looking for code references setting up indirect code
3180
+ * flow. As such, ignore direct code flow and the actual
3181
+ * dynamic branches.
3182
+ */
3183
+ return ;
3184
+
3185
+ case INSN_NOP :
3186
+ /*
3187
+ * handle_group_alt() will create INSN_NOP instruction that
3188
+ * don't belong to any section, ignore all NOP since they won't
3189
+ * carry a (useful) relocation anyway.
3190
+ */
3191
+ return ;
3192
+
3193
+ default :
3194
+ break ;
3195
+ }
3196
+
3197
+ for (reloc = insn_reloc (file , insn );
3198
+ reloc ;
3199
+ reloc = find_reloc_by_dest_range (file -> elf , insn -> sec ,
3200
+ reloc -> offset + 1 ,
3201
+ (insn -> offset + insn -> len ) - (reloc -> offset + 1 ))) {
3202
+ dest = validate_ibt_reloc (file , reloc );
3203
+ if (dest )
3204
+ validate_ibt_dest (file , insn , dest );
3205
+ }
3206
+ }
3207
+
3066
3208
/*
3067
3209
* Follow the branch starting at the given instruction, and recursively follow
3068
3210
* any other branches (jumps). Meanwhile, track the frame pointer state at
@@ -3272,6 +3414,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
3272
3414
break ;
3273
3415
}
3274
3416
3417
+ if (ibt )
3418
+ validate_ibt_insn (file , insn );
3419
+
3275
3420
if (insn -> dead_end )
3276
3421
return 0 ;
3277
3422
@@ -3557,6 +3702,53 @@ static int validate_functions(struct objtool_file *file)
3557
3702
return warnings ;
3558
3703
}
3559
3704
3705
+ static int validate_ibt (struct objtool_file * file )
3706
+ {
3707
+ struct section * sec ;
3708
+ struct reloc * reloc ;
3709
+
3710
+ for_each_sec (file , sec ) {
3711
+ bool is_data ;
3712
+
3713
+ /* already done in validate_branch() */
3714
+ if (sec -> sh .sh_flags & SHF_EXECINSTR )
3715
+ continue ;
3716
+
3717
+ if (!sec -> reloc )
3718
+ continue ;
3719
+
3720
+ if (!strncmp (sec -> name , ".orc" , 4 ))
3721
+ continue ;
3722
+
3723
+ if (!strncmp (sec -> name , ".discard" , 8 ))
3724
+ continue ;
3725
+
3726
+ if (!strncmp (sec -> name , ".debug" , 6 ))
3727
+ continue ;
3728
+
3729
+ if (!strcmp (sec -> name , "_error_injection_whitelist" ))
3730
+ continue ;
3731
+
3732
+ if (!strcmp (sec -> name , "_kprobe_blacklist" ))
3733
+ continue ;
3734
+
3735
+ is_data = strstr (sec -> name , ".data" ) || strstr (sec -> name , ".rodata" );
3736
+
3737
+ list_for_each_entry (reloc , & sec -> reloc -> reloc_list , list ) {
3738
+ struct instruction * dest ;
3739
+
3740
+ dest = validate_ibt_reloc (file , reloc );
3741
+ if (is_data && dest && !dest -> noendbr ) {
3742
+ warn_noendbr ("data " , reloc -> sym -> sec ,
3743
+ reloc -> sym -> offset + reloc -> addend ,
3744
+ dest );
3745
+ }
3746
+ }
3747
+ }
3748
+
3749
+ return 0 ;
3750
+ }
3751
+
3560
3752
static int validate_reachable_instructions (struct objtool_file * file )
3561
3753
{
3562
3754
struct instruction * insn ;
@@ -3584,6 +3776,11 @@ int check(struct objtool_file *file)
3584
3776
return 1 ;
3585
3777
}
3586
3778
3779
+ if (ibt && !lto ) {
3780
+ fprintf (stderr , "--ibt requires: --lto\n" );
3781
+ return 1 ;
3782
+ }
3783
+
3587
3784
arch_initial_func_cfi_state (& initial_func_cfi );
3588
3785
init_cfi_state (& init_cfi );
3589
3786
init_cfi_state (& func_cfi );
@@ -3630,6 +3827,13 @@ int check(struct objtool_file *file)
3630
3827
goto out ;
3631
3828
warnings += ret ;
3632
3829
3830
+ if (ibt ) {
3831
+ ret = validate_ibt (file );
3832
+ if (ret < 0 )
3833
+ goto out ;
3834
+ warnings += ret ;
3835
+ }
3836
+
3633
3837
if (!warnings ) {
3634
3838
ret = validate_reachable_instructions (file );
3635
3839
if (ret < 0 )
0 commit comments