@@ -4616,14 +4616,25 @@ static int bpf_core_calc_field_relo(const struct bpf_program *prog,
4616
4616
const struct bpf_core_spec * spec ,
4617
4617
__u32 * val , bool * validate )
4618
4618
{
4619
- const struct bpf_core_accessor * acc = & spec -> spec [ spec -> len - 1 ] ;
4620
- const struct btf_type * t = btf__type_by_id ( spec -> btf , acc -> type_id ) ;
4619
+ const struct bpf_core_accessor * acc ;
4620
+ const struct btf_type * t ;
4621
4621
__u32 byte_off , byte_sz , bit_off , bit_sz ;
4622
4622
const struct btf_member * m ;
4623
4623
const struct btf_type * mt ;
4624
4624
bool bitfield ;
4625
4625
__s64 sz ;
4626
4626
4627
+ if (relo -> kind == BPF_FIELD_EXISTS ) {
4628
+ * val = spec ? 1 : 0 ;
4629
+ return 0 ;
4630
+ }
4631
+
4632
+ if (!spec )
4633
+ return - EUCLEAN ; /* request instruction poisoning */
4634
+
4635
+ acc = & spec -> spec [spec -> len - 1 ];
4636
+ t = btf__type_by_id (spec -> btf , acc -> type_id );
4637
+
4627
4638
/* a[n] accessor needs special handling */
4628
4639
if (!acc -> name ) {
4629
4640
if (relo -> kind == BPF_FIELD_BYTE_OFFSET ) {
@@ -4709,21 +4720,88 @@ static int bpf_core_calc_field_relo(const struct bpf_program *prog,
4709
4720
break ;
4710
4721
case BPF_FIELD_EXISTS :
4711
4722
default :
4712
- pr_warn ("prog '%s': unknown relo %d at insn #%d\n" ,
4713
- bpf_program__title (prog , false),
4714
- relo -> kind , relo -> insn_off / 8 );
4715
- return - EINVAL ;
4723
+ return - EOPNOTSUPP ;
4716
4724
}
4717
4725
4718
4726
return 0 ;
4719
4727
}
4720
4728
4729
+ struct bpf_core_relo_res
4730
+ {
4731
+ /* expected value in the instruction, unless validate == false */
4732
+ __u32 orig_val ;
4733
+ /* new value that needs to be patched up to */
4734
+ __u32 new_val ;
4735
+ /* relocation unsuccessful, poison instruction, but don't fail load */
4736
+ bool poison ;
4737
+ /* some relocations can't be validated against orig_val */
4738
+ bool validate ;
4739
+ };
4740
+
4741
+ /* Calculate original and target relocation values, given local and target
4742
+ * specs and relocation kind. These values are calculated for each candidate.
4743
+ * If there are multiple candidates, resulting values should all be consistent
4744
+ * with each other. Otherwise, libbpf will refuse to proceed due to ambiguity.
4745
+ * If instruction has to be poisoned, *poison will be set to true.
4746
+ */
4747
+ static int bpf_core_calc_relo (const struct bpf_program * prog ,
4748
+ const struct bpf_core_relo * relo ,
4749
+ int relo_idx ,
4750
+ const struct bpf_core_spec * local_spec ,
4751
+ const struct bpf_core_spec * targ_spec ,
4752
+ struct bpf_core_relo_res * res )
4753
+ {
4754
+ int err = - EOPNOTSUPP ;
4755
+
4756
+ res -> orig_val = 0 ;
4757
+ res -> new_val = 0 ;
4758
+ res -> poison = false;
4759
+ res -> validate = true;
4760
+
4761
+ if (core_relo_is_field_based (relo -> kind )) {
4762
+ err = bpf_core_calc_field_relo (prog , relo , local_spec , & res -> orig_val , & res -> validate );
4763
+ err = err ?: bpf_core_calc_field_relo (prog , relo , targ_spec , & res -> new_val , NULL );
4764
+ }
4765
+
4766
+ if (err == - EUCLEAN ) {
4767
+ /* EUCLEAN is used to signal instruction poisoning request */
4768
+ res -> poison = true;
4769
+ err = 0 ;
4770
+ } else if (err == - EOPNOTSUPP ) {
4771
+ /* EOPNOTSUPP means unknown/unsupported relocation */
4772
+ pr_warn ("prog '%s': relo #%d: unrecognized CO-RE relocation %s (%d) at insn #%d\n" ,
4773
+ bpf_program__title (prog , false), relo_idx ,
4774
+ core_relo_kind_str (relo -> kind ), relo -> kind , relo -> insn_off / 8 );
4775
+ }
4776
+
4777
+ return err ;
4778
+ }
4779
+
4780
+ /*
4781
+ * Turn instruction for which CO_RE relocation failed into invalid one with
4782
+ * distinct signature.
4783
+ */
4784
+ static void bpf_core_poison_insn (struct bpf_program * prog , int relo_idx ,
4785
+ int insn_idx , struct bpf_insn * insn )
4786
+ {
4787
+ pr_debug ("prog '%s': relo #%d: substituting insn #%d w/ invalid insn\n" ,
4788
+ bpf_program__title (prog , false), relo_idx , insn_idx );
4789
+ insn -> code = BPF_JMP | BPF_CALL ;
4790
+ insn -> dst_reg = 0 ;
4791
+ insn -> src_reg = 0 ;
4792
+ insn -> off = 0 ;
4793
+ /* if this instruction is reachable (not a dead code),
4794
+ * verifier will complain with the following message:
4795
+ * invalid func unknown#195896080
4796
+ */
4797
+ insn -> imm = 195896080 ; /* => 0xbad2310 => "bad relo" */
4798
+ }
4799
+
4721
4800
/*
4722
4801
* Patch relocatable BPF instruction.
4723
4802
*
4724
4803
* Patched value is determined by relocation kind and target specification.
4725
- * For field existence relocation target spec will be NULL if field is not
4726
- * found.
4804
+ * For existence relocations target spec will be NULL if field/type is not found.
4727
4805
* Expected insn->imm value is determined using relocation kind and local
4728
4806
* spec, and is checked before patching instruction. If actual insn->imm value
4729
4807
* is wrong, bail out with error.
@@ -4732,16 +4810,14 @@ static int bpf_core_calc_field_relo(const struct bpf_program *prog,
4732
4810
* 1. rX = <imm> (assignment with immediate operand);
4733
4811
* 2. rX += <imm> (arithmetic operations with immediate operand);
4734
4812
*/
4735
- static int bpf_core_reloc_insn (struct bpf_program * prog ,
4813
+ static int bpf_core_patch_insn (struct bpf_program * prog ,
4736
4814
const struct bpf_core_relo * relo ,
4737
4815
int relo_idx ,
4738
- const struct bpf_core_spec * local_spec ,
4739
- const struct bpf_core_spec * targ_spec )
4816
+ const struct bpf_core_relo_res * res )
4740
4817
{
4741
4818
__u32 orig_val , new_val ;
4742
4819
struct bpf_insn * insn ;
4743
- bool validate = true;
4744
- int insn_idx , err ;
4820
+ int insn_idx ;
4745
4821
__u8 class ;
4746
4822
4747
4823
if (relo -> insn_off % sizeof (struct bpf_insn ))
@@ -4750,39 +4826,20 @@ static int bpf_core_reloc_insn(struct bpf_program *prog,
4750
4826
insn = & prog -> insns [insn_idx ];
4751
4827
class = BPF_CLASS (insn -> code );
4752
4828
4753
- if (relo -> kind == BPF_FIELD_EXISTS ) {
4754
- orig_val = 1 ; /* can't generate EXISTS relo w/o local field */
4755
- new_val = targ_spec ? 1 : 0 ;
4756
- } else if (!targ_spec ) {
4757
- pr_debug ("prog '%s': relo #%d: substituting insn #%d w/ invalid insn\n" ,
4758
- bpf_program__title (prog , false), relo_idx , insn_idx );
4759
- insn -> code = BPF_JMP | BPF_CALL ;
4760
- insn -> dst_reg = 0 ;
4761
- insn -> src_reg = 0 ;
4762
- insn -> off = 0 ;
4763
- /* if this instruction is reachable (not a dead code),
4764
- * verifier will complain with the following message:
4765
- * invalid func unknown#195896080
4766
- */
4767
- insn -> imm = 195896080 ; /* => 0xbad2310 => "bad relo" */
4829
+ if (res -> poison ) {
4830
+ bpf_core_poison_insn (prog , relo_idx , insn_idx , insn );
4768
4831
return 0 ;
4769
- } else {
4770
- err = bpf_core_calc_field_relo (prog , relo , local_spec ,
4771
- & orig_val , & validate );
4772
- if (err )
4773
- return err ;
4774
- err = bpf_core_calc_field_relo (prog , relo , targ_spec ,
4775
- & new_val , NULL );
4776
- if (err )
4777
- return err ;
4778
4832
}
4779
4833
4834
+ orig_val = res -> orig_val ;
4835
+ new_val = res -> new_val ;
4836
+
4780
4837
switch (class ) {
4781
4838
case BPF_ALU :
4782
4839
case BPF_ALU64 :
4783
4840
if (BPF_SRC (insn -> code ) != BPF_K )
4784
4841
return - EINVAL ;
4785
- if (validate && insn -> imm != orig_val ) {
4842
+ if (res -> validate && insn -> imm != orig_val ) {
4786
4843
pr_warn ("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n" ,
4787
4844
bpf_program__title (prog , false), relo_idx ,
4788
4845
insn_idx , insn -> imm , orig_val , new_val );
@@ -4797,7 +4854,7 @@ static int bpf_core_reloc_insn(struct bpf_program *prog,
4797
4854
case BPF_LDX :
4798
4855
case BPF_ST :
4799
4856
case BPF_STX :
4800
- if (validate && insn -> off != orig_val ) {
4857
+ if (res -> validate && insn -> off != orig_val ) {
4801
4858
pr_warn ("prog '%s': relo #%d: unexpected insn #%d (LD/LDX/ST/STX) value: got %u, exp %u -> %u\n" ,
4802
4859
bpf_program__title (prog , false), relo_idx ,
4803
4860
insn_idx , insn -> off , orig_val , new_val );
@@ -4938,6 +4995,7 @@ static int bpf_core_reloc_field(struct bpf_program *prog,
4938
4995
const char * prog_name = bpf_program__title (prog , false);
4939
4996
struct bpf_core_spec local_spec , cand_spec , targ_spec ;
4940
4997
const void * type_key = u32_as_hash_key (relo -> type_id );
4998
+ struct bpf_core_relo_res cand_res , targ_res ;
4941
4999
const struct btf_type * local_type ;
4942
5000
const char * local_name ;
4943
5001
struct ids_vec * cand_ids ;
@@ -5005,16 +5063,31 @@ static int bpf_core_reloc_field(struct bpf_program *prog,
5005
5063
if (err == 0 )
5006
5064
continue ;
5007
5065
5066
+ err = bpf_core_calc_relo (prog , relo , relo_idx , & local_spec , & cand_spec , & cand_res );
5067
+ if (err )
5068
+ return err ;
5069
+
5008
5070
if (j == 0 ) {
5071
+ targ_res = cand_res ;
5009
5072
targ_spec = cand_spec ;
5010
5073
} else if (cand_spec .bit_offset != targ_spec .bit_offset ) {
5011
- /* if there are many candidates, they should all
5012
- * resolve to the same bit offset
5074
+ /* if there are many field relo candidates, they
5075
+ * should all resolve to the same bit offset
5013
5076
*/
5014
- pr_warn ("prog '%s': relo #%d: offset ambiguity: %u != %u\n" ,
5077
+ pr_warn ("prog '%s': relo #%d: field offset ambiguity: %u != %u\n" ,
5015
5078
prog_name , relo_idx , cand_spec .bit_offset ,
5016
5079
targ_spec .bit_offset );
5017
5080
return - EINVAL ;
5081
+ } else if (cand_res .poison != targ_res .poison || cand_res .new_val != targ_res .new_val ) {
5082
+ /* all candidates should result in the same relocation
5083
+ * decision and value, otherwise it's dangerous to
5084
+ * proceed due to ambiguity
5085
+ */
5086
+ pr_warn ("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n" ,
5087
+ prog_name , relo_idx ,
5088
+ cand_res .poison ? "failure" : "success" , cand_res .new_val ,
5089
+ targ_res .poison ? "failure" : "success" , targ_res .new_val );
5090
+ return - EINVAL ;
5018
5091
}
5019
5092
5020
5093
cand_ids -> data [j ++ ] = cand_spec .spec [0 ].type_id ;
@@ -5042,13 +5115,18 @@ static int bpf_core_reloc_field(struct bpf_program *prog,
5042
5115
* verifier. If it was an error, then verifier will complain and point
5043
5116
* to a specific instruction number in its log.
5044
5117
*/
5045
- if (j == 0 )
5118
+ if (j == 0 ) {
5046
5119
pr_debug ("prog '%s': relo #%d: no matching targets found\n" ,
5047
5120
prog_name , relo_idx );
5048
5121
5049
- /* bpf_core_reloc_insn should know how to handle missing targ_spec */
5050
- err = bpf_core_reloc_insn (prog , relo , relo_idx , & local_spec ,
5051
- j ? & targ_spec : NULL );
5122
+ /* calculate single target relo result explicitly */
5123
+ err = bpf_core_calc_relo (prog , relo , relo_idx , & local_spec , NULL , & targ_res );
5124
+ if (err )
5125
+ return err ;
5126
+ }
5127
+
5128
+ /* bpf_core_patch_insn() should know how to handle missing targ_spec */
5129
+ err = bpf_core_patch_insn (prog , relo , relo_idx , & targ_res );
5052
5130
if (err ) {
5053
5131
pr_warn ("prog '%s': relo #%d: failed to patch insn at offset %d: %d\n" ,
5054
5132
prog_name , relo_idx , relo -> insn_off , err );
0 commit comments