@@ -3182,114 +3182,6 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
3182
3182
return next_insn_same_sec (file , insn );
3183
3183
}
3184
3184
3185
- static struct instruction *
3186
- validate_ibt_reloc (struct objtool_file * file , struct reloc * reloc )
3187
- {
3188
- struct instruction * dest ;
3189
- struct section * sec ;
3190
- unsigned long off ;
3191
-
3192
- sec = reloc -> sym -> sec ;
3193
- off = reloc -> sym -> offset ;
3194
-
3195
- if ((reloc -> sec -> base -> sh .sh_flags & SHF_EXECINSTR ) &&
3196
- (reloc -> type == R_X86_64_PC32 || reloc -> type == R_X86_64_PLT32 ))
3197
- off += arch_dest_reloc_offset (reloc -> addend );
3198
- else
3199
- off += reloc -> addend ;
3200
-
3201
- dest = find_insn (file , sec , off );
3202
- if (!dest )
3203
- return NULL ;
3204
-
3205
- if (dest -> type == INSN_ENDBR ) {
3206
- if (!list_empty (& dest -> call_node ))
3207
- list_del_init (& dest -> call_node );
3208
-
3209
- return NULL ;
3210
- }
3211
-
3212
- if (reloc -> sym -> static_call_tramp )
3213
- return NULL ;
3214
-
3215
- return dest ;
3216
- }
3217
-
3218
- static void warn_noendbr (const char * msg , struct section * sec , unsigned long offset ,
3219
- struct instruction * dest )
3220
- {
3221
- WARN_FUNC ("%srelocation to !ENDBR: %s" , sec , offset , msg ,
3222
- offstr (dest -> sec , dest -> offset ));
3223
- }
3224
-
3225
- static void validate_ibt_dest (struct objtool_file * file , struct instruction * insn ,
3226
- struct instruction * dest )
3227
- {
3228
- if (dest -> func && dest -> func == insn -> func ) {
3229
- /*
3230
- * Anything from->to self is either _THIS_IP_ or IRET-to-self.
3231
- *
3232
- * There is no sane way to annotate _THIS_IP_ since the compiler treats the
3233
- * relocation as a constant and is happy to fold in offsets, skewing any
3234
- * annotation we do, leading to vast amounts of false-positives.
3235
- *
3236
- * There's also compiler generated _THIS_IP_ through KCOV and
3237
- * such which we have no hope of annotating.
3238
- *
3239
- * As such, blanket accept self-references without issue.
3240
- */
3241
- return ;
3242
- }
3243
-
3244
- if (dest -> noendbr )
3245
- return ;
3246
-
3247
- warn_noendbr ("" , insn -> sec , insn -> offset , dest );
3248
- }
3249
-
3250
- static void validate_ibt_insn (struct objtool_file * file , struct instruction * insn )
3251
- {
3252
- struct instruction * dest ;
3253
- struct reloc * reloc ;
3254
-
3255
- switch (insn -> type ) {
3256
- case INSN_CALL :
3257
- case INSN_CALL_DYNAMIC :
3258
- case INSN_JUMP_CONDITIONAL :
3259
- case INSN_JUMP_UNCONDITIONAL :
3260
- case INSN_JUMP_DYNAMIC :
3261
- case INSN_JUMP_DYNAMIC_CONDITIONAL :
3262
- case INSN_RETURN :
3263
- /*
3264
- * We're looking for code references setting up indirect code
3265
- * flow. As such, ignore direct code flow and the actual
3266
- * dynamic branches.
3267
- */
3268
- return ;
3269
-
3270
- case INSN_NOP :
3271
- /*
3272
- * handle_group_alt() will create INSN_NOP instruction that
3273
- * don't belong to any section, ignore all NOP since they won't
3274
- * carry a (useful) relocation anyway.
3275
- */
3276
- return ;
3277
-
3278
- default :
3279
- break ;
3280
- }
3281
-
3282
- for (reloc = insn_reloc (file , insn );
3283
- reloc ;
3284
- reloc = find_reloc_by_dest_range (file -> elf , insn -> sec ,
3285
- reloc -> offset + 1 ,
3286
- (insn -> offset + insn -> len ) - (reloc -> offset + 1 ))) {
3287
- dest = validate_ibt_reloc (file , reloc );
3288
- if (dest )
3289
- validate_ibt_dest (file , insn , dest );
3290
- }
3291
- }
3292
-
3293
3185
/*
3294
3186
* Follow the branch starting at the given instruction, and recursively follow
3295
3187
* any other branches (jumps). Meanwhile, track the frame pointer state at
@@ -3499,9 +3391,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
3499
3391
break ;
3500
3392
}
3501
3393
3502
- if (opts .ibt )
3503
- validate_ibt_insn (file , insn );
3504
-
3505
3394
if (insn -> dead_end )
3506
3395
return 0 ;
3507
3396
@@ -3787,48 +3676,173 @@ static int validate_functions(struct objtool_file *file)
3787
3676
return warnings ;
3788
3677
}
3789
3678
3790
- static int validate_ibt (struct objtool_file * file )
3679
+ static void mark_endbr_used (struct instruction * insn )
3791
3680
{
3792
- struct section * sec ;
3681
+ if (!list_empty (& insn -> call_node ))
3682
+ list_del_init (& insn -> call_node );
3683
+ }
3684
+
3685
+ static int validate_ibt_insn (struct objtool_file * file , struct instruction * insn )
3686
+ {
3687
+ struct instruction * dest ;
3793
3688
struct reloc * reloc ;
3689
+ unsigned long off ;
3690
+ int warnings = 0 ;
3794
3691
3795
- for_each_sec (file , sec ) {
3796
- bool is_data ;
3692
+ /*
3693
+ * Looking for function pointer load relocations. Ignore
3694
+ * direct/indirect branches:
3695
+ */
3696
+ switch (insn -> type ) {
3697
+ case INSN_CALL :
3698
+ case INSN_CALL_DYNAMIC :
3699
+ case INSN_JUMP_CONDITIONAL :
3700
+ case INSN_JUMP_UNCONDITIONAL :
3701
+ case INSN_JUMP_DYNAMIC :
3702
+ case INSN_JUMP_DYNAMIC_CONDITIONAL :
3703
+ case INSN_RETURN :
3704
+ case INSN_NOP :
3705
+ return 0 ;
3706
+ default :
3707
+ break ;
3708
+ }
3797
3709
3798
- /* already done in validate_branch() */
3799
- if (sec -> sh .sh_flags & SHF_EXECINSTR )
3800
- continue ;
3710
+ for (reloc = insn_reloc (file , insn );
3711
+ reloc ;
3712
+ reloc = find_reloc_by_dest_range (file -> elf , insn -> sec ,
3713
+ reloc -> offset + 1 ,
3714
+ (insn -> offset + insn -> len ) - (reloc -> offset + 1 ))) {
3801
3715
3802
- if (!sec -> reloc )
3716
+ /*
3717
+ * static_call_update() references the trampoline, which
3718
+ * doesn't have (or need) ENDBR. Skip warning in that case.
3719
+ */
3720
+ if (reloc -> sym -> static_call_tramp )
3803
3721
continue ;
3804
3722
3805
- if (!strncmp (sec -> name , ".orc" , 4 ))
3723
+ off = reloc -> sym -> offset ;
3724
+ if (reloc -> type == R_X86_64_PC32 || reloc -> type == R_X86_64_PLT32 )
3725
+ off += arch_dest_reloc_offset (reloc -> addend );
3726
+ else
3727
+ off += reloc -> addend ;
3728
+
3729
+ dest = find_insn (file , reloc -> sym -> sec , off );
3730
+ if (!dest )
3806
3731
continue ;
3807
3732
3808
- if (!strncmp (sec -> name , ".discard" , 8 ))
3733
+ if (dest -> type == INSN_ENDBR ) {
3734
+ mark_endbr_used (dest );
3809
3735
continue ;
3736
+ }
3810
3737
3811
- if (!strncmp (sec -> name , ".debug" , 6 ))
3738
+ if (dest -> func && dest -> func == insn -> func ) {
3739
+ /*
3740
+ * Anything from->to self is either _THIS_IP_ or
3741
+ * IRET-to-self.
3742
+ *
3743
+ * There is no sane way to annotate _THIS_IP_ since the
3744
+ * compiler treats the relocation as a constant and is
3745
+ * happy to fold in offsets, skewing any annotation we
3746
+ * do, leading to vast amounts of false-positives.
3747
+ *
3748
+ * There's also compiler generated _THIS_IP_ through
3749
+ * KCOV and such which we have no hope of annotating.
3750
+ *
3751
+ * As such, blanket accept self-references without
3752
+ * issue.
3753
+ */
3812
3754
continue ;
3755
+ }
3813
3756
3814
- if (! strcmp ( sec -> name , "_error_injection_whitelist" ) )
3757
+ if (dest -> noendbr )
3815
3758
continue ;
3816
3759
3817
- if (!strcmp (sec -> name , "_kprobe_blacklist" ))
3760
+ WARN_FUNC ("relocation to !ENDBR: %s" ,
3761
+ insn -> sec , insn -> offset ,
3762
+ offstr (dest -> sec , dest -> offset ));
3763
+
3764
+ warnings ++ ;
3765
+ }
3766
+
3767
+ return warnings ;
3768
+ }
3769
+
3770
+ static int validate_ibt_data_reloc (struct objtool_file * file ,
3771
+ struct reloc * reloc )
3772
+ {
3773
+ struct instruction * dest ;
3774
+
3775
+ dest = find_insn (file , reloc -> sym -> sec ,
3776
+ reloc -> sym -> offset + reloc -> addend );
3777
+ if (!dest )
3778
+ return 0 ;
3779
+
3780
+ if (dest -> type == INSN_ENDBR ) {
3781
+ mark_endbr_used (dest );
3782
+ return 0 ;
3783
+ }
3784
+
3785
+ if (dest -> noendbr )
3786
+ return 0 ;
3787
+
3788
+ WARN_FUNC ("data relocation to !ENDBR: %s" ,
3789
+ reloc -> sec -> base , reloc -> offset ,
3790
+ offstr (dest -> sec , dest -> offset ));
3791
+
3792
+ return 1 ;
3793
+ }
3794
+
3795
+ /*
3796
+ * Validate IBT rules and remove used ENDBR instructions from the seal list.
3797
+ * Unused ENDBR instructions will be annotated for sealing (i.e., replaced with
3798
+ * NOPs) later, in create_ibt_endbr_seal_sections().
3799
+ */
3800
+ static int validate_ibt (struct objtool_file * file )
3801
+ {
3802
+ struct section * sec ;
3803
+ struct reloc * reloc ;
3804
+ struct instruction * insn ;
3805
+ int warnings = 0 ;
3806
+
3807
+ for_each_insn (file , insn )
3808
+ warnings += validate_ibt_insn (file , insn );
3809
+
3810
+ for_each_sec (file , sec ) {
3811
+
3812
+ /* Already done by validate_ibt_insn() */
3813
+ if (sec -> sh .sh_flags & SHF_EXECINSTR )
3818
3814
continue ;
3819
3815
3820
- is_data = strstr (sec -> name , ".data" ) || strstr (sec -> name , ".rodata" );
3816
+ if (!sec -> reloc )
3817
+ continue ;
3821
3818
3822
- list_for_each_entry (reloc , & sec -> reloc -> reloc_list , list ) {
3823
- struct instruction * dest ;
3819
+ /*
3820
+ * These sections can reference text addresses, but not with
3821
+ * the intent to indirect branch to them.
3822
+ */
3823
+ if (!strncmp (sec -> name , ".discard" , 8 ) ||
3824
+ !strncmp (sec -> name , ".debug" , 6 ) ||
3825
+ !strcmp (sec -> name , ".altinstructions" ) ||
3826
+ !strcmp (sec -> name , ".ibt_endbr_seal" ) ||
3827
+ !strcmp (sec -> name , ".orc_unwind_ip" ) ||
3828
+ !strcmp (sec -> name , ".parainstructions" ) ||
3829
+ !strcmp (sec -> name , ".retpoline_sites" ) ||
3830
+ !strcmp (sec -> name , ".smp_locks" ) ||
3831
+ !strcmp (sec -> name , ".static_call_sites" ) ||
3832
+ !strcmp (sec -> name , "_error_injection_whitelist" ) ||
3833
+ !strcmp (sec -> name , "_kprobe_blacklist" ) ||
3834
+ !strcmp (sec -> name , "__bug_table" ) ||
3835
+ !strcmp (sec -> name , "__ex_table" ) ||
3836
+ !strcmp (sec -> name , "__jump_table" ) ||
3837
+ !strcmp (sec -> name , "__mcount_loc" ) ||
3838
+ !strcmp (sec -> name , "__tracepoints" ))
3839
+ continue ;
3824
3840
3825
- dest = validate_ibt_reloc (file , reloc );
3826
- if (is_data && dest && !dest -> noendbr )
3827
- warn_noendbr ("data " , sec , reloc -> offset , dest );
3828
- }
3841
+ list_for_each_entry (reloc , & sec -> reloc -> reloc_list , list )
3842
+ warnings += validate_ibt_data_reloc (file , reloc );
3829
3843
}
3830
3844
3831
- return 0 ;
3845
+ return warnings ;
3832
3846
}
3833
3847
3834
3848
static int validate_reachable_instructions (struct objtool_file * file )
@@ -3899,7 +3913,7 @@ int check(struct objtool_file *file)
3899
3913
warnings += ret ;
3900
3914
}
3901
3915
3902
- if (opts .stackval || opts .orc || opts .uaccess || opts .ibt || opts . sls ) {
3916
+ if (opts .stackval || opts .orc || opts .uaccess || opts .sls ) {
3903
3917
ret = validate_functions (file );
3904
3918
if (ret < 0 )
3905
3919
goto out ;
0 commit comments