Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10596608 authored by Liping Zhang's avatar Liping Zhang Committed by Pablo Neira Ayuso
Browse files

netfilter: nf_tables: fix mismatch in big-endian system



Currently, there are two different methods to store an u16 integer to
the u32 data register. For example:
  u32 *dest = &regs->data[priv->dreg];
  1. *dest = 0; *(u16 *) dest = val_u16;
  2. *dest = val_u16;

For method 1, the u16 value will be stored like this, either in
big-endian or little-endian system:
  0          15           31
  +-+-+-+-+-+-+-+-+-+-+-+-+
  |   Value   |     0     |
  +-+-+-+-+-+-+-+-+-+-+-+-+

For method 2, in little-endian system, the u16 value will be the same
as listed above. But in big-endian system, the u16 value will be stored
like this:
  0          15           31
  +-+-+-+-+-+-+-+-+-+-+-+-+
  |     0     |   Value   |
  +-+-+-+-+-+-+-+-+-+-+-+-+

So later we use "memcmp(&regs->data[priv->sreg], data, 2);" to do
compare in nft_cmp, nft_lookup expr ..., method 2 will get the wrong
result in big-endian system, as 0~15 bits will always be zero.

For the similar reason, when loading an u16 value from the u32 data
register, we should use "*(u16 *) sreg;" instead of "(u16)*sreg;",
the 2nd method will get the wrong value in the big-endian system.

So introduce some wrapper functions to store/load an u8 or u16
integer to/from the u32 data register, and use them in the right
place.

Signed-off-by: default avatarLiping Zhang <zlpnobody@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent fd89b23a
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -103,6 +103,35 @@ struct nft_regs {
	};
};

/* Store/load an u16 or u8 integer to/from the u32 data register.
 *
 * Note, when using concatenations, register allocation happens at 32-bit
 * level. So for store instruction, pad the rest part with zero to avoid
 * garbage values.
 */

static inline void nft_reg_store16(u32 *dreg, u16 val)
{
	*dreg = 0;
	*(u16 *)dreg = val;
}

static inline void nft_reg_store8(u32 *dreg, u8 val)
{
	*dreg = 0;
	*(u8 *)dreg = val;
}

static inline u16 nft_reg_load16(u32 *sreg)
{
	return *(u16 *)sreg;
}

static inline u8 nft_reg_load8(u32 *sreg)
{
	return *(u8 *)sreg;
}

static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
				 unsigned int len)
{
+4 −4
Original line number Diff line number Diff line
@@ -26,10 +26,10 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
	memset(&range, 0, sizeof(range));
	range.flags = priv->flags;
	if (priv->sreg_proto_min) {
		range.min_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_min];
		range.max_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_max];
		range.min_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_min]);
		range.max_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_max]);
	}
	regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, nft_hook(pkt),
						    &range, nft_out(pkt));
+4 −4
Original line number Diff line number Diff line
@@ -26,10 +26,10 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr,

	memset(&mr, 0, sizeof(mr));
	if (priv->sreg_proto_min) {
		mr.range[0].min.all =
			*(__be16 *)&regs->data[priv->sreg_proto_min];
		mr.range[0].max.all =
			*(__be16 *)&regs->data[priv->sreg_proto_max];
		mr.range[0].min.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_min]);
		mr.range[0].max.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_max]);
		mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
	}

+4 −4
Original line number Diff line number Diff line
@@ -27,10 +27,10 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
	memset(&range, 0, sizeof(range));
	range.flags = priv->flags;
	if (priv->sreg_proto_min) {
		range.min_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_min];
		range.max_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_max];
		range.min_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_min]);
		range.max_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_max]);
	}
	regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range,
						    nft_out(pkt));
+4 −4
Original line number Diff line number Diff line
@@ -26,10 +26,10 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr,

	memset(&range, 0, sizeof(range));
	if (priv->sreg_proto_min) {
		range.min_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_min],
		range.max_proto.all =
			*(__be16 *)&regs->data[priv->sreg_proto_max],
		range.min_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_min]);
		range.max_proto.all = (__force __be16)nft_reg_load16(
			&regs->data[priv->sreg_proto_max]);
		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
	}

Loading