From: Sasha Levin Date: Tue, 24 Aug 2021 16:37:56 +0000 (-0400) Subject: Fixes for 4.14 X-Git-Tag: v5.13.13~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f4bf415f13a445506f88af0ef46c799af60b3819;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 4.14 Signed-off-by: Sasha Levin --- diff --git a/queue-4.14/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch b/queue-4.14/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch new file mode 100644 index 00000000000..71a72e86ea4 --- /dev/null +++ b/queue-4.14/netfilter-nft_exthdr-fix-endianness-of-tcp-option-ca.patch @@ -0,0 +1,127 @@ +From 4b68ba37a6419d3cae86d4af2f33fd900e09ee09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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( 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 +Acked-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 a301d3bbd3fa..e73a1503e8d7 100644 +--- a/net/netfilter/nft_exthdr.c ++++ b/net/netfilter/nft_exthdr.c +@@ -138,7 +138,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) +@@ -147,7 +146,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; +@@ -168,13 +166,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( ++ ®s->data[priv->sreg]); + + switch (priv->type) { + case TCPOPT_MSS: +@@ -192,7 +190,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 + diff --git a/queue-4.14/series b/queue-4.14/series index 5a95d496cb1..88f0060219a 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -60,3 +60,4 @@ btrfs-prevent-rename2-from-exchanging-a-subvol-with-.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