Home | History | Annotate | Download | only in netinet
History log of /src/sys/netinet/ip_reass.c
RevisionDateAuthorComments
 1.23  31-May-2022  andvar fix various typos in comments, documentation and messages.
 1.22  16-Feb-2022  andvar fix various typos, mainly in comments.
 1.21  12-Oct-2018  maxv Force ip_off to zero when the reassembly is complete. This was lost in my
rev1.19 - before that the IP struct was clobbered for the reassembly, but
it actually implicitly guaranteed that the first fragment of the packet
would end up with ip_off = 0, and this was a desired behavior.
 1.20  17-Sep-2018  maxv Kick fragments that would introduce several !MFFs in a reassembly chain.

The problem arises if we receive three fragments of the kind

3. A -> has MFF
1. B -> doesn't have MFF
2. C -> doesn't have MFF

Because of the received order B->C->A, we don't see that B is !MFF, and
therefore that there is a problem in this chain.

Now we do two checks, and drop us if:

* there is a fragment preceding us, and this fragment is !MFF, or
* there is a fragment following us, and we are !MFF

Spotted a long time ago.
 1.19  17-Sep-2018  maxv Hold ip_off and ip_len in the fragment entry, instead of always reading
the associated mbuf (and converting to host order). This reduces the
cache/TLB misses when processing long lists.
 1.18  10-Jul-2018  maxv Remove the second argument from ip_reass_packet(). We want the IP header
on the mbuf, not elsewhere. Simplifies the NPF reassembly code a little.
No real functional change.
 1.17  15-May-2018  maxv branches: 1.17.2;
When reassembling IPv4/IPv6 packets, ensure each fragment has been subject
to the same IPsec processing. That is to say, that all fragments are ESP,
or AH, or AH+ESP, or none.

The reassembly mechanism can be used both on the wire and inside an IPsec
tunnel, so we need to make sure all fragments of a packet were received
on only one side.

Even though I haven't tried, I believe there are configurations where it
would be possible for an attacker to inject an unencrypted fragment into a
legitimate stream of already-decrypted-and-authenticated fragments.

Typically on IPsec gateways with ESP tunnels, where we can encapsulate
fragments (as opposed to the general case, where we fragment encapsulated
data).

Note, for the record: a funnier thing, under IPv4, would be to send a
zero-sized !MFF fragment at the head of the packet, and manage to trigger
an ICMP error; M_DECRYPTED gets lost by the reassembly, and ICMP will reply
with the packet in clear (not encrypted).
 1.16  03-May-2018  maxv Rename m_pkthdr_remove -> m_remove_pkthdr, to match the existing naming
convention, eg m_copy_pkthdr and m_move_pkthdr.
 1.15  11-Apr-2018  maxv Add 'static', like the prototype.
 1.14  09-Mar-2018  maxv Remove M_PKTHDR from secondary mbufs when reassembling packets.

This is a real problem, because I found at least one component that relies
on the fact that only the first mbuf has M_PKTHDR: far from here, in
m_splithdr, we don't update m->m_pkthdr.len if M_PKTHDR is found in a
secondary mbuf. (The initial intention there was to avoid updating
m_pkthdr.len twice, the assumption was that if M_PKTHDR is set then we're
dealing with the first mbuf.) Therefore, when handling fragmented IPsec
packets (in particular IPv6, IPv4 is a bit more complicated), we may end
up with an incorrect m_pkthdr.len after authentication or decryption. In
the case of ESP, this can lead to a remote crash on this instruction:

m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);

m_pkthdr.len is bigger than the actual mbuf chain.

It seems possible to me to trigger this bug even if you don't have the ESP
key, because the fragmentation part is outside of the encrypted ESP
payload.

