]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 4.19
authorSasha Levin <sashal@kernel.org>
Tue, 24 Aug 2021 16:37:55 +0000 (12:37 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 24 Aug 2021 16:37:55 +0000 (12:37 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.19/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch [new file with mode: 0644]
queue-4.19/series

diff --git a/queue-4.19/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch b/queue-4.19/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch
new file mode 100644 (file)
index 0000000..c988776
--- /dev/null
@@ -0,0 +1,127 @@
+From 96d7f18cb6113426236d3a6b0c25c4c5aa9fbe05 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 29 Mar 2020 19:19:14 +0700
+Subject: netfilter: nft_exthdr: fix endianness of tcp option cast
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sergey Marinkevich <sergey.marinkevich@eltex-co.ru>
+
+[ Upstream commit 2e34328b396a69b73661ba38d47d92b7cf21c2c4 ]
+
+I got a problem on MIPS with Big-Endian is turned on: every time when
+NF trying to change TCP MSS it returns because of new.v16 was greater
+than old.v16. But real MSS was 1460 and my rule was like this:
+
+       add rule table chain tcp option maxseg size set 1400
+
+And 1400 is lesser that 1460, not greater.
+
+Later I founded that main causer is cast from u32 to __be16.
+
+Debugging:
+
+In example MSS = 1400(HEX: 0x578). Here is representation of each byte
+like it is in memory by addresses from left to right(e.g. [0x0 0x1 0x2
+0x3]). LE — Little-Endian system, BE — Big-Endian, left column is type.
+
+            LE               BE
+       u32: [78 05 00 00]    [00 00 05 78]
+
+As you can see, u32 representation will be casted to u16 from different
+half of 4-byte address range. But actually nf_tables uses registers and
+store data of various size. Actually TCP MSS stored in 2 bytes. But
+registers are still u32 in definition:
+
+       struct nft_regs {
+               union {
+                       u32                     data[20];
+                       struct nft_verdict      verdict;
+               };
+       };
+
+So, access like regs->data[priv->sreg] exactly u32. So, according to
+table presents above, per-byte representation of stored TCP MSS in
+register will be:
+
+                            LE               BE
+       (u32)regs->data[]:   [78 05 00 00]    [05 78 00 00]
+                                              ^^ ^^
+
+We see that register uses just half of u32 and other 2 bytes may be
+used for some another data. But in nft_exthdr_tcp_set_eval() it casted
+just like u32 -> __be16:
+
+       new.v16 = src
+
+But u32 overfill __be16, so it get 2 low bytes. For clarity draw
+one more table(<xx xx> means that bytes will be used for cast).
+
+                            LE                 BE
+       u32:                 [<78 05> 00 00]    [00 00 <05 78>]
+       (u32)regs->data[]:   [<78 05> 00 00]    [05 78 <00 00>]
+
+As you can see, for Little-Endian nothing changes, but for Big-endian we
+take the wrong half. In my case there is some other data instead of
+zeros, so new MSS was wrongly greater.
+
+For shooting this bug I used solution for ports ranges. Applying of this
+patch does not affect Little-Endian systems.
+
+Signed-off-by: Sergey Marinkevich <sergey.marinkevich@eltex-co.ru>
+Acked-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_exthdr.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
+index 64e69d6683ca..93fee4106019 100644
+--- a/net/netfilter/nft_exthdr.c
++++ b/net/netfilter/nft_exthdr.c
+@@ -137,7 +137,6 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
+       unsigned int i, optl, tcphdr_len, offset;
+       struct tcphdr *tcph;
+       u8 *opt;
+-      u32 src;
+       tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
+       if (!tcph)
+@@ -146,7 +145,6 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
+       opt = (u8 *)tcph;
+       for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
+               union {
+-                      u8 octet;
+                       __be16 v16;
+                       __be32 v32;
+               } old, new;
+@@ -167,13 +165,13 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
+               if (!tcph)
+                       return;
+-              src = regs->data[priv->sreg];
+               offset = i + priv->offset;
+               switch (priv->len) {
+               case 2:
+                       old.v16 = get_unaligned((u16 *)(opt + offset));
+-                      new.v16 = src;
++                      new.v16 = (__force __be16)nft_reg_load16(
++                              &regs->data[priv->sreg]);
+                       switch (priv->type) {
+                       case TCPOPT_MSS:
+@@ -191,7 +189,7 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
+                                                old.v16, new.v16, false);
+                       break;
+               case 4:
+-                      new.v32 = src;
++                      new.v32 = regs->data[priv->sreg];
+                       old.v32 = get_unaligned((u32 *)(opt + offset));
+                       if (old.v32 == new.v32)
+-- 
+2.30.2
+
index 103932a530316ffa0cd3a7e9d0397dbc3fc706f0..91a881238ab2414a6c76a64ccd8089bb2fae4e54 100644 (file)
@@ -80,3 +80,4 @@ pci-increase-d3-delay-for-amd-renoir-cezanne-xhci.patch
 asoc-intel-atom-fix-breakage-for-pcm-buffer-address-.patch
 locks-print-a-warning-when-mount-fails-due-to-lack-o.patch
 fs-warn-about-impending-deprecation-of-mandatory-loc.patch
+netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch