Commit f3b8c281 authored by Martin KaFai Lau's avatar Martin KaFai Lau
Browse files

Merge branch 'bpf-tcp-fix-type-confusion-in-bpf-helper-functions'

Kuniyuki Iwashima says:

====================
bpf: tcp: Fix type confusion in bpf helper functions.

bpf_tcp_sock() only check if sk->sk_protocol is IPPROTO_TCP,
but RAW socket can bypass it:

  socket(AF_INET, SOCK_RAW, IPPROTO_TCP)

The same issues exist in other bpf functions:

  * bpf_mptcp_sock_from_subflow()
  * bpf_skc_to_tcp_sock()
  * bpf_skc_to_tcp6_sock()
  * sol_tcp_sockopt()

Patch 1 fixes bpf_tcp_sock() and Patch 2 adds a test for it.
Patch 3 ~ 6 fix the rest of the functions above.

Changes:
  v2:
    * Inverse if (err) to if (!err) in the selftest
    * Add patch 3 ~ 6

  v1: https://lore.kernel.org/bpf/20260430184405.1227386-1-kuniyu@google.com/
      https://lore.kernel.org/mptcp/20260430-mptcp-bpf-mptcp-sock-type-v1-1-d2ed5cda7da9@kernel.org/
====================

Link: https://patch.msgid.link/20260504210610.180150-1-kuniyu@google.com


Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents 0c7ae130 1c2958e4
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -5481,7 +5481,7 @@ static int sol_tcp_sockopt(struct sock *sk, int optname,
			   char *optval, int *optlen,
			   bool getopt)
{
	if (sk->sk_protocol != IPPROTO_TCP)
	if (!sk_is_tcp(sk))
		return -EINVAL;

	switch (optname) {
@@ -7475,7 +7475,7 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type,

BPF_CALL_1(bpf_tcp_sock, struct sock *, sk)
{
	if (sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP)
	if (sk_fullsock(sk) && sk_is_tcp(sk))
		return (unsigned long)sk;

	return (unsigned long)NULL;
@@ -11947,7 +11947,7 @@ BPF_CALL_1(bpf_skc_to_tcp6_sock, struct sock *, sk)
	 */
	BTF_TYPE_EMIT(struct tcp6_sock);
	if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP &&
	    sk->sk_family == AF_INET6)
	    sk->sk_type == SOCK_STREAM && sk->sk_family == AF_INET6)
		return (unsigned long)sk;

	return (unsigned long)NULL;
@@ -11963,7 +11963,7 @@ const struct bpf_func_proto bpf_skc_to_tcp6_sock_proto = {

BPF_CALL_1(bpf_skc_to_tcp_sock, struct sock *, sk)
{
	if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP)
	if (sk && sk_fullsock(sk) && sk_is_tcp(sk))
		return (unsigned long)sk;

	return (unsigned long)NULL;
+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@

struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk)
{
	if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && sk_is_mptcp(sk))
	if (sk && sk_fullsock(sk) && sk_is_tcp(sk) && sk_is_mptcp(sk))
		return mptcp_sk(mptcp_subflow_ctx(sk)->conn);

	return NULL;
+16 −1
Original line number Diff line number Diff line
@@ -190,7 +190,7 @@ static int getsetsockopt(void)
	fd = socket(AF_NETLINK, SOCK_RAW, 0);
	if (fd < 0) {
		log_err("Failed to create AF_NETLINK socket");
		return -1;
		goto err;
	}

	buf.u32 = 1;
@@ -211,6 +211,21 @@ static int getsetsockopt(void)
	}
	ASSERT_EQ(optlen, 8, "Unexpected NETLINK_LIST_MEMBERSHIPS value");

	/* Trick bpf_tcp_sock() with IPPROTO_TCP */
	close(fd);
	fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	if (!ASSERT_OK_FD(fd, "socket"))
		goto err;

	/* The BPF prog intercepts this before the kernel sees it, any
	 * optlen works. Go with 4 bytes for simplicity.
	 */
	buf.u32 = 1;
	optlen = sizeof(buf.u32);
	err = setsockopt(fd, SOL_TCP, TCP_SAVED_SYN, &buf, optlen);
	if (!ASSERT_ERR(err, "setsockopt(TCP_SAVED_SYN)"))
		goto err;

	free(big_buf);
	close(fd);
	return 0;
+16 −0
Original line number Diff line number Diff line
@@ -149,6 +149,20 @@ int _setsockopt(struct bpf_sockopt *ctx)
	if (sk && sk->family == AF_NETLINK)
		goto out;

	if (sk && sk->family == AF_INET && sk->type == SOCK_RAW) {
		struct bpf_tcp_sock *tp = bpf_tcp_sock(sk);

		if (tp) {
			char saved_syn[60];

			bpf_getsockopt(sk, SOL_TCP, TCP_SAVED_SYN,
				       &saved_syn, sizeof(saved_syn));
			goto consumed;
		}

		goto out;
	}

	/* Make sure bpf_get_netns_cookie is callable.
	 */
	if (bpf_get_netns_cookie(NULL) == 0)
@@ -224,6 +238,8 @@ int _setsockopt(struct bpf_sockopt *ctx)
		return 0; /* couldn't get sk storage */

	storage->val = optval[0];

consumed:
	ctx->optlen = -1; /* BPF has consumed this option, don't call kernel
			   * setsockopt handler.
			   */