const struct btf *btf, u32 func_id,
                                    struct bpf_reg_state *regs,
                                    bool ptr_to_mem_ok,
-                                   u32 kfunc_flags)
+                                   u32 kfunc_flags,
+                                   bool processing_call)
 {
        enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
        bool rel = false, kptr_get = false, trusted_arg = false;
                                        reg_ref_tname);
                                return -EINVAL;
                        }
-               } else if (ptr_to_mem_ok) {
+               } else if (ptr_to_mem_ok && processing_call) {
                        const struct btf_type *resolve_ret;
                        u32 type_size;
 
        return rel ? ref_regno : 0;
 }
 
-/* Compare BTF of a function with given bpf_reg_state.
+/* Compare BTF of a function declaration with given bpf_reg_state.
  * Returns:
  * EFAULT - there is a verifier bug. Abort verification.
  * EINVAL - there is a type mismatch or BTF is not available.
                return -EINVAL;
 
        is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
-       err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, 0);
+       err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, 0, false);
+
+       /* Compiler optimizations can remove arguments from static functions
+        * or mismatched type can be passed into a global function.
+        * In such cases mark the function as unreliable from BTF point of view.
+        */
+       if (err)
+               prog->aux->func_info_aux[subprog].unreliable = true;
+       return err;
+}
+
+/* Compare BTF of a function call with given bpf_reg_state.
+ * Returns:
+ * EFAULT - there is a verifier bug. Abort verification.
+ * EINVAL - there is a type mismatch or BTF is not available.
+ * 0 - BTF matches with what bpf_reg_state expects.
+ * Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
+ *
+ * NOTE: the code is duplicated from btf_check_subprog_arg_match()
+ * because btf_check_func_arg_match() is still doing both. Once that
+ * function is split in 2, we can call from here btf_check_subprog_arg_match()
+ * first, and then treat the calling part in a new code path.
+ */
+int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
+                          struct bpf_reg_state *regs)
+{
+       struct bpf_prog *prog = env->prog;
+       struct btf *btf = prog->aux->btf;
+       bool is_global;
+       u32 btf_id;
+       int err;
+
+       if (!prog->aux->func_info)
+               return -EINVAL;
+
+       btf_id = prog->aux->func_info[subprog].type_id;
+       if (!btf_id)
+               return -EFAULT;
+
+       if (prog->aux->func_info_aux[subprog].unreliable)
+               return -EINVAL;
+
+       is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
+       err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, 0, true);
 
        /* Compiler optimizations can remove arguments from static functions
         * or mismatched type can be passed into a global function.
                              struct bpf_reg_state *regs,
                              u32 kfunc_flags)
 {
-       return btf_check_func_arg_match(env, btf, func_id, regs, true, kfunc_flags);
+       return btf_check_func_arg_match(env, btf, func_id, regs, true, kfunc_flags, true);
 }
 
 /* Convert BTF of a function into bpf_reg_state if possible