From: Alexei Starovoitov After bpf_msg_apply_bytes() a verdict can apply to a part of the message. Then tcp_bpf_send_verdict() sends this part and goes to more_data, and the sk_msg prog runs on the rest. The SK_REDIRECT branch does not go there if the message is the cork. It removes the cork from the psock before it releases the socket lock. After the redirect it frees the cork together with the rest. Thus the rest is lost, and sendmsg() reports it as sent. If bytes remain after the redirect, put the cork back into the psock. Then the prog runs on the rest, as after SK_PASS. If the new verdict is SK_DROP or the push fails, sendmsg() now returns a smaller value or the error. msg->cork_bytes keeps its value, thus the rest can stay in the cork. If the psock has a new cork after lock_sock(), the kernel still frees the rest. If the redirect of a cork fails, sendmsg() still returns success and frees it. Fixes: 4f738adba30a ("bpf: create tcp_bpf_ulp allowing BPF to monitor socket TX/RX data") Signed-off-by: Alexei Starovoitov --- net/ipv4/tcp_bpf.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 9bdcbb0a5998..a9af6be2aadb 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -536,6 +536,14 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, if (!cork) *copied -= free; } + /* + * The verdict was for tosend bytes. The rest goes back, unless + * another thread made a new cork while the lock was released. + */ + if (cork && msg->sg.size && !psock->cork) { + psock->cork = msg; + cork = false; + } if (cork) { sk_msg_free(sk, msg); kfree(msg); -- 2.55.0