Re: m_pullup alingment crash armv7 sparc64

2022-02-04 Thread Alexander Bluhm
On Fri, Feb 04, 2022 at 07:27:38PM +1000, David Gwynne wrote:
> On Thu, Feb 03, 2022 at 11:39:50PM +0100, Alexander Bluhm wrote:
> > On Thu, Feb 03, 2022 at 10:22:46PM +1000, David Gwynne wrote:
> > > bpf should know better than this. it has all the information it needs to
> > > align the payload properly, it just doesnt make enough of an effort. can
> > > you try the diff below?
> > 
> > Diff seems to work.  regress/sbin/slaacd passes.
> > I have started a full regress run on armv7 and sparc64.

Regress passed.

> tun(4) uses EMSGSIZE for this, but errno(2) says EMSGSIZE means
> "Message too long". EINVAL sounds good.
> 
> want me to do it now?

As you move the code around, you can easily adopt the error code
now.

OK bluhm@



Re: m_pullup alingment crash armv7 sparc64

2022-02-04 Thread David Gwynne
On Thu, Feb 03, 2022 at 11:39:50PM +0100, Alexander Bluhm wrote:
> On Thu, Feb 03, 2022 at 10:22:46PM +1000, David Gwynne wrote:
> > bpf should know better than this. it has all the information it needs to
> > align the payload properly, it just doesnt make enough of an effort. can
> > you try the diff below?
> 
> Diff seems to work.  regress/sbin/slaacd passes.
> I have started a full regress run on armv7 and sparc64.

cool.

> 
> > +   if (len < hlen)
> > +   return (EPERM);
> 
> This is not a permission problem.  Maybe EINVAL.

this is preveserving the existing semantics of this chunk:

@@ -232,10 +247,6 @@ bpf_movein(struct uio *uio, struct bpf_d
goto bad;
}
 
-   if (m->m_len < hlen) {
-   error = EPERM;
-   goto bad;
-   }
/*
 * Make room for link header, and copy it to sockaddr
 */

i was going to follow this diff up with another one to tweak it.
tun(4) uses EMSGSIZE for this, but errno(2) says EMSGSIZE means
"Message too long". EINVAL sounds good.

want me to do it now?

> 
> > +   /*
> > + * Get the length of the payload so we can align it properly.
> > +*/
> 
> Whitespace misaligned.

yep.

> > +   if (mlen > MAXMCLBYTES)
> > +   return (EIO);
> 
> Should we use EMSGSIZE like in bpfwrite() if the data does not fit.

yes. want me to do that now too?

> 
> > +   mlen = max(max_linkhdr, hlen) + roundup(alen, sizeof(long));
> ...
> > +   m_align(m, alen); /* Align the payload. */
> 
> sizeof(long) seems the correct alignment for mbuf.  In rev 1.237
> of m_pullup() you introduced ALIGNBYTES.  This is architecture
> dependent and the only place in the network stack where it is used.
> Should it also be sizeof(long) there?

i probably did that when i was adding ALIGNED_POINTER checks to ethernet
tunnel drivers.

my initial thought is it doesnt save m_pullup any work, so it's
probably better to use sizeof(long) so there's a better chance that
accesses to words in a pulled up payload are aligned. even if an
arch doesnt fault on unaligned access it still goes faster with
aligned accesses. if m_pullup is going to move data around it may as
well move it to the best place possible.

i'll spit a diff out for that next.

