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

netfilter: nft_set_rbtree: Detect partial overlap with start endpoint match

Getting creative with nft and omitting the interval_overlap()
check from the set_overlap() function, without omitting
set_overlap() altogether, led to the observation of a partial
overlap that wasn't detected, and would actually result in
replacement of the end element of an existing interval.

This is due to the fact that we'll return -EEXIST on a matching,
pre-existing start element, instead of -ENOTEMPTY, and the error
is cleared by API if NLM_F_EXCL is not given. At this point, we
can insert a matching start, and duplicate the end element as long
as we don't end up into other intervals.

For instance, inserting interval 0 - 2 with an existing 0 - 3
interval would result in a single 0 - 2 interval, and a dangling
'3' end element. This is because nft will proceed after inserting
the '0' start element as no error is reported, and no further
conflicting intervals are detected on insertion of the end element.

This needs a different approach as it's a local condition that can
be detected by looking for duplicate ends coming from left and
right, separately. Track those and directly report -ENOTEMPTY on
duplicated end elements for a matching start.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Stefano Brivio and committed by
Pablo Neira Ayuso
07267630 226a88de

+33 -1
+33 -1
net/netfilter/nft_set_rbtree.c
··· 218 218 struct nft_rbtree_elem *new, 219 219 struct nft_set_ext **ext) 220 220 { 221 + bool overlap = false, dup_end_left = false, dup_end_right = false; 221 222 struct nft_rbtree *priv = nft_set_priv(set); 222 223 u8 genmask = nft_genmask_next(net); 223 224 struct nft_rbtree_elem *rbe; 224 225 struct rb_node *parent, **p; 225 - bool overlap = false; 226 226 int d; 227 227 228 228 /* Detect overlaps as we descend the tree. Set the flag in these cases: ··· 262 262 * 263 263 * which always happen as last step and imply that no further 264 264 * overlapping is possible. 265 + * 266 + * Another special case comes from the fact that start elements matching 267 + * an already existing start element are allowed: insertion is not 268 + * performed but we return -EEXIST in that case, and the error will be 269 + * cleared by the caller if NLM_F_EXCL is not present in the request. 270 + * This way, request for insertion of an exact overlap isn't reported as 271 + * error to userspace if not desired. 272 + * 273 + * However, if the existing start matches a pre-existing start, but the 274 + * end element doesn't match the corresponding pre-existing end element, 275 + * we need to report a partial overlap. This is a local condition that 276 + * can be noticed without need for a tracking flag, by checking for a 277 + * local duplicated end for a corresponding start, from left and right, 278 + * separately. 265 279 */ 266 280 267 281 parent = NULL; ··· 295 281 !nft_set_elem_expired(&rbe->ext) && !*p) 296 282 overlap = false; 297 283 } else { 284 + if (dup_end_left && !*p) 285 + return -ENOTEMPTY; 286 + 298 287 overlap = nft_rbtree_interval_end(rbe) && 299 288 nft_set_elem_active(&rbe->ext, 300 289 genmask) && 301 290 !nft_set_elem_expired(&rbe->ext); 291 + 292 + if (overlap) { 293 + dup_end_right = true; 294 + continue; 295 + } 302 296 } 303 297 } else if (d > 0) { 304 298 p = &parent->rb_right; 305 299 306 300 if (nft_rbtree_interval_end(new)) { 301 + if (dup_end_right && !*p) 302 + return -ENOTEMPTY; 303 + 307 304 overlap = nft_rbtree_interval_end(rbe) && 308 305 nft_set_elem_active(&rbe->ext, 309 306 genmask) && 310 307 !nft_set_elem_expired(&rbe->ext); 308 + 309 + if (overlap) { 310 + dup_end_left = true; 311 + continue; 312 + } 311 313 } else if (nft_set_elem_active(&rbe->ext, genmask) && 312 314 !nft_set_elem_expired(&rbe->ext)) { 313 315 overlap = nft_rbtree_interval_end(rbe); ··· 351 321 p = &parent->rb_left; 352 322 } 353 323 } 324 + 325 + dup_end_left = dup_end_right = false; 354 326 } 355 327 356 328 if (overlap)