Very old FreeBSD mbuf exhaustion...
This Linux exploit reminds me a bug in FreeBSD reported by me 4 years ago.
The bug resides in the IPV6_PKTOPTIONS setsockopt() code located in ip6_output.c:
soopt_getm() is called with user supplied sopt defined like this.
Notice that in our case sopt_td field is not NULL. As you can see below, depending on the user supplied sopt_valsize, soopt_getm() will try to allocate a mbuf and a cluster of mbuf if sopt_valsize is greater than MLEN (~256). If there is not enough mbuf avalaible, soopt_getm() will wait infinetely until mbufs are released because sopt_td is not NULL.
If you have noticed the possible integer overflow on sopt_size, stay seated because it is already checked by the callers.
To trigger this mbuf exhaution you can use this poc.
(Try an ifconfig iface down up as root and system will now be completely frozen. I guess the iface releases its mbufs when it goes down but cannot allocate new ones when it goes up because our poc has already eaten all of them, gniark)
This bug is quite funny and dangerous because it does not cause a system crash followed by a reboot like a simple kernel panic but it consumes infinitely all the available system mbufs and cause serious networking troubles. :-)
The bug resides in the IPV6_PKTOPTIONS setsockopt() code located in ip6_output.c:
int ip6_ctloutput(struct socket *so, struct sockopt *sopt)
{
(...)
switch (optname) {
case IPV6_2292PKTOPTIONS:
#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
#endif
{
struct mbuf *m;
error = soopt_getm(sopt, &m); /* XXX */
if (error != 0)
break;
(...)
}
soopt_getm() is called with user supplied sopt defined like this.
struct sockopt {
enum sopt_dir sopt_dir; /* is this a get or a set? */
int sopt_level; /* second arg of [gs]etsockopt */
int sopt_name; /* third arg of [gs]etsockopt */
void *sopt_val; /* fourth arg of [gs]etsockopt */
size_t sopt_valsize; /* (almost) fifth arg of [gs]etsockopt */
struct thread *sopt_td; /* calling thread or null if kernel */
};
Notice that in our case sopt_td field is not NULL. As you can see below, depending on the user supplied sopt_valsize, soopt_getm() will try to allocate a mbuf and a cluster of mbuf if sopt_valsize is greater than MLEN (~256). If there is not enough mbuf avalaible, soopt_getm() will wait infinetely until mbufs are released because sopt_td is not NULL.
int soopt_getm(struct sockopt *sopt, struct mbuf **mp)
{
struct mbuf *m, *m_prev;
int sopt_size = sopt->sopt_valsize;
MGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT, MT_DATA);
if (m == NULL)
return ENOBUFS;
if (sopt_size > MLEN) {
MCLGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT);
(...)
}
If you have noticed the possible integer overflow on sopt_size, stay seated because it is already checked by the callers.
To trigger this mbuf exhaution you can use this poc.
$ uname -v
FreeBSD 8.1-RELEASE
$ netstat -m
258/267/525 mbufs in use (current/cache/total)
256/134/390/4672 mbuf clusters in use (current/cache/total/max)
(...)
$ ./foo&
(...)
$ netstat -m
4547/193/4740 mbufs in use (current/cache/total)
4544/128/4672/4672 mbuf clusters in use (current/cache/total/max)
(Try an ifconfig iface down up as root and system will now be completely frozen. I guess the iface releases its mbufs when it goes down but cannot allocate new ones when it goes up because our poc has already eaten all of them, gniark)
This bug is quite funny and dangerous because it does not cause a system crash followed by a reboot like a simple kernel panic but it consumes infinitely all the available system mbufs and cause serious networking troubles. :-)
0 Comments:
Post a Comment
<< Home