bool is_narrower_load;
        u32 target_size;
 
-       if (ops->gen_prologue) {
+       if (ops->gen_prologue || env->seen_direct_write) {
+               if (!ops->gen_prologue) {
+                       verbose(env, "bpf verifier is misconfigured\n");
+                       return -EINVAL;
+               }
                cnt = ops->gen_prologue(insn_buf, env->seen_direct_write,
                                        env->prog);
                if (cnt >= ARRAY_SIZE(insn_buf)) {
 
                                               prog->expected_attach_type);
 }
 
+static int bpf_noop_prologue(struct bpf_insn *insn_buf, bool direct_write,
+                            const struct bpf_prog *prog)
+{
+       /* Neither direct read nor direct write requires any preliminary
+        * action.
+        */
+       return 0;
+}
+
 static int bpf_unclone_prologue(struct bpf_insn *insn_buf, bool direct_write,
                                const struct bpf_prog *prog, int drop_verdict)
 {
        .get_func_proto         = xdp_func_proto,
        .is_valid_access        = xdp_is_valid_access,
        .convert_ctx_access     = xdp_convert_ctx_access,
+       .gen_prologue           = bpf_noop_prologue,
 };
 
 const struct bpf_prog_ops xdp_prog_ops = {
        .get_func_proto         = sk_msg_func_proto,
        .is_valid_access        = sk_msg_is_valid_access,
        .convert_ctx_access     = sk_msg_convert_ctx_access,
+       .gen_prologue           = bpf_noop_prologue,
 };
 
 const struct bpf_prog_ops sk_msg_prog_ops = {