Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Remove redundant free_verifier_state()/pop_stack()

This patch removes duplicated code.

Eduard points out [1]:

Same cleanup cycles are done in push_stack() and push_async_cb(),
both functions are only reachable from do_check_common() via
do_check() -> do_check_insn().

Hence, I think that cur state should not be freed in push_*()
functions and pop_stack() loop there is not needed.

This would also fix the 'symptom' for [2], but the issue also has a
simpler fix which was sent separately. This fix also makes sure the
push_*() callers always return an error for which
error_recoverable_with_nospec(err) is false. This is required because
otherwise we try to recover and access the stale `state`.

Moving free_verifier_state() and pop_stack(..., pop_log=false) to happen
after the bpf_vlog_reset() call in do_check_common() is fine because the
pop_stack() call that is moved does not call bpf_vlog_reset() with the
pop_log=false parameter.

[1] https://lore.kernel.org/all/b6931bd0dd72327c55287862f821ca6c4c3eb69a.camel@gmail.com/
[2] https://lore.kernel.org/all/68497853.050a0220.33aa0e.036a.GAE@google.com/

Reported-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/all/b6931bd0dd72327c55287862f821ca6c4c3eb69a.camel@gmail.com/
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de>
Link: https://lore.kernel.org/r/20250613090157.568349-2-luis.gerhorst@fau.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Luis Gerhorst and committed by
Alexei Starovoitov
f66b4aaf 4a4b84ba

+10 -26
+10 -26
kernel/bpf/verifier.c
··· 2097 2097 2098 2098 elem = kzalloc(sizeof(struct bpf_verifier_stack_elem), GFP_KERNEL_ACCOUNT); 2099 2099 if (!elem) 2100 - goto err; 2100 + return NULL; 2101 2101 2102 2102 elem->insn_idx = insn_idx; 2103 2103 elem->prev_insn_idx = prev_insn_idx; ··· 2107 2107 env->stack_size++; 2108 2108 err = copy_verifier_state(&elem->st, cur); 2109 2109 if (err) 2110 - goto err; 2110 + return NULL; 2111 2111 elem->st.speculative |= speculative; 2112 2112 if (env->stack_size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) { 2113 2113 verbose(env, "The sequence of %d jumps is too complex.\n", 2114 2114 env->stack_size); 2115 - goto err; 2115 + return NULL; 2116 2116 } 2117 2117 if (elem->st.parent) { 2118 2118 ++elem->st.parent->branches; ··· 2127 2127 */ 2128 2128 } 2129 2129 return &elem->st; 2130 - err: 2131 - free_verifier_state(env->cur_state, true); 2132 - env->cur_state = NULL; 2133 - /* pop all elements and return */ 2134 - while (!pop_stack(env, NULL, NULL, false)); 2135 - return NULL; 2136 2130 } 2137 2131 2138 2132 #define CALLER_SAVED_REGS 6 ··· 2858 2864 2859 2865 elem = kzalloc(sizeof(struct bpf_verifier_stack_elem), GFP_KERNEL_ACCOUNT); 2860 2866 if (!elem) 2861 - goto err; 2867 + return NULL; 2862 2868 2863 2869 elem->insn_idx = insn_idx; 2864 2870 elem->prev_insn_idx = prev_insn_idx; ··· 2870 2876 verbose(env, 2871 2877 "The sequence of %d jumps is too complex for async cb.\n", 2872 2878 env->stack_size); 2873 - goto err; 2879 + return NULL; 2874 2880 } 2875 2881 /* Unlike push_stack() do not copy_verifier_state(). 2876 2882 * The caller state doesn't matter. ··· 2881 2887 elem->st.in_sleepable = is_sleepable; 2882 2888 frame = kzalloc(sizeof(*frame), GFP_KERNEL_ACCOUNT); 2883 2889 if (!frame) 2884 - goto err; 2890 + return NULL; 2885 2891 init_func_state(env, frame, 2886 2892 BPF_MAIN_FUNC /* callsite */, 2887 2893 0 /* frameno within this callchain */, 2888 2894 subprog /* subprog number within this prog */); 2889 2895 elem->st.frame[0] = frame; 2890 2896 return &elem->st; 2891 - err: 2892 - free_verifier_state(env->cur_state, true); 2893 - env->cur_state = NULL; 2894 - /* pop all elements and return */ 2895 - while (!pop_stack(env, NULL, NULL, false)); 2896 - return NULL; 2897 2897 } 2898 2898 2899 2899 ··· 22923 22935 struct bpf_scc_info *info; 22924 22936 int i, j; 22925 22937 22938 + free_verifier_state(env->cur_state, true); 22939 + env->cur_state = NULL; 22940 + while (!pop_stack(env, NULL, NULL, false)); 22941 + 22926 22942 list_for_each_safe(pos, tmp, &env->free_list) { 22927 22943 sl = container_of(pos, struct bpf_verifier_state_list, node); 22928 22944 free_verifier_state(&sl->state, false); ··· 23078 23086 23079 23087 ret = do_check(env); 23080 23088 out: 23081 - /* check for NULL is necessary, since cur_state can be freed inside 23082 - * do_check() under memory pressure. 23083 - */ 23084 - if (env->cur_state) { 23085 - free_verifier_state(env->cur_state, true); 23086 - env->cur_state = NULL; 23087 - } 23088 - while (!pop_stack(env, NULL, NULL, false)); 23089 23089 if (!ret && pop_log) 23090 23090 bpf_vlog_reset(&env->log, 0); 23091 23091 free_states(env);