@@ -7488,6 +7488,7 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
74887488{
74897489 struct bpf_insn_aux_data * aux = cur_aux (env );
74907490 struct bpf_reg_state * regs = cur_regs (env );
7491+ struct bpf_reg_state * dst_reg ;
74917492 struct bpf_map * map ;
74927493 int err ;
74937494
@@ -7504,25 +7505,44 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
75047505 if (err )
75057506 return err ;
75067507
7508+ dst_reg = & regs [insn -> dst_reg ];
75077509 if (insn -> src_reg == 0 ) {
75087510 u64 imm = ((u64 )(insn + 1 )-> imm << 32 ) | (u32 )insn -> imm ;
75097511
7510- regs [ insn -> dst_reg ]. type = SCALAR_VALUE ;
7512+ dst_reg -> type = SCALAR_VALUE ;
75117513 __mark_reg_known (& regs [insn -> dst_reg ], imm );
75127514 return 0 ;
75137515 }
75147516
7517+ if (insn -> src_reg == BPF_PSEUDO_BTF_ID ) {
7518+ mark_reg_known_zero (env , regs , insn -> dst_reg );
7519+
7520+ dst_reg -> type = aux -> btf_var .reg_type ;
7521+ switch (dst_reg -> type ) {
7522+ case PTR_TO_MEM :
7523+ dst_reg -> mem_size = aux -> btf_var .mem_size ;
7524+ break ;
7525+ case PTR_TO_BTF_ID :
7526+ dst_reg -> btf_id = aux -> btf_var .btf_id ;
7527+ break ;
7528+ default :
7529+ verbose (env , "bpf verifier is misconfigured\n" );
7530+ return - EFAULT ;
7531+ }
7532+ return 0 ;
7533+ }
7534+
75157535 map = env -> used_maps [aux -> map_index ];
75167536 mark_reg_known_zero (env , regs , insn -> dst_reg );
7517- regs [ insn -> dst_reg ]. map_ptr = map ;
7537+ dst_reg -> map_ptr = map ;
75187538
75197539 if (insn -> src_reg == BPF_PSEUDO_MAP_VALUE ) {
7520- regs [ insn -> dst_reg ]. type = PTR_TO_MAP_VALUE ;
7521- regs [ insn -> dst_reg ]. off = aux -> map_off ;
7540+ dst_reg -> type = PTR_TO_MAP_VALUE ;
7541+ dst_reg -> off = aux -> map_off ;
75227542 if (map_value_has_spin_lock (map ))
7523- regs [ insn -> dst_reg ]. id = ++ env -> id_gen ;
7543+ dst_reg -> id = ++ env -> id_gen ;
75247544 } else if (insn -> src_reg == BPF_PSEUDO_MAP_FD ) {
7525- regs [ insn -> dst_reg ]. type = CONST_PTR_TO_MAP ;
7545+ dst_reg -> type = CONST_PTR_TO_MAP ;
75267546 } else {
75277547 verbose (env , "bpf verifier is misconfigured\n" );
75287548 return - EINVAL ;
@@ -9424,6 +9444,73 @@ static int do_check(struct bpf_verifier_env *env)
94249444 return 0 ;
94259445}
94269446
9447+ /* replace pseudo btf_id with kernel symbol address */
9448+ static int check_pseudo_btf_id (struct bpf_verifier_env * env ,
9449+ struct bpf_insn * insn ,
9450+ struct bpf_insn_aux_data * aux )
9451+ {
9452+ u32 type , id = insn -> imm ;
9453+ const struct btf_type * t ;
9454+ const char * sym_name ;
9455+ u64 addr ;
9456+
9457+ if (!btf_vmlinux ) {
9458+ verbose (env , "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n" );
9459+ return - EINVAL ;
9460+ }
9461+
9462+ if (insn [1 ].imm != 0 ) {
9463+ verbose (env , "reserved field (insn[1].imm) is used in pseudo_btf_id ldimm64 insn.\n" );
9464+ return - EINVAL ;
9465+ }
9466+
9467+ t = btf_type_by_id (btf_vmlinux , id );
9468+ if (!t ) {
9469+ verbose (env , "ldimm64 insn specifies invalid btf_id %d.\n" , id );
9470+ return - ENOENT ;
9471+ }
9472+
9473+ if (!btf_type_is_var (t )) {
9474+ verbose (env , "pseudo btf_id %d in ldimm64 isn't KIND_VAR.\n" ,
9475+ id );
9476+ return - EINVAL ;
9477+ }
9478+
9479+ sym_name = btf_name_by_offset (btf_vmlinux , t -> name_off );
9480+ addr = kallsyms_lookup_name (sym_name );
9481+ if (!addr ) {
9482+ verbose (env , "ldimm64 failed to find the address for kernel symbol '%s'.\n" ,
9483+ sym_name );
9484+ return - ENOENT ;
9485+ }
9486+
9487+ insn [0 ].imm = (u32 )addr ;
9488+ insn [1 ].imm = addr >> 32 ;
9489+
9490+ type = t -> type ;
9491+ t = btf_type_skip_modifiers (btf_vmlinux , type , NULL );
9492+ if (!btf_type_is_struct (t )) {
9493+ const struct btf_type * ret ;
9494+ const char * tname ;
9495+ u32 tsize ;
9496+
9497+ /* resolve the type size of ksym. */
9498+ ret = btf_resolve_size (btf_vmlinux , t , & tsize );
9499+ if (IS_ERR (ret )) {
9500+ tname = btf_name_by_offset (btf_vmlinux , t -> name_off );
9501+ verbose (env , "ldimm64 unable to resolve the size of type '%s': %ld\n" ,
9502+ tname , PTR_ERR (ret ));
9503+ return - EINVAL ;
9504+ }
9505+ aux -> btf_var .reg_type = PTR_TO_MEM ;
9506+ aux -> btf_var .mem_size = tsize ;
9507+ } else {
9508+ aux -> btf_var .reg_type = PTR_TO_BTF_ID ;
9509+ aux -> btf_var .btf_id = type ;
9510+ }
9511+ return 0 ;
9512+ }
9513+
94279514static int check_map_prealloc (struct bpf_map * map )
94289515{
94299516 return (map -> map_type != BPF_MAP_TYPE_HASH &&
@@ -9534,10 +9621,14 @@ static bool bpf_map_is_cgroup_storage(struct bpf_map *map)
95349621 map -> map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE );
95359622}
95369623
9537- /* look for pseudo eBPF instructions that access map FDs and
9538- * replace them with actual map pointers
9624+ /* find and rewrite pseudo imm in ld_imm64 instructions:
9625+ *
9626+ * 1. if it accesses map FD, replace it with actual map pointer.
9627+ * 2. if it accesses btf_id of a VAR, replace it with pointer to the var.
9628+ *
9629+ * NOTE: btf_vmlinux is required for converting pseudo btf_id.
95399630 */
9540- static int replace_map_fd_with_map_ptr (struct bpf_verifier_env * env )
9631+ static int resolve_pseudo_ldimm64 (struct bpf_verifier_env * env )
95419632{
95429633 struct bpf_insn * insn = env -> prog -> insnsi ;
95439634 int insn_cnt = env -> prog -> len ;
@@ -9578,6 +9669,14 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env)
95789669 /* valid generic load 64-bit imm */
95799670 goto next_insn ;
95809671
9672+ if (insn [0 ].src_reg == BPF_PSEUDO_BTF_ID ) {
9673+ aux = & env -> insn_aux_data [i ];
9674+ err = check_pseudo_btf_id (env , insn , aux );
9675+ if (err )
9676+ return err ;
9677+ goto next_insn ;
9678+ }
9679+
95819680 /* In final convert_pseudo_ld_imm64() step, this is
95829681 * converted into regular 64-bit imm load insn.
95839682 */
@@ -11633,10 +11732,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
1163311732 if (is_priv )
1163411733 env -> test_state_freq = attr -> prog_flags & BPF_F_TEST_STATE_FREQ ;
1163511734
11636- ret = replace_map_fd_with_map_ptr (env );
11637- if (ret < 0 )
11638- goto skip_full_check ;
11639-
1164011735 if (bpf_prog_is_dev_bound (env -> prog -> aux )) {
1164111736 ret = bpf_prog_offload_verifier_prep (env -> prog );
1164211737 if (ret )
@@ -11662,6 +11757,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
1166211757 if (ret )
1166311758 goto skip_full_check ;
1166411759
11760+ ret = resolve_pseudo_ldimm64 (env );
11761+ if (ret < 0 )
11762+ goto skip_full_check ;
11763+
1166511764 ret = check_cfg (env );
1166611765 if (ret < 0 )
1166711766 goto skip_full_check ;
0 commit comments