summaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2018-03-09 08:27:31 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2018-03-11 16:24:49 -0400
commitc8d70a700a5b486bfa8e5a7d33d805389f6e59f9 (patch)
treeb0f6d0d872b182ce70ce13a2709705bb7cb8ec38 /net/bridge
parentb1d0a5d0cba4597c0394997b2d5fced3e3841b4e (diff)
netfilter: bridge: ebt_among: add more missing match size checks
ebt_among is special, it has a dynamic match size and is exempt from the central size checks. commit c4585a2823edf ("bridge: ebt_among: add missing match size checks") added validation for pool size, but missed fact that the macros ebt_among_wh_src/dst can already return out-of-bound result because they do not check value of wh_src/dst_ofs (an offset) vs. the size of the match that userspace gave to us. v2: check that offset has correct alignment. Paolo Abeni points out that we should also check that src/dst wormhash arrays do not overlap, and src + length lines up with start of dst (or vice versa). v3: compact wormhash_sizes_valid() part NB: Fixes tag is intentionally wrong, this bug exists from day one when match was added for 2.6 kernel. Tag is there so stable maintainers will notice this one too. Tested with same rules from the earlier patch. Fixes: c4585a2823edf ("bridge: ebt_among: add missing match size checks") Reported-by: <syzbot+bdabab6f1983a03fc009@syzkaller.appspotmail.com> Signed-off-by: Florian Westphal <fw@strlen.de> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/netfilter/ebt_among.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
index c5afb4232ecb..620e54f08296 100644
--- a/net/bridge/netfilter/ebt_among.c
+++ b/net/bridge/netfilter/ebt_among.c
@@ -177,6 +177,28 @@ static bool poolsize_invalid(const struct ebt_mac_wormhash *w)
177 return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple)); 177 return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple));
178} 178}
179 179
180static bool wormhash_offset_invalid(int off, unsigned int len)
181{
182 if (off == 0) /* not present */
183 return false;
184
185 if (off < (int)sizeof(struct ebt_among_info) ||
186 off % __alignof__(struct ebt_mac_wormhash))
187 return true;
188
189 off += sizeof(struct ebt_mac_wormhash);
190
191 return off > len;
192}
193
194static bool wormhash_sizes_valid(const struct ebt_mac_wormhash *wh, int a, int b)
195{
196 if (a == 0)
197 a = sizeof(struct ebt_among_info);
198
199 return ebt_mac_wormhash_size(wh) + a == b;
200}
201
180static int ebt_among_mt_check(const struct xt_mtchk_param *par) 202static int ebt_among_mt_check(const struct xt_mtchk_param *par)
181{ 203{
182 const struct ebt_among_info *info = par->matchinfo; 204 const struct ebt_among_info *info = par->matchinfo;
@@ -189,6 +211,10 @@ static int ebt_among_mt_check(const struct xt_mtchk_param *par)
189 if (expected_length > em->match_size) 211 if (expected_length > em->match_size)
190 return -EINVAL; 212 return -EINVAL;
191 213
214 if (wormhash_offset_invalid(info->wh_dst_ofs, em->match_size) ||
215 wormhash_offset_invalid(info->wh_src_ofs, em->match_size))
216 return -EINVAL;
217
192 wh_dst = ebt_among_wh_dst(info); 218 wh_dst = ebt_among_wh_dst(info);
193 if (poolsize_invalid(wh_dst)) 219 if (poolsize_invalid(wh_dst))
194 return -EINVAL; 220 return -EINVAL;
@@ -201,6 +227,14 @@ static int ebt_among_mt_check(const struct xt_mtchk_param *par)
201 if (poolsize_invalid(wh_src)) 227 if (poolsize_invalid(wh_src))
202 return -EINVAL; 228 return -EINVAL;
203 229
230 if (info->wh_src_ofs < info->wh_dst_ofs) {
231 if (!wormhash_sizes_valid(wh_src, info->wh_src_ofs, info->wh_dst_ofs))
232 return -EINVAL;
233 } else {
234 if (!wormhash_sizes_valid(wh_dst, info->wh_dst_ofs, info->wh_src_ofs))
235 return -EINVAL;
236 }
237
204 expected_length += ebt_mac_wormhash_size(wh_src); 238 expected_length += ebt_mac_wormhash_size(wh_src);
205 239
206 if (em->match_size != EBT_ALIGN(expected_length)) { 240 if (em->match_size != EBT_ALIGN(expected_length)) {