netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs
@ 2022-08-14 20:53 Kirill Tkhai
  2022-08-14 23:59 ` kernel test robot
  2022-08-22  5:25 ` kernel test robot
  0 siblings, 2 replies; 3+ messages in thread
From: Kirill Tkhai @ 2022-08-14 20:53 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Kirill Tkhai

When a fd owning a counter of some critical resource, say, of a mount,
it's impossible to umount that mount and disconnect related block device.
That fd may be contained in some unix socket receive queue skb.

Despite we have an interface for detecting such the sockets queues
(/proc/[PID]/fdinfo/[fd] shows non-zero scm_fds count if so) and
it's possible to kill that process to release the counter, the problem is
that there may be several processes, and it's not a good thing to kill
each of them.

This patch adds a simple interface to grab files from receive queue,
so the caller may analyze them, and even do that recursively, if grabbed
file is unix socket itself. So, the described above problem may be solved
by this ioctl() in pair with pidfd_getfd().

Note, that the existing recvmsg(,,MSG_PEEK) is not suitable for that
purpose, since it modifies peek offset inside socket, and this results
in a problem in case of examined process uses peek offset itself.
Additional ptrace freezing of that task plus ioctl(SO_PEEK_OFF) won't help
too, since that socket may relate to several tasks, and there is no
reliable and non-racy way to detect that. Also, if the caller of such
trick will die, the examined task will remain frozen forever. The new
suggested ioctl(SIOCUNIXGRABFDS) does not have such problems.

The realization of ioctl(SIOCUNIXGRABFDS) is pretty simple. The only
interesting thing is protocol with userspace. Firstly, we let userspace
to know the number of all files in receive queue skbs. Then we receive
fds one by one starting from requested offset. We return number of
received fds if there is a successfully received fd, and this number
may be less in case of error or desired fds number lack. Userspace
may detect that situations by comparison of returned value and
out.nr_all minus in.nr_skip. Looking over different variant this one
looks the best for me (I considered returning error in case of error
and there is a received fd. Also I considered returning number of
received files as one more member in struct unix_ioc_grab_fds).

Signed-off-by: Kirill Tkhai <tkhai@ya.ru>
---
 include/uapi/linux/un.h |   12 ++++++++
 net/unix/af_unix.c      |   70 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/include/uapi/linux/un.h b/include/uapi/linux/un.h
index 0ad59dc8b686..995b358263dd 100644
--- a/include/uapi/linux/un.h
+++ b/include/uapi/linux/un.h
@@ -11,6 +11,18 @@ struct sockaddr_un {
 	char sun_path[UNIX_PATH_MAX];	/* pathname */
 };
 
+struct unix_ioc_grab_fds {
+	struct {
+		int nr_grab;
+		int nr_skip;
+		int *fds;
+	} in;
+	struct {
+		int nr_all;
+	} out;
+};
+
 #define SIOCUNIXFILE (SIOCPROTOPRIVATE + 0) /* open a socket file with O_PATH */
+#define SIOCUNIXGRABFDS (SIOCPROTOPRIVATE + 1) /* grab files from recv queue */
 
 #endif /* _LINUX_UN_H */
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index bf338b782fc4..3c7e8049eba1 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -3079,6 +3079,73 @@ static int unix_open_file(struct sock *sk)
 	return fd;
 }
 
+static int unix_ioc_grab_fds(struct sock *sk, struct unix_ioc_grab_fds __user *uarg)
+{
+	int i, todo, skip, count, all, err, done = 0;
+	struct unix_sock *u = unix_sk(sk);
+	struct unix_ioc_grab_fds arg;
+	struct sk_buff *skb = NULL;
+	struct scm_fp_list *fp;
+
+	if (copy_from_user(&arg, uarg, sizeof(arg)))
+		return -EFAULT;
+
+	skip = arg.in.nr_skip;
+	todo = arg.in.nr_grab;
+
+	if (skip < 0 || todo <= 0)
+		return -EINVAL;
+	if (mutex_lock_interruptible(&u->iolock))
+		return -EINTR;
+
+	all = atomic_read(&u->scm_stat.nr_fds);
+	err = -EFAULT;
+	/* Set uarg->out.nr_all before the first file is received. */
+	if (put_user(all, &uarg->out.nr_all))
+		goto unlock;
+	err = 0;
+	if (all <= skip)
+		goto unlock;
+	if (all - skip < todo)
+		todo = all - skip;
+	while (todo) {
+		spin_lock(&sk->sk_receive_queue.lock);
+		if (!skb)
+			skb = skb_peek(&sk->sk_receive_queue);
+		else
+			skb = skb_peek_next(skb, &sk->sk_receive_queue);
+		spin_unlock(&sk->sk_receive_queue.lock);
+
+		if (!skb)
+			goto unlock;
+
+		fp = UNIXCB(skb).fp;
+		count = fp->count;
+		if (skip >= count) {
+			skip -= count;
+			continue;
+		}
+
+		for (i = skip; i < count && todo; i++) {
+			err = receive_fd_user(fp->fp[i], &arg.in.fds[done], 0);
+			if (err < 0)
+				goto unlock;
+			done++;
+			todo--;
+		}
+		skip = 0;
+	}
+unlock:
+	mutex_unlock(&u->iolock);
+
+	/* Return number of fds (non-error) if there is a received file. */
+	if (done)
+		return done;
+	if (err < 0)
+		return err;
+	return 0;
+}
+
 static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
