Commit e400cfa3 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Paolo Abeni
Browse files

af_unix: Fix wrong ioctl(SIOCATMARK) when consumed OOB skb is at the head.



Even if OOB data is recv()ed, ioctl(SIOCATMARK) must return 1 when the
OOB skb is at the head of the receive queue and no new OOB data is queued.

Without fix:

  #  RUN           msg_oob.no_peek.oob ...
  # msg_oob.c:305:oob:Expected answ[0] (0) == oob_head (1)
  # oob: Test terminated by assertion
  #          FAIL  msg_oob.no_peek.oob
  not ok 2 msg_oob.no_peek.oob

With fix:

  #  RUN           msg_oob.no_peek.oob ...
  #            OK  msg_oob.no_peek.oob
  ok 2 msg_oob.no_peek.oob

Fixes: 314001f0 ("af_unix: Add OOB support")
Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 48a99837
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -3107,12 +3107,23 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
	case SIOCATMARK:
		{
			struct unix_sock *u = unix_sk(sk);
			struct sk_buff *skb;
			int answ = 0;

			mutex_lock(&u->iolock);

			skb = skb_peek(&sk->sk_receive_queue);
			if (skb && skb == READ_ONCE(unix_sk(sk)->oob_skb))
			if (skb) {
				struct sk_buff *oob_skb = READ_ONCE(u->oob_skb);

				if (skb == oob_skb ||
				    (!oob_skb && !unix_skb_len(skb)))
					answ = 1;
			}

			mutex_unlock(&u->iolock);

			err = put_user(answ, (int __user *)arg);
		}
		break;
+68 −0
Original line number Diff line number Diff line
@@ -288,6 +288,26 @@ static void __setinlinepair(struct __test_metadata *_metadata,
	}
}

static void __siocatmarkpair(struct __test_metadata *_metadata,
			     FIXTURE_DATA(msg_oob) *self,
			     bool oob_head)
{
	int answ[2] = {};
	int i;

	for (i = 0; i < 2; i++) {
		int ret;

		ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]);
		ASSERT_EQ(ret, 0);
	}

	ASSERT_EQ(answ[0], oob_head);

	if (self->tcp_compliant)
		ASSERT_EQ(answ[0], answ[1]);
}

#define sendpair(buf, len, flags)					\
	__sendpair(_metadata, self, buf, len, flags)

@@ -304,6 +324,9 @@ static void __setinlinepair(struct __test_metadata *_metadata,
#define epollpair(oob_remaining)					\
	__epollpair(_metadata, self, oob_remaining)

#define siocatmarkpair(oob_head)					\
	__siocatmarkpair(_metadata, self, oob_head)

#define setinlinepair()							\
	__setinlinepair(_metadata, self)

@@ -325,9 +348,11 @@ TEST_F(msg_oob, oob)
{
	sendpair("x", 1, MSG_OOB);
	epollpair(true);
	siocatmarkpair(true);

	recvpair("x", 1, 1, MSG_OOB);
	epollpair(false);
	siocatmarkpair(true);
}

TEST_F(msg_oob, oob_drop)
@@ -481,18 +506,40 @@ TEST_F(msg_oob, ex_oob_ahead_break)
	epollpair(false);
}

TEST_F(msg_oob, ex_oob_siocatmark)
{
	sendpair("hello", 5, MSG_OOB);
	epollpair(true);
	siocatmarkpair(false);

	recvpair("o", 1, 1, MSG_OOB);
	epollpair(false);
	siocatmarkpair(false);

	sendpair("world", 5, MSG_OOB);
	epollpair(true);
	siocatmarkpair(false);

	recvpair("hell", 4, 4, 0);		/* Intentionally stop at ex-OOB. */
	epollpair(true);
	siocatmarkpair(false);
}

TEST_F(msg_oob, inline_oob)
{
	setinlinepair();

	sendpair("x", 1, MSG_OOB);
	epollpair(true);
	siocatmarkpair(true);

	recvpair("", -EINVAL, 1, MSG_OOB);
	epollpair(true);
	siocatmarkpair(true);

	recvpair("x", 1, 1, 0);
	epollpair(false);
	siocatmarkpair(false);
}

TEST_F(msg_oob, inline_oob_break)
@@ -591,4 +638,25 @@ TEST_F(msg_oob, inline_ex_oob_drop)
	}
}

TEST_F(msg_oob, inline_ex_oob_siocatmark)
{
	sendpair("hello", 5, MSG_OOB);
	epollpair(true);
	siocatmarkpair(false);

	recvpair("o", 1, 1, MSG_OOB);
	epollpair(false);
	siocatmarkpair(false);

	setinlinepair();

	sendpair("world", 5, MSG_OOB);
	epollpair(true);
	siocatmarkpair(false);

	recvpair("hell", 4, 4, 0);		/* Intentionally stop at ex-OOB. */
	epollpair(true);
	siocatmarkpair(false);
}

TEST_HARNESS_MAIN