Index: bpf.c
===
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.210
diff -u -p -r1.210 bpf.c
--- bpf.c   16 Jan 2022 06:27:14 -  1.210
+++ bpf.c   4 Feb 2022 09:25:40 -
@@ -144,7 +144,7 @@ bpf_movein(struct uio *uio, struct bpf_d
struct mbuf *m;
struct m_tag *mtag;
int error;
-   u_int hlen;
+   u_int hlen, alen, mlen;
u_int len;
u_int linktype;
u_int slen;
@@ -198,23 +198,38 @@ bpf_movein(struct uio *uio, struct bpf_d
return (EIO);
}
 
-   if (uio->uio_resid > MAXMCLBYTES)
-   return (EIO);
len = uio->uio_resid;
+   if (len < hlen)
+   return (EPERM);
 
-   MGETHDR(m, M_WAIT, MT_DATA);
-   m->m_pkthdr.ph_ifidx = 0;
-   m->m_pkthdr.len = len - hlen;
+   /*
+* Get the length of the payload so we can align it properly.
+*/
+   alen = len - hlen;
+
+   /*
+* Allocate enough space for headers and the aligned payload.
+*/
+   mlen = max(max_linkhdr, hlen) + roundup(alen, sizeof(long));
+
+   if (mlen > MAXMCLBYTES)
+   return (EIO);
 
-   if (len > MHLEN) {
-   MCLGETL(m, M_WAIT, len);
+   MGETHDR(m, M_WAIT, MT_DATA);
+   if (mlen > MHLEN) {
+   MCLGETL(m, M_WAIT, mlen);
if ((m->m_flags & M_EXT) == 0) {
error = ENOBUFS;
goto bad;
}
}
+
+   m_align(m, alen); /* Align the payload. */
+   m->m_data -= hlen;
+
+   m->m_pkthdr.ph_ifidx = 0;
+   m->m_pkthdr.len = len;
m->m_len = len;
-   *mp = m;
 
error = uiomove(mtod(m, caddr_t), len, uio);
if (error)
@@ -232,10 +247,6 @@ bpf_movein(struct uio *uio, struct bpf_d
goto bad;
}
 
-   if (m->m_len < hlen) {
-   error = EPERM;
-   goto bad;
-   }
/*
 * Make room for link header, and copy it to sockaddr
 */
@@ -249,8 +260,10 @@ bpf_movein(struct uio *uio, struct bpf_d
sockp->sa_family = ntohl(af);
} else
memcpy(sockp->sa_data, m->m_data, hlen);
+
+   m->m_pkthdr.len -= hlen;
m->m_len -= hlen;
-   m->m_data += hlen; /* XXX */
+   m->m_data += hlen;
}
 
/*
@@ -260,6 +273,7 @@ bpf_movein(struct uio *uio, struct bpf_d
*(u_int *)(mtag + 1) = linktype;
m_tag_prepend(m, mtag);
 
+   *mp = m;
return (0);
  bad:
m_freem(m);



Re: m_pullup alingment crash armv7 sparc64

2022-02-03 Thread Alexander Bluhm
On Thu, Feb 03, 2022 at 10:22:46PM +1000, David Gwynne wrote:
> bpf should know better than this. it has all the information it needs to
> align the payload properly, it just doesnt make enough of an effort. can
> you try the diff below?

Diff seems to work.  regress/sbin/slaacd passes.
I have started a full regress run on armv7 and sparc64.

> + if (len < hlen)
> + return (EPERM);

This is not a permission problem.  Maybe EINVAL.

> + /*
> + * Get the length of the payload so we can align it properly.
> +  */

Whitespace misaligned.

> + if (mlen > MAXMCLBYTES)
> + return (EIO);

Should we use EMSGSIZE like in bpfwrite() if the data does not fit.

> + mlen = max(max_linkhdr, hlen) + roundup(alen, sizeof(long));
...
> + m_align(m, alen); /* Align the payload. */

sizeof(long) seems the correct alignment for mbuf.  In rev 1.237
of m_pullup() you introduced ALIGNBYTES.  This is architecture
dependent and the only place in the network stack where it is used.
Should it also be sizeof(long) there?

bluhm



Re: m_pullup alingment crash armv7 sparc64

2022-02-03 Thread David Gwynne
On Wed, Feb 02, 2022 at 10:30:24PM +0100, Alexander Bluhm wrote:
> Hi,
> 
> With the new m_pullup() code, I see crashes on armv7 and sparc64.
> regress/sbin/slaacd sends some IPv6 ND6 packets over pair(4) from
> scapy.  It crashes after m_pullup() in ipv6_check().
> 
>  run-parse-ra 
> ifconfig pair1 destroy 2>/dev/null || true
> ifconfig pair2 destroy 2>/dev/null || true
> ifconfig pair1 rdomain 1 10.11.12.1/24 up
> ifconfig pair2 rdomain 1 10.11.12.2/24 up
> ifconfig pair1 rdomain 1 patch pair2
> ifconfig pair1 inet6 rdomain 1 eui64
> ifconfig pair2 inet6 rdomain 1 eui64
> ifconfig pair2 inet6 rdomain 1 autoconf
> route -nq -T 1 add -host default 10.11.12.1 -reject
> route -T1 exec python3 -B -u /usr/src/regress/sbin/slaacd/process_ra.py  
> pair1 pair2 /dev/slaacd.sock
> Timeout, server ot21 not responding.
> 
> panic: trap type 0x34 (mem address not aligned): pc=14a1a7c npc=14a1a80 
> pstate=99820006
> Stopped at  db_enter+0x8:   nop
> TIDPIDUID PRFLAGS PFLAGS  CPU  COMMAND
> *317234  41858  0 0x14000  0x2000  softnet
>  416946  63125  0 0x14000  0x2001  systq
> trap(40025e91650, 34, 14a1a7c, 99820006, 180, 3b9ac800) at trap+0x328
> Lslowtrap_reenter(40005090d00, 0, fffe, 1832866, 6000, 6) at 
> Lslowtrap_reenter+0xf8
> ipv6_check(40005364000, 40005090d00, 0, 2018000, e0018000, 1a) at 
> ipv6_check+0x21c
> ip6_input_if(40025e91a68, 40025e91a74, 29, 0, 40005364000, 3b9ac800) at 
> ip6_input_if+0x7c
> ipv6_input(40005364000, 198a2f0, 40005090d00, 1, e, 0) at ipv6_input+0x3c
> ether_input(40005364000, 40005090d00, 20, 1780210, 0, 1780210) at 
> ether_input+0x274
> if_input_process(40005364000, 40025e91ce0, 20, 1780210, 0, 6) at 
> if_input_process+0x44
> ifiq_process(40005364410, 40025e91dc8, 1cd6288, 1cc1840, 1c00, 6) at 
> ifiq_process+0x68
> taskq_thread(4000432e080, 40004300810, 1775908, 480, 180, 3b9ac800) at 
> taskq_thread+0x7c
> proc_trampoline(0, 0, 0, 0, 0, 0) at proc_trampoline+0x14
> 
> bpfwrite() calls bpf_movein() which fills the mbuf with these
> wonderful comments:
> 
> case DLT_EN10MB:
> sockp->sa_family = AF_UNSPEC;
> /* XXX Would MAXLINKHDR be better? */
> hlen = ETHER_HDR_LEN;
> ...
> m->m_data += hlen; /* XXX */
> 
> Note that ETHER_HDR_LEN is 14, so the mbuf is not 4 byte aligned.
> Next ether_encap() makes room for an aligned ehternet header.  This
> is 16 bytes and does not fit.  So we prepend a new empty mbuf.
> 
> m = m_prepend(m, ETHER_ALIGN + sizeof(eh), M_DONTWAIT);
> 
> When we reach ip6_input(), the ethernet header was added and removed.
> So we still have an empty mbuf in front of an mbuf with an unaligned
> IPv6 header.
> 
> New m_pullup() code preserves the alingment of the second mbuf data.
> During review we agreed that this might be a good idea.  Mbuf
> alingnmet is very fragile, but it works unless you change anything.
> So back to the old behavior, align the data at the beginning of the
> empty mbuf.
> 
> ok?

bpf should know better than this. it has all the information it needs to
align the payload properly, it just doesnt make enough of an effort. can
you try the diff below?

Index: bpf.c
===
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.210
diff -u -p -r1.210 bpf.c
--- bpf.c   16 Jan 2022 06:27:14 -  1.210
+++ bpf.c   3 Feb 2022 12:21:33 -
@@ -144,7 +144,7 @@ bpf_movein(struct uio *uio, struct bpf_d
struct mbuf *m;
struct m_tag *mtag;
int error;
-   u_int hlen;
+   u_int hlen, alen, mlen;
u_int len;
u_int linktype;
u_int slen;
@@ -198,23 +198,38 @@ bpf_movein(struct uio *uio, struct bpf_d
return (EIO);
}
 
-   if (uio->uio_resid > MAXMCLBYTES)
-   return (EIO);
len = uio->uio_resid;
+   if (len < hlen)
+   return (EPERM);
 
-   MGETHDR(m, M_WAIT, MT_DATA);
-   m->m_pkthdr.ph_ifidx = 0;
-   m->m_pkthdr.len = len - hlen;
+   /*
+ * Get the length of the payload so we can align it properly.
+*/
+   alen = len - hlen;
+
+   /*
+* Allocate enough space for headers and the aligned payload.
+*/
+   mlen = max(max_linkhdr, hlen) + roundup(alen, sizeof(long));
+
+   if (mlen > MAXMCLBYTES)
+   return (EIO);
 
-   if (len > MHLEN) {
-   MCLGETL(m, M_WAIT, len);
+   MGETHDR(m, M_WAIT, MT_DATA);
+   if (mlen > MHLEN) {
+   MCLGETL(m, M_WAIT, mlen);
if ((m->m_flags & M_EXT) == 0) {
error = ENOBUFS;
goto bad;
}
}
+
+   m_align(m, alen); /* Align the payload. */
+   m->m_data -= hlen;
+
+   m->m_pkthdr.ph_ifidx = 0;
+   m->m_pkthdr.len = len;
m->m_len = len;
-   *mp = m;
 
error = ui

m_pullup alingment crash armv7 sparc64

2022-02-02 Thread Alexander Bluhm
Hi,

With the new m_pullup() code, I see crashes on armv7 and sparc64.
regress/sbin/slaacd sends some IPv6 ND6 packets over pair(4) from
scapy.  It crashes after m_pullup() in ipv6_check().

 run-parse-ra 
ifconfig pair1 destroy 2>/dev/null || true
ifconfig pair2 destroy 2>/dev/null || true
ifconfig pair1 rdomain 1 10.11.12.1/24 up
ifconfig pair2 rdomain 1 10.11.12.2/24 up
ifconfig pair1 rdomain 1 patch pair2
ifconfig pair1 inet6 rdomain 1 eui64
ifconfig pair2 inet6 rdomain 1 eui64
ifconfig pair2 inet6 rdomain 1 autoconf
route -nq -T 1 add -host default 10.11.12.1 -reject
route -T1 exec python3 -B -u /usr/src/regress/sbin/slaacd/process_ra.py  pair1 
pair2 /dev/slaacd.sock
Timeout, server ot21 not responding.

panic: trap type 0x34 (mem address not aligned): pc=14a1a7c npc=14a1a80 
pstate=99820006
Stopped at  db_enter+0x8:   nop
TIDPIDUID PRFLAGS PFLAGS  CPU  COMMAND
*317234  41858  0 0x14000  0x2000  softnet
 416946  63125  0 0x14000  0x2001  systq
trap(40025e91650, 34, 14a1a7c, 99820006, 180, 3b9ac800) at trap+0x328
Lslowtrap_reenter(40005090d00, 0, fffe, 1832866, 6000, 6) at 
Lslowtrap_reenter+0xf8
ipv6_check(40005364000, 40005090d00, 0, 2018000, e0018000, 1a) at 
ipv6_check+0x21c
ip6_input_if(40025e91a68, 40025e91a74, 29, 0, 40005364000, 3b9ac800) at 
ip6_input_if+0x7c
ipv6_input(40005364000, 198a2f0, 40005090d00, 1, e, 0) at ipv6_input+0x3c
ether_input(40005364000, 40005090d00, 20, 1780210, 0, 1780210) at 
ether_input+0x274
if_input_process(40005364000, 40025e91ce0, 20, 1780210, 0, 6) at 
if_input_process+0x44
ifiq_process(40005364410, 40025e91dc8, 1cd6288, 1cc1840, 1c00, 6) at 
ifiq_process+0x68
taskq_thread(4000432e080, 40004300810, 1775908, 480, 180, 3b9ac800) at 
taskq_thread+0x7c
proc_trampoline(0, 0, 0, 0, 0, 0) at proc_trampoline+0x14

bpfwrite() calls bpf_movein() which fills the mbuf with these
wonderful comments:

case DLT_EN10MB:
sockp->sa_family = AF_UNSPEC;
/* XXX Would MAXLINKHDR be better? */
hlen = ETHER_HDR_LEN;
...
m->m_data += hlen; /* XXX */

Note that ETHER_HDR_LEN is 14, so the mbuf is not 4 byte aligned.
Next ether_encap() makes room for an aligned ehternet header.  This
is 16 bytes and does not fit.  So we prepend a new empty mbuf.

m = m_prepend(m, ETHER_ALIGN + sizeof(eh), M_DONTWAIT);

When we reach ip6_input(), the ethernet header was added and removed.
So we still have an empty mbuf in front of an mbuf with an unaligned
IPv6 header.

New m_pullup() code preserves the alingment of the second mbuf data.
During review we agreed that this might be a good idea.  Mbuf
alingnmet is very fragile, but it works unless you change anything.
So back to the old behavior, align the data at the beginning of the
empty mbuf.

ok?

bluhm

Index: kern/uipc_mbuf.c
===
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_mbuf.c,v
retrieving revision 1.280
diff -u -p -r1.280 uipc_mbuf.c
--- kern/uipc_mbuf.c18 Jan 2022 12:38:21 -  1.280
+++ kern/uipc_mbuf.c2 Feb 2022 20:02:50 -
@@ -955,7 +955,7 @@ m_pullup(struct mbuf *m0, int len)
if (!M_READONLY(m0) && len <= tail - head) {
/* we can copy everything into the first mbuf */
if (m0->m_len == 0) {
-   m0->m_data = head;
+   m0->m_data = M_DATABUF(m0);
} else if (len > tail - mtod(m0, caddr_t)) {
/* need to memmove to make space at the end */
memmove(head, mtod(m0, caddr_t), m0->m_len);