@@ -3113,6 +3180,9 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 		}
 		break;
 #endif
+	case SIOCUNIXGRABFDS:
+		err = unix_ioc_grab_fds(sk, (struct unix_ioc_grab_fds __user *)arg);
+		break;
 	default:
 		err = -ENOIOCTLCMD;
 		break;



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs
  2022-08-14 20:53 [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs Kirill Tkhai
@ 2022-08-14 23:59 ` kernel test robot
  2022-08-22  5:25 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kernel test robot @ 2022-08-14 23:59 UTC (permalink / raw)
  To: Kirill Tkhai, netdev
  Cc: llvm, kbuild-all, Eric Dumazet, Paolo Abeni, Kirill Tkhai

Hi Kirill,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net/master]
[also build test ERROR on net-next/master linus/master v5.19 next-20220812]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kirill-Tkhai/af_unix-Add-ioctl-SIOCUNIXGRABFDS-to-grab-files-of-receive-queue-skbs/20220815-045608
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git 777885673122b78b2abd2f1e428730961a786ff2
config: s390-randconfig-r044-20220815 (https://download.01.org/0day-ci/archive/20220815/202208150743.t05nZxqC-lkp@intel.com/config)
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 3329cec2f79185bafd678f310fafadba2a8c76d2)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install s390 cross compiling tool for clang build
        # apt-get install binutils-s390x-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/0b4bc309fb3cdc6e470ee5c28e33f2909bfb8266
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kirill-Tkhai/af_unix-Add-ioctl-SIOCUNIXGRABFDS-to-grab-files-of-receive-queue-skbs/20220815-045608
        git checkout 0b4bc309fb3cdc6e470ee5c28e33f2909bfb8266
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>, old ones prefixed by <<):

ERROR: modpost: "devm_ioremap" [drivers/net/ethernet/altera/altera_tse.ko] undefined!
>> ERROR: modpost: "__receive_fd" [net/unix/unix.ko] undefined!

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs
  2022-08-14 20:53 [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs Kirill Tkhai
  2022-08-14 23:59 ` kernel test robot
@ 2022-08-22  5:25 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kernel test robot @ 2022-08-22  5:25 UTC (permalink / raw)
  To: Kirill Tkhai, netdev; +Cc: kbuild-all, Eric Dumazet, Paolo Abeni, Kirill Tkhai

Hi Kirill,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net/master]
[also build test WARNING on net-next/master linus/master horms-ipvs/master v6.0-rc2 next-20220819]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kirill-Tkhai/af_unix-Add-ioctl-SIOCUNIXGRABFDS-to-grab-files-of-receive-queue-skbs/20220815-045608
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git 777885673122b78b2abd2f1e428730961a786ff2
config: i386-randconfig-s003 (https://download.01.org/0day-ci/archive/20220822/202208221324.VmNeMI4S-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.4-39-gce1a6720-dirty
        # https://github.com/intel-lab-lkp/linux/commit/0b4bc309fb3cdc6e470ee5c28e33f2909bfb8266
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kirill-Tkhai/af_unix-Add-ioctl-SIOCUNIXGRABFDS-to-grab-files-of-receive-queue-skbs/20220815-045608
        git checkout 0b4bc309fb3cdc6e470ee5c28e33f2909bfb8266
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash arch/x86/entry/ fs/cifs/ net/unix/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

sparse warnings: (new ones prefixed by >>)
>> net/unix/af_unix.c:3130:69: sparse: sparse: incorrect type in argument 2 (different address spaces) @@     expected int [noderef] __user *ufd @@     got int * @@
   net/unix/af_unix.c:3130:69: sparse:     expected int [noderef] __user *ufd
   net/unix/af_unix.c:3130:69: sparse:     got int *
   net/unix/af_unix.c:159:13: sparse: sparse: context imbalance in 'unix_table_double_lock' - wrong count at exit
   net/unix/af_unix.c:178:28: sparse: sparse: context imbalance in 'unix_table_double_unlock' - unexpected unlock
   net/unix/af_unix.c:1290:13: sparse: sparse: context imbalance in 'unix_state_double_lock' - wrong count at exit
   net/unix/af_unix.c:1308:17: sparse: sparse: context imbalance in 'unix_state_double_unlock' - unexpected unlock
   net/unix/af_unix.c:1609:18: sparse: sparse: context imbalance in 'unix_stream_connect' - different lock contexts for basic block
   net/unix/af_unix.c:1972:25: sparse: sparse: context imbalance in 'unix_dgram_sendmsg' - unexpected unlock
   net/unix/af_unix.c:3325:20: sparse: sparse: context imbalance in 'unix_get_first' - wrong count at exit
   net/unix/af_unix.c:3356:34: sparse: sparse: context imbalance in 'unix_get_next' - unexpected unlock
   net/unix/af_unix.c:3386:42: sparse: sparse: context imbalance in 'unix_seq_stop' - unexpected unlock
   net/unix/af_unix.c:3489:34: sparse: sparse: context imbalance in 'bpf_iter_unix_hold_batch' - unexpected unlock

vim +3130 net/unix/af_unix.c

  3081	
  3082	static int unix_ioc_grab_fds(struct sock *sk, struct unix_ioc_grab_fds __user *uarg)
  3083	{
  3084		int i, todo, skip, count, all, err, done = 0;
  3085		struct unix_sock *u = unix_sk(sk);
  3086		struct unix_ioc_grab_fds arg;
  3087		struct sk_buff *skb = NULL;
  3088		struct scm_fp_list *fp;
  3089	
  3090		if (copy_from_user(&arg, uarg, sizeof(arg)))
  3091			return -EFAULT;
  3092	
  3093		skip = arg.in.nr_skip;
  3094		todo = arg.in.nr_grab;
  3095	
  3096		if (skip < 0 || todo <= 0)
  3097			return -EINVAL;
  3098		if (mutex_lock_interruptible(&u->iolock))
  3099			return -EINTR;
  3100	
  3101		all = atomic_read(&u->scm_stat.nr_fds);
  3102		err = -EFAULT;
  3103		/* Set uarg->out.nr_all before the first file is received. */
  3104		if (put_user(all, &uarg->out.nr_all))
  3105			goto unlock;
  3106		err = 0;
  3107		if (all <= skip)
  3108			goto unlock;
  3109		if (all - skip < todo)
  3110			todo = all - skip;
  3111		while (todo) {
  3112			spin_lock(&sk->sk_receive_queue.lock);
  3113			if (!skb)
  3114				skb = skb_peek(&sk->sk_receive_queue);
  3115			else
  3116				skb = skb_peek_next(skb, &sk->sk_receive_queue);
  3117			spin_unlock(&sk->sk_receive_queue.lock);
  3118	
  3119			if (!skb)
  3120				goto unlock;
  3121	
  3122			fp = UNIXCB(skb).fp;
  3123			count = fp->count;
  3124			if (skip >= count) {
  3125				skip -= count;
  3126				continue;
  3127			}
  3128	
  3129			for (i = skip; i < count && todo; i++) {
> 3130				err = receive_fd_user(fp->fp[i], &arg.in.fds[done], 0);
  3131				if (err < 0)
  3132					goto unlock;
  3133				done++;
  3134				todo--;
  3135			}
  3136			skip = 0;
  3137		}
  3138	unlock:
  3139		mutex_unlock(&u->iolock);
  3140	
  3141		/* Return number of fds (non-error) if there is a received file. */
  3142		if (done)
  3143			return done;
  3144		if (err < 0)
  3145			return err;
  3146		return 0;
  3147	}
  3148	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-08-22  5:25 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-14 20:53 [PATCH] af_unix: Add ioctl(SIOCUNIXGRABFDS) to grab files of receive queue skbs Kirill Tkhai
2022-08-14 23:59 ` kernel test robot
2022-08-22  5:25 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).