概述

  1. 从____sys_sendmsg的实现上看,其在开辟内存后,拷贝了ctrl_buf以后,执行完send操作,就立马释放了,因此需要配合userfaultfd来使用,从而达到持久留驻在内存中的目的。
  2. 建议每次spray都使用单独的sock:
  3. 单个sock的可发送数据包的上限是0x5000
  4. 单次sendmsg可发送的数据包的上限也是0x5000

    代码

    ```c

    define ALLOC_SIZE 0x1000

    define PAGE_ROUND_DOWN(x) (((unsigned long)(x)) & (~(PAGE_SIZE-1)))

void spray_sendmsg(){ int sockfd = socket(AF_INET, SOCK_DGRAM, 0); struct msghdr msgh = {0}; void* ctrl_buf;

  1. ctrl_buf = mmap( NULL, PAGE_ROUND_UP(ALLOC_SIZE), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
  2. memset( ctrl_buf, 0x61, PAGE_ROUND_DOWN(ALLOC_SIZE) );
  3. addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  4. addr.sin_family = AF_INET;
  5. addr.sin_port = htons(6666);
  6. // set user space buf(msg header)
  7. msgh.msg_control = ctrl_buf;
  8. msgh.msg_controllen = ALLOC_SIZE;
  9. msgh.msg_name = (caddr_t)&addr;
  10. msgh.msg_namelen = sizeof(addr);
  11. sendmsg(sockfd, &msgh, 0);

}

<a name="UkQOI"></a>
### 内核实现
<a name="RnzoB"></a>
### 内核数据结构
```c
struct msghdr {
  void    *msg_name;  /* ptr to socket address structure */
  int    msg_namelen;  /* size of socket address structure */

  struct iov_iter  msg_iter;  /* data */

  /*
   * Ancillary data. msg_control_user is the user buffer used for the
   * recv* side when msg_control_is_user is set, msg_control is the kernel
   * buffer used for all other cases.
   */
  union {
    void    *msg_control;
    void __user  *msg_control_user;              // 可控区域
  };
  bool    msg_control_is_user : 1;
  __kernel_size_t  msg_controllen;               // 可控区域长度

  unsigned int  msg_flags;  /* flags on received message */
  struct kiocb  *msg_iocb;  /* ptr to iocb for async requests */
};

代码分析

// 此刻msg_sys是保存用户态msghdr的内核变量,userd_address 和 allowed_msghdr_flags 是空
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
         unsigned int flags, struct used_address *used_address,
         unsigned int allowed_msghdr_flags)
{
  ...
  // 拷贝msg_control
  flags |= (msg_sys->msg_flags & allowed_msghdr_flags);
  ctl_len = msg_sys->msg_controllen;
  if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
    err = cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
                 sizeof(ctl));
    if (err)
      goto out;
    ctl_buf = msg_sys->msg_control;
    ctl_len = msg_sys->msg_controllen;
  } else if (ctl_len) {
    BUILD_BUG_ON(sizeof(struct cmsghdr) !=
           CMSG_ALIGN(sizeof(struct cmsghdr)));
    if (ctl_len > sizeof(ctl)) {
      ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);            [1]
      if (ctl_buf == NULL)
        goto out;
    }
    err = -EFAULT;
    if (copy_from_user(ctl_buf, msg_sys->msg_control_user, ctl_len))    [2]
      goto out_freectl;
    msg_sys->msg_control = ctl_buf;
    msg_sys->msg_control_is_user = false;
  }
  ...
  err = sock_sendmsg(sock, msg_sys);                                    [3]
  ...
out_freectl:
  if (ctl_buf != ctl)
    sock_kfree_s(sock->sk, ctl_buf, ctl_len);                            [4]
out:
  return err;
}

[1] sock_kmalloc

在对用户态的size大小进行校验后,直接传递给了kmalloc来开辟内核空间

void *sock_kmalloc(struct sock *sk, int size, gfp_t priority)
{
  if ((unsigned int)size <= sysctl_optmem_max &&
      atomic_read(&sk->sk_omem_alloc) + size < sysctl_optmem_max) {      [5] 
    void *mem;
    atomic_add(size, &sk->sk_omem_alloc);
    mem = kmalloc(size, priority);
    if (mem)
      return mem;
    atomic_sub(size, &sk->sk_omem_alloc);
  }
  return NULL;
}

[2] copy_from_user

msg_sys是保存用户态msghdr的内核变量,其msg_control_user 及为用户态代码设置的buf,此刻将用户态spray数据拷贝到内核态空间中

[3] sock_sendmsg

调用具体协议注册的发包函数来进行实际的发包,eg.udp_sendmsg,进一步的内部实现不必关注

[4] sock_kfree_s

释放开辟的内核空间,意味着无法持久化的保留内存,因此该方法需要配合userfaultfd来实现spray内存的持久化

[5] sysctl_optmem_max

#define UIO_MAXIOV    1024
/* Maximal space eaten by iovec or ancillary data plus some space */
int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
EXPORT_SYMBOL(sysctl_optmem_max);

该全局变量限制了默认的单次sendmsg的上限是0x5000,且单个sock可发送的上限也是0x5000