From: Mykyta Yatsenko File dynptr reads may sleep when the requested folios are not in the page cache. To avoid sleeping in non-sleepable contexts while still supporting valid sleepable use, given that dynptrs are non-sleepable by default, enable sleeping only when bpf_dynptr_from_file() is invoked from a sleepable context. This change: * Introduces a sleepable constructor: bpf_dynptr_from_file_sleepable() * Override non-sleepable constructor with sleepable if it's always called in sleepable context Signed-off-by: Mykyta Yatsenko --- include/linux/bpf.h | 3 +++ kernel/bpf/helpers.c | 5 +++++ kernel/bpf/verifier.c | 11 ++++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 1d7d50d0c587..73fca44a3dfa 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -670,6 +670,9 @@ static inline bool bpf_map_has_internal_structs(struct bpf_map *map) void bpf_map_free_internal_structs(struct bpf_map *map, void *obj); +int bpf_dynptr_from_file_sleepable(struct file *file, u32 flags, + struct bpf_dynptr *ptr__uninit); + extern const struct bpf_map_ops bpf_map_offload_ops; /* bpf_type_flag contains a set of flags that are applicable to the values of diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index da83298bf916..42e2add35589 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -4337,6 +4337,11 @@ __bpf_kfunc int bpf_dynptr_from_file(struct file *file, u32 flags, struct bpf_dy return make_file_dynptr(file, flags, false, (struct bpf_dynptr_kern *)ptr__uninit); } +int bpf_dynptr_from_file_sleepable(struct file *file, u32 flags, struct bpf_dynptr *ptr__uninit) +{ + return make_file_dynptr(file, flags, true, (struct bpf_dynptr_kern *)ptr__uninit); +} + __bpf_kfunc int bpf_dynptr_file_discard(struct bpf_dynptr *dynptr) { struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)dynptr; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f9f7151eaf1f..673923ecd465 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3126,7 +3126,8 @@ struct bpf_kfunc_btf_tab { static unsigned long kfunc_call_imm(unsigned long func_addr, u32 func_id); -static void specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc); +static void specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc, + int insn_idx); static int kfunc_desc_cmp_by_id_off(const void *a, const void *b) { @@ -21870,7 +21871,8 @@ static unsigned long kfunc_call_imm(unsigned long func_addr, u32 func_id) } /* replace a generic kfunc with a specialized version if necessary */ -static void specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc) +static void specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc, + int insn_idx) { struct bpf_prog *prog = env->prog; bool seen_direct_write; @@ -21905,6 +21907,9 @@ static void specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc } else if (func_id == special_kfunc_list[KF_bpf_remove_dentry_xattr]) { if (bpf_lsm_has_d_inode_locked(prog)) addr = (unsigned long)bpf_remove_dentry_xattr_locked; + } else if (func_id == special_kfunc_list[KF_bpf_dynptr_from_file]) { + if (!env->insn_aux_data[insn_idx].non_sleepable) + addr = (unsigned long)bpf_dynptr_from_file_sleepable; } if (!addr) /* Nothing to patch with */ @@ -21954,7 +21959,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EFAULT; } - specialize_kfunc(env, desc); + specialize_kfunc(env, desc, insn_idx); if (!bpf_jit_supports_far_kfunc_call()) insn->imm = BPF_CALL_IMM(desc->addr); -- 2.51.0