LCOV - code coverage report
Current view: top level - net/unix - scm.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 60 63 95.2 %
Date: 2021-04-22 12:43:58 Functions: 7 7 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : #include <linux/module.h>
       3             : #include <linux/kernel.h>
       4             : #include <linux/string.h>
       5             : #include <linux/socket.h>
       6             : #include <linux/net.h>
       7             : #include <linux/fs.h>
       8             : #include <net/af_unix.h>
       9             : #include <net/scm.h>
      10             : #include <linux/init.h>
      11             : #include <linux/io_uring.h>
      12             : 
      13             : #include "scm.h"
      14             : 
      15             : unsigned int unix_tot_inflight;
      16             : EXPORT_SYMBOL(unix_tot_inflight);
      17             : 
      18             : LIST_HEAD(gc_inflight_list);
      19             : EXPORT_SYMBOL(gc_inflight_list);
      20             : 
      21             : DEFINE_SPINLOCK(unix_gc_lock);
      22             : EXPORT_SYMBOL(unix_gc_lock);
      23             : 
      24         108 : struct sock *unix_get_socket(struct file *filp)
      25             : {
      26         108 :         struct sock *u_sock = NULL;
      27         108 :         struct inode *inode = file_inode(filp);
      28             : 
      29             :         /* Socket ? */
      30         108 :         if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
      31          90 :                 struct socket *sock = SOCKET_I(inode);
      32          90 :                 struct sock *s = sock->sk;
      33             : 
      34             :                 /* PF_UNIX ? */
      35          90 :                 if (s && sock->ops && sock->ops->family == PF_UNIX)
      36          90 :                         u_sock = s;
      37             :         } else {
      38             :                 /* Could be an io_uring instance */
      39         108 :                 u_sock = io_uring_get_socket(filp);
      40             :         }
      41         108 :         return u_sock;
      42             : }
      43             : EXPORT_SYMBOL(unix_get_socket);
      44             : 
      45             : /* Keep the number of times in flight count for the file
      46             :  * descriptor if it is for an AF_UNIX socket.
      47             :  */
      48          54 : void unix_inflight(struct user_struct *user, struct file *fp)
      49             : {
      50          54 :         struct sock *s = unix_get_socket(fp);
      51             : 
      52          54 :         spin_lock(&unix_gc_lock);
      53             : 
      54          54 :         if (s) {
      55          45 :                 struct unix_sock *u = unix_sk(s);
      56             : 
      57          90 :                 if (atomic_long_inc_return(&u->inflight) == 1) {
      58          45 :                         BUG_ON(!list_empty(&u->link));
      59          45 :                         list_add_tail(&u->link, &gc_inflight_list);
      60             :                 } else {
      61           0 :                         BUG_ON(list_empty(&u->link));
      62             :                 }
      63          45 :                 unix_tot_inflight++;
      64             :         }
      65          54 :         user->unix_inflight++;
      66          54 :         spin_unlock(&unix_gc_lock);
      67          54 : }
      68             : 
      69          54 : void unix_notinflight(struct user_struct *user, struct file *fp)
      70             : {
      71          54 :         struct sock *s = unix_get_socket(fp);
      72             : 
      73          54 :         spin_lock(&unix_gc_lock);
      74             : 
      75          54 :         if (s) {
      76          45 :                 struct unix_sock *u = unix_sk(s);
      77             : 
      78          45 :                 BUG_ON(!atomic_long_read(&u->inflight));
      79          45 :                 BUG_ON(list_empty(&u->link));
      80             : 
      81          90 :                 if (atomic_long_dec_and_test(&u->inflight))
      82          45 :                         list_del_init(&u->link);
      83          45 :                 unix_tot_inflight--;
      84             :         }
      85          54 :         user->unix_inflight--;
      86          54 :         spin_unlock(&unix_gc_lock);
      87          54 : }
      88             : 
      89             : /*
      90             :  * The "user->unix_inflight" variable is protected by the garbage
      91             :  * collection lock, and we just read it locklessly here. If you go
      92             :  * over the limit, there might be a tiny race in actually noticing
      93             :  * it across threads. Tough.
      94             :  */
      95          54 : static inline bool too_many_unix_fds(struct task_struct *p)
      96             : {
      97          54 :         struct user_struct *user = current_user();
      98             : 
      99          54 :         if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)))
     100           0 :                 return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
     101             :         return false;
     102             : }
     103             : 
     104          54 : int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
     105             : {
     106          54 :         int i;
     107             : 
     108          54 :         if (too_many_unix_fds(current))
     109             :                 return -ETOOMANYREFS;
     110             : 
     111             :         /*
     112             :          * Need to duplicate file references for the sake of garbage
     113             :          * collection.  Otherwise a socket in the fps might become a
     114             :          * candidate for GC while the skb is not yet queued.
     115             :          */
     116          54 :         UNIXCB(skb).fp = scm_fp_dup(scm->fp);
     117          54 :         if (!UNIXCB(skb).fp)
     118             :                 return -ENOMEM;
     119             : 
     120         108 :         for (i = scm->fp->count - 1; i >= 0; i--)
     121          54 :                 unix_inflight(scm->fp->user, scm->fp->fp[i]);
     122             :         return 0;
     123             : }
     124             : EXPORT_SYMBOL(unix_attach_fds);
     125             : 
     126          54 : void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
     127             : {
     128          54 :         int i;
     129             : 
     130          54 :         scm->fp = UNIXCB(skb).fp;
     131          54 :         UNIXCB(skb).fp = NULL;
     132             : 
     133         108 :         for (i = scm->fp->count-1; i >= 0; i--)
     134          54 :                 unix_notinflight(scm->fp->user, scm->fp->fp[i]);
     135          54 : }
     136             : EXPORT_SYMBOL(unix_detach_fds);
     137             : 
     138        2711 : void unix_destruct_scm(struct sk_buff *skb)
     139             : {
     140        2711 :         struct scm_cookie scm;
     141             : 
     142        2711 :         memset(&scm, 0, sizeof(scm));
     143        2711 :         scm.pid  = UNIXCB(skb).pid;
     144        2711 :         if (UNIXCB(skb).fp)
     145           0 :                 unix_detach_fds(&scm, skb);
     146             : 
     147             :         /* Alas, it calls VFS */
     148             :         /* So fscking what? fput() had been SMP-safe since the last Summer */
     149        2711 :         scm_destroy(&scm);
     150        2711 :         sock_wfree(skb);
     151        2712 : }
     152             : EXPORT_SYMBOL(unix_destruct_scm);

Generated by: LCOV version 1.14