@@ -3182,114 +3182,6 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
31823182 return next_insn_same_sec (file , insn );
31833183}
31843184
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-
32933185/*
32943186 * Follow the branch starting at the given instruction, and recursively follow
32953187 * 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,
34993391 break ;
35003392 }
35013393
3502- if (opts .ibt )
3503- validate_ibt_insn (file , insn );
3504-
35053394 if (insn -> dead_end )
35063395 return 0 ;
35073396
@@ -3787,48 +3676,173 @@ static int validate_functions(struct objtool_file *file)
37873676 return warnings ;
37883677}
37893678
3790- static int validate_ibt (struct objtool_file * file )
3679+ static void mark_endbr_used (struct instruction * insn )
37913680{
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 ;
37933688 struct reloc * reloc ;
3689+ unsigned long off ;
3690+ int warnings = 0 ;
37943691
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+ }
37973709
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 ))) {
38013715
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 )
38033721 continue ;
38043722
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 )
38063731 continue ;
38073732
3808- if (!strncmp (sec -> name , ".discard" , 8 ))
3733+ if (dest -> type == INSN_ENDBR ) {
3734+ mark_endbr_used (dest );
38093735 continue ;
3736+ }
38103737
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+ */
38123754 continue ;
3755+ }
38133756
3814- if (! strcmp ( sec -> name , "_error_injection_whitelist" ) )
3757+ if (dest -> noendbr )
38153758 continue ;
38163759
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 )
38183814 continue ;
38193815
3820- is_data = strstr (sec -> name , ".data" ) || strstr (sec -> name , ".rodata" );
3816+ if (!sec -> reloc )
3817+ continue ;
38213818
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 ;
38243840
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 );
38293843 }
38303844
3831- return 0 ;
3845+ return warnings ;
38323846}
38333847
38343848static int validate_reachable_instructions (struct objtool_file * file )
@@ -3899,7 +3913,7 @@ int check(struct objtool_file *file)
38993913 warnings += ret ;
39003914 }
39013915
3902- if (opts .stackval || opts .orc || opts .uaccess || opts .ibt || opts . sls ) {
3916+ if (opts .stackval || opts .orc || opts .uaccess || opts .sls ) {
39033917 ret = validate_functions (file );
39043918 if (ret < 0 )
39053919 goto out ;
0 commit comments