static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
 {
-       struct bpf_verifier_state *state = env->cur_state;
+       struct bpf_verifier_state *state = env->cur_state, *prev_st;
        struct bpf_func_state *caller, *callee;
        struct bpf_reg_state *r0;
+       bool in_callback_fn;
        int err;
 
        callee = state->frame[state->curframe];
         * there function call logic would reschedule callback visit. If iteration
         * converges is_state_visited() would prune that visit eventually.
         */
-       if (callee->in_callback_fn)
+       in_callback_fn = callee->in_callback_fn;
+       if (in_callback_fn)
                *insn_idx = callee->callsite;
        else
                *insn_idx = callee->callsite + 1;
         * bpf_throw, this will be done by copy_verifier_state for extra frames. */
        free_func_state(callee);
        state->frame[state->curframe--] = NULL;
+
+       /* for callbacks widen imprecise scalars to make programs like below verify:
+        *
+        *   struct ctx { int i; }
+        *   void cb(int idx, struct ctx *ctx) { ctx->i++; ... }
+        *   ...
+        *   struct ctx = { .i = 0; }
+        *   bpf_loop(100, cb, &ctx, 0);
+        *
+        * This is similar to what is done in process_iter_next_call() for open
+        * coded iterators.
+        */
+       prev_st = in_callback_fn ? find_prev_entry(env, state, *insn_idx) : NULL;
+       if (prev_st) {
+               err = widen_imprecise_scalars(env, prev_st, state);
+               if (err)
+                       return err;
+       }
        return 0;
 }