So if you MITM the target, and intercept an incoming ESP packet (which you
can't decrypt), you should be able to forge a new specially-crafted,
fragmented packet and stuff the ESP payload (still encrypted, as you
intercepted it) into it. The decryption succeeds and the target crashes.
 1.13  08-Feb-2018  maxv branches: 1.13.2;
Change the error stat from IP_STAT_BADFRAGS to IP_STAT_TOOLONG. The
ping_of_death ATF test expects this counter to get increased.
 1.12  06-Feb-2018  maxv Add one more check in ip_reass_packet(): make sure that the end of each
fragment does not exceed IP_MAXPACKET.

In ip_reass(), we only check the final length of the reassembled packet
against IP_MAXPACKET.

But there is an integer overflow that can happen a little earlier. We
are doing:

i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) -
ntohs(ip->ip_off);
[...]
ip->ip_off = htons(ntohs(ip->ip_off) + i);

It is possible that

ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) > 65535

so the computation of ip_off wraps to zero. This breaks an assumption in
the reassembler - it expects the list of fragments to be ordered by
offset, and here it's not ordered anymore. (Un)Fortunately I couldn't
turn this into anything exploitable.

With the new check, it is guaranteed that ip_off+ip_len<=65535.
 1.11  11-Jan-2017  ozaki-r branches: 1.11.8;
Get rid of unnecessary header inclusions
 1.10  26-Apr-2016  ozaki-r branches: 1.10.2;
Sweep unnecessary route.h inclusions
 1.9  25-Feb-2014  pooka branches: 1.9.4; 1.9.6; 1.9.8; 1.9.12;
Ensure that the top level sysctl nodes (kern, vfs, net, ...) exist before
the sysctl link sets are processed, and remove redundancy.

Shaves >13kB off of an amd64 GENERIC, not to mention >1k duplicate
lines of code.
 1.8  27-Jun-2011  enami branches: 1.8.2; 1.8.12; 1.8.16;
Don't increment ip_nfragpackets when failed to allocate fragment queue.
No one will decrement it on such case.
 1.7  05-Nov-2010  rmind branches: 1.7.6;
ip_reass_packet: finish abstraction; some clean-up.
Discussed some time ago with matt@.
 1.6  07-Oct-2010  yamt make ipfr_lock IPL_VM as ip_reass_drain is called in interrupts via
the drain hook for mbuf pools.
 1.5  06-Oct-2010  enami Don't free memory still in use. Fixes nfs root problem reported
by Christoph Egger on source-changes-d.
 1.4  03-Oct-2010  rmind Re-structure IPv4 reassembly code to make it more MP-friendly and simplify
some code fragments while here. Also, use pool_cache(9) and mutex(9).

IPv4 reassembly mechanism is MP-safe now.
 1.3  25-Aug-2010  rmind Use own IPv4 reassembly queue entry structure and leave struct ipqent only
for TCP. Now both struct ipfr_qent, struct ipfr_queue and hashed fragment
queue are abstracted and no longer public.
 1.2  19-Jul-2010  rmind branches: 1.2.2; 1.2.4;
Abstract IP reassembly into single generic routine - ip_reass_packet().
Make struct ipq private and struct ipqent not visible to userland.
Push ip_len adjustment into reassembly layer.

OK matt@
 1.1  13-Jul-2010  rmind Split-off IPv4 re-assembly mechanism into a separate module. Abstract
into ip_reass_init(), ip_reass_lookup(), etc (note: abstraction is not
yet complete). No functional changes to the actual mechanism.

