Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nft_set_bitmap: fetch the element key based on the set->klen

Currently we just assume the element key as a u32 integer, regardless of
the set key length.

This is incorrect, for example, the tcp port number is only 16 bits.
So when we use the nft_payload expr to get the tcp dport and store
it to dreg, the dport will be stored at 0~15 bits, and 16~31 bits
will be padded with zero.

So the reg->data[dreg] will be looked like as below:
0 15 31
+-+-+-+-+-+-+-+-+-+-+-+-+
| tcp dport | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+
But for these big-endian systems, if we treate this register as a u32
integer, the element key will be larger than 65535, so the following
lookup in bitmap set will cause out of bound access.

Another issue is that if we add element with comments in bitmap
set(although the comments will be ignored eventually), the element will
vanish strangely. Because we treate the element key as a u32 integer, so
the comments will become the part of the element key, then the element
key will also be larger than 65535 and out of bound access will happen:
# nft add element t s { 1 comment test }

Since set->klen is 1 or 2, it's fine to treate the element key as a u8 or
u16 integer.

Fixes: 665153ff5752 ("netfilter: nf_tables: add bitmap set type")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Liping Zhang and committed by
Pablo Neira Ayuso
fd89b23a 568af6de

+17 -10
+17 -10
net/netfilter/nft_set_bitmap.c
··· 45 45 u8 bitmap[]; 46 46 }; 47 47 48 - static inline void nft_bitmap_location(u32 key, u32 *idx, u32 *off) 48 + static inline void nft_bitmap_location(const struct nft_set *set, 49 + const void *key, 50 + u32 *idx, u32 *off) 49 51 { 50 - u32 k = (key << 1); 52 + u32 k; 53 + 54 + if (set->klen == 2) 55 + k = *(u16 *)key; 56 + else 57 + k = *(u8 *)key; 58 + k <<= 1; 51 59 52 60 *idx = k / BITS_PER_BYTE; 53 61 *off = k % BITS_PER_BYTE; ··· 77 69 u8 genmask = nft_genmask_cur(net); 78 70 u32 idx, off; 79 71 80 - nft_bitmap_location(*key, &idx, &off); 72 + nft_bitmap_location(set, key, &idx, &off); 81 73 82 74 return nft_bitmap_active(priv->bitmap, idx, off, genmask); 83 75 } ··· 91 83 u8 genmask = nft_genmask_next(net); 92 84 u32 idx, off; 93 85 94 - nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off); 86 + nft_bitmap_location(set, nft_set_ext_key(ext), &idx, &off); 95 87 if (nft_bitmap_active(priv->bitmap, idx, off, genmask)) 96 88 return -EEXIST; 97 89 ··· 110 102 u8 genmask = nft_genmask_next(net); 111 103 u32 idx, off; 112 104 113 - nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off); 105 + nft_bitmap_location(set, nft_set_ext_key(ext), &idx, &off); 114 106 /* Enter 00 state. */ 115 107 priv->bitmap[idx] &= ~(genmask << off); 116 108 } ··· 124 116 u8 genmask = nft_genmask_next(net); 125 117 u32 idx, off; 126 118 127 - nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off); 119 + nft_bitmap_location(set, nft_set_ext_key(ext), &idx, &off); 128 120 /* Enter 11 state. */ 129 121 priv->bitmap[idx] |= (genmask << off); 130 122 } ··· 136 128 u8 genmask = nft_genmask_next(net); 137 129 u32 idx, off; 138 130 139 - nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off); 131 + nft_bitmap_location(set, nft_set_ext_key(ext), &idx, &off); 140 132 /* Enter 10 state, similar to deactivation. */ 141 133 priv->bitmap[idx] &= ~(genmask << off); 142 134 ··· 169 161 struct nft_bitmap *priv = nft_set_priv(set); 170 162 u8 genmask = nft_genmask_next(net); 171 163 struct nft_set_ext *ext; 172 - u32 idx, off, key = 0; 164 + u32 idx, off; 173 165 174 - memcpy(&key, elem->key.val.data, set->klen); 175 - nft_bitmap_location(key, &idx, &off); 166 + nft_bitmap_location(set, elem->key.val.data, &idx, &off); 176 167 177 168 if (!nft_bitmap_active(priv->bitmap, idx, off, genmask)) 178 169 return NULL;