OK matt@
 1.2.4.4  06-Nov-2010  uebayasi Sync with HEAD.
 1.2.4.3  22-Oct-2010  uebayasi Sync with HEAD (-D20101022).
 1.2.4.2  17-Aug-2010  uebayasi Sync with HEAD.
 1.2.4.1  19-Jul-2010  uebayasi file ip_reass.c was added on branch uebayasi-xip on 2010-08-17 06:47:46 +0000
 1.2.2.3  09-Oct-2010  yamt sync with head
 1.2.2.2  11-Aug-2010  yamt sync with head.
 1.2.2.1  19-Jul-2010  yamt file ip_reass.c was added on branch yamt-nfs-mp on 2010-08-11 22:54:56 +0000
 1.7.6.2  05-Mar-2011  rmind sync with head
 1.7.6.1  05-Nov-2010  rmind file ip_reass.c was added on branch rmind-uvmplock on 2011-03-05 20:55:58 +0000
 1.8.16.1  18-May-2014  rmind sync with head
 1.8.12.2  03-Dec-2017  jdolecek update from HEAD
 1.8.12.1  20-Aug-2014  tls Rebase to HEAD as of a few days ago.
 1.8.2.1  22-May-2014  yamt sync with head.

for a reference, the tree before this commit was tagged
as yamt-pagecache-tag8.

this commit was splitted into small chunks to avoid
a limitation of cvs. ("Protocol error: too many arguments")
 1.9.12.1  05-Apr-2018  martin Pull up following revision(s) (requested by maxv in ticket #1594):

sys/kern/uipc_mbuf.c: revision 1.182
sys/netinet6/frag6.c: revision 1.67
sys/netinet/ip_reass.c: revision 1.14
sys/sys/mbuf.h: revision 1.179

Remove M_PKTHDR from secondary mbufs when reassembling packets.

This is a real problem, because I found at least one component that relies
on the fact that only the first mbuf has M_PKTHDR: far from here, in
m_splithdr, we don't update m->m_pkthdr.len if M_PKTHDR is found in a
secondary mbuf. (The initial intention there was to avoid updating
m_pkthdr.len twice, the assumption was that if M_PKTHDR is set then we're
dealing with the first mbuf.) Therefore, when handling fragmented IPsec
packets (in particular IPv6, IPv4 is a bit more complicated), we may end
up with an incorrect m_pkthdr.len after authentication or decryption. In
the case of ESP, this can lead to a remote crash on this instruction:
m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
m_pkthdr.len is bigger than the actual mbuf chain.

It seems possible to me to trigger this bug even if you don't have the ESP
key, because the fragmentation part is outside of the encrypted ESP
payload.

So if you MITM the target, and intercept an incoming ESP packet (which you
can't decrypt), you should be able to forge a new specially-crafted,
fragmented packet and stuff the ESP payload (still encrypted, as you
intercepted it) into it. The decryption succeeds and the target crashes.
 1.9.8.1  05-Apr-2018  martin Pull up following revision(s) (requested by maxv in ticket #1594):

sys/kern/uipc_mbuf.c: revision 1.182
sys/netinet6/frag6.c: revision 1.67
sys/netinet/ip_reass.c: revision 1.14
sys/sys/mbuf.h: revision 1.179

Remove M_PKTHDR from secondary mbufs when reassembling packets.

This is a real problem, because I found at least one component that relies
on the fact that only the first mbuf has M_PKTHDR: far from here, in
m_splithdr, we don't update m->m_pkthdr.len if M_PKTHDR is found in a
secondary mbuf. (The initial intention there was to avoid updating
m_pkthdr.len twice, the assumption was that if M_PKTHDR is set then we're
dealing with the first mbuf.) Therefore, when handling fragmented IPsec
packets (in particular IPv6, IPv4 is a bit more complicated), we may end
up with an incorrect m_pkthdr.len after authentication or decryption. In
the case of ESP, this can lead to a remote crash on this instruction:
m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
m_pkthdr.len is bigger than the actual mbuf chain.

It seems possible to me to trigger this bug even if you don't have the ESP
key, because the fragmentation part is outside of the encrypted ESP
payload.

So if you MITM the target, and intercept an incoming ESP packet (which you
can't decrypt), you should be able to forge a new specially-crafted,
fragmented packet and stuff the ESP payload (still encrypted, as you
intercepted it) into it. The decryption succeeds and the target crashes.
 1.9.6.2  05-Feb-2017  skrll Sync with HEAD
 1.9.6.1  29-May-2016  skrll Sync with HEAD
 1.9.4.1  05-Apr-2018  martin Pull up following revision(s) (requested by maxv in ticket #1594):

sys/kern/uipc_mbuf.c: revision 1.182
sys/netinet6/frag6.c: revision 1.67
sys/netinet/ip_reass.c: revision 1.14
sys/sys/mbuf.h: revision 1.179

Remove M_PKTHDR from secondary mbufs when reassembling packets.

This is a real problem, because I found at least one component that relies
on the fact that only the first mbuf has M_PKTHDR: far from here, in
m_splithdr, we don't update m->m_pkthdr.len if M_PKTHDR is found in a
secondary mbuf. (The initial intention there was to avoid updating
m_pkthdr.len twice, the assumption was that if M_PKTHDR is set then we're
dealing with the first mbuf.) Therefore, when handling fragmented IPsec
packets (in particular IPv6, IPv4 is a bit more complicated), we may end
up with an incorrect m_pkthdr.len after authentication or decryption. In
the case of ESP, this can lead to a remote crash on this instruction:
m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
m_pkthdr.len is bigger than the actual mbuf chain.

It seems possible to me to trigger this bug even if you don't have the ESP
key, because the fragmentation part is outside of the encrypted ESP
payload.

So if you MITM the target, and intercept an incoming ESP packet (which you
can't decrypt), you should be able to forge a new specially-crafted,
fragmented packet and stuff the ESP payload (still encrypted, as you
intercepted it) into it. The decryption succeeds and the target crashes.
 1.10.2.1  20-Mar-2017  pgoyette Sync with HEAD
 1.11.8.7  17-Oct-2018  martin Pull up following revision(s) (requested by maxv in ticket #1045):

sys/netinet/ip_reass.c: revision 1.19-1.21

Hold ip_off and ip_len in the fragment entry, instead of always reading
the associated mbuf (and converting to host order). This reduces the
cache/TLB misses when processing long lists.

-

Kick fragments that would introduce several !MFFs in a reassembly chain.

The problem arises if we receive three fragments of the kind
3. A -> has MFF
1. B -> doesn't have MFF
2. C -> doesn't have MFF

Because of the received order B->C->A, we don't see that B is !MFF, and
therefore that there is a problem in this chain.

Now we do two checks, and drop us if:

* there is a fragment preceding us, and this fragment is !MFF, or
* there is a fragment following us, and we are !MFF

Spotted a long time ago.

-

Force ip_off to zero when the reassembly is complete. This was lost in my
rev1.19 - before that the IP struct was clobbered for the reassembly, but
it actually implicitly guaranteed that the first fragment of the packet
would end up with ip_off = 0, and this was a desired behavior.
 1.11.8.6  09-Oct-2018  martin Back out the following from ticket #1045 by maxv:

sys/netinet/ip_reass.c 1.19

Faster IPv4 packet reassembly - causes fallout, needs further investigation
(see PR kern/53664)
 1.11.8.5  03-Oct-2018  martin Pull up following revision(s) (requested by maxv in ticket #1045):

sys/netinet/ip_reass.c: revision 1.19

Hold ip_off and ip_len in the fragment entry, instead of always reading
the associated mbuf (and converting to host order). This reduces the
cache/TLB misses when processing long lists.
 1.11.8.4  27-Sep-2018  martin Pull up following revision(s) (requested by maxv in ticket #1041):

sys/netinet/ip_reass.c: revision 1.17 (patch)
sys/netinet6/frag6.c: revision 1.74 (patch)

When reassembling IPv4/IPv6 packets, ensure each fragment has been subject
to the same IPsec processing. That is to say, that all fragments are ESP,
or AH, or AH+ESP, or none.

The reassembly mechanism can be used both on the wire and inside an IPsec
tunnel, so we need to make sure all fragments of a packet were received
on only one side.

Even though I haven't tried, I believe there are configurations where it
would be possible for an attacker to inject an unencrypted fragment into a
legitimate stream of already-decrypted-and-authenticated fragments.

Typically on IPsec gateways with ESP tunnels, where we can encapsulate
fragments (as opposed to the general case, where we fragment encapsulated
data).

Note, for the record: a funnier thing, under IPv4, would be to send a
zero-sized !MFF fragment at the head of the packet, and manage to trigger
an ICMP error; M_DECRYPTED gets lost by the reassembly, and ICMP will reply
with the packet in clear (not encrypted).
 1.11.8.3  09-Apr-2018  martin Additionally pull up the following revision for ticket #668,
requested by ozaki-r:

sys/netinet/ip_reass.c 1.13

Change the error stat from IP_STAT_BADFRAGS to IP_STAT_TOOLONG. The
ping_of_death ATF test expects this counter to get increased.
 1.11.8.2  05-Apr-2018  martin Pull up following revision(s) (requested by maxv in ticket #695):

sys/kern/uipc_mbuf.c: revision 1.182
sys/netinet6/frag6.c: revision 1.67
sys/netinet/ip_reass.c: revision 1.14
sys/sys/mbuf.h: revision 1.179

Remove M_PKTHDR from secondary mbufs when reassembling packets.

This is a real problem, because I found at least one component that relies
on the fact that only the first mbuf has M_PKTHDR: far from here, in
m_splithdr, we don't update m->m_pkthdr.len if M_PKTHDR is found in a
secondary mbuf. (The initial intention there was to avoid updating
m_pkthdr.len twice, the assumption was that if M_PKTHDR is set then we're
dealing with the first mbuf.) Therefore, when handling fragmented IPsec
packets (in particular IPv6, IPv4 is a bit more complicated), we may end
up with an incorrect m_pkthdr.len after authentication or decryption. In
the case of ESP, this can lead to a remote crash on this instruction:
m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
m_pkthdr.len is bigger than the actual mbuf chain.

It seems possible to me to trigger this bug even if you don't have the ESP
key, because the fragmentation part is outside of the encrypted ESP
payload.

So if you MITM the target, and intercept an incoming ESP packet (which you
can't decrypt), you should be able to forge a new specially-crafted,
fragmented packet and stuff the ESP payload (still encrypted, as you
intercepted it) into it. The decryption succeeds and the target crashes.
 1.11.8.1  30-Mar-2018  martin Pull up following revision(s) (requested by maxv in ticket #668):
sys/netinet/ip_reass.c: revision 1.12

Add one more check in ip_reass_packet(): make sure that the end of each
fragment does not exceed IP_MAXPACKET.

In ip_reass(), we only check the final length of the reassembled packet
against IP_MAXPACKET.

But there is an integer overflow that can happen a little earlier. We
are doing:

i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) -
ntohs(ip->ip_off);
[...]
ip->ip_off = htons(ntohs(ip->ip_off) + i);

It is possible that

ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) > 65535

so the computation of ip_off wraps to zero. This breaks an assumption in
the reassembler - it expects the list of fragments to be ordered by
offset, and here it's not ordered anymore. (Un)Fortunately I couldn't
turn this into anything exploitable.

With the new check, it is guaranteed that ip_off+ip_len<=65535.
 1.13.2.6  20-Oct-2018  pgoyette Sync with head
 1.13.2.5  30-Sep-2018  pgoyette Ssync with HEAD
 1.13.2.4  28-Jul-2018  pgoyette Sync with HEAD
 1.13.2.3  21-May-2018  pgoyette Sync with HEAD
 1.13.2.2  16-Apr-2018  pgoyette Sync with HEAD, resolve some conflicts
 1.13.2.1  15-Mar-2018  pgoyette Synch with HEAD
 1.17.2.1  10-Jun-2019  christos Sync with HEAD

RSS XML Feed