Skip to content

Commit caa39a1

Browse files
Florian Westphalgregkh
authored andcommitted
netfilter: x_tables: validate all offsets and sizes in a rule
commit 13631bfc604161a9d69cd68991dff8603edd66f9 upstream. Validate that all matches (if any) add up to the beginning of the target and that each match covers at least the base structure size. The compat path should be able to safely re-use the function as the structures only differ in alignment; added a BUILD_BUG_ON just in case we have an arch that adds padding as well. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8a86562 commit caa39a1

1 file changed

Lines changed: 76 additions & 5 deletions

File tree

net/netfilter/x_tables.c

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,47 @@ int xt_check_match(struct xt_mtchk_param *par,
415415
}
416416
EXPORT_SYMBOL_GPL(xt_check_match);
417417

418+
/** xt_check_entry_match - check that matches end before start of target
419+
*
420+
* @match: beginning of xt_entry_match
421+
* @target: beginning of this rules target (alleged end of matches)
422+
* @alignment: alignment requirement of match structures
423+
*
424+
* Validates that all matches add up to the beginning of the target,
425+
* and that each match covers at least the base structure size.
426+
*
427+
* Return: 0 on success, negative errno on failure.
428+
*/
429+
static int xt_check_entry_match(const char *match, const char *target,
430+
const size_t alignment)
431+
{
432+
const struct xt_entry_match *pos;
433+
int length = target - match;
434+
435+
if (length == 0) /* no matches */
436+
return 0;
437+
438+
pos = (struct xt_entry_match *)match;
439+
do {
440+
if ((unsigned long)pos % alignment)
441+
return -EINVAL;
442+
443+
if (length < (int)sizeof(struct xt_entry_match))
444+
return -EINVAL;
445+
446+
if (pos->u.match_size < sizeof(struct xt_entry_match))
447+
return -EINVAL;
448+
449+
if (pos->u.match_size > length)
450+
return -EINVAL;
451+
452+
length -= pos->u.match_size;
453+
pos = ((void *)((char *)(pos) + (pos)->u.match_size));
454+
} while (length > 0);
455+
456+
return 0;
457+
}
458+
418459
#ifdef CONFIG_COMPAT
419460
int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta)
420461
{
@@ -570,7 +611,14 @@ int xt_compat_check_entry_offsets(const void *base, const char *elems,
570611
target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
571612
return -EINVAL;
572613

573-
return 0;
614+
/* compat_xt_entry match has less strict aligment requirements,
615+
* otherwise they are identical. In case of padding differences
616+
* we need to add compat version of xt_check_entry_match.
617+
*/
618+
BUILD_BUG_ON(sizeof(struct compat_xt_entry_match) != sizeof(struct xt_entry_match));
619+
620+
return xt_check_entry_match(elems, base + target_offset,
621+
__alignof__(struct compat_xt_entry_match));
574622
}
575623
EXPORT_SYMBOL(xt_compat_check_entry_offsets);
576624
#endif /* CONFIG_COMPAT */
@@ -583,17 +631,39 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
583631
* @target_offset: the arp/ip/ip6_t->target_offset
584632
* @next_offset: the arp/ip/ip6_t->next_offset
585633
*
586-
* validates that target_offset and next_offset are sane.
587-
* Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
634+
* validates that target_offset and next_offset are sane and that all
635+
* match sizes (if any) align with the target offset.
588636
*
589637
* This function does not validate the targets or matches themselves, it
590-
* only tests that all the offsets and sizes are correct.
638+
* only tests that all the offsets and sizes are correct, that all
639+
* match structures are aligned, and that the last structure ends where
640+
* the target structure begins.
641+
*
642+
* Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
591643
*
592644
* The arp/ip/ip6t_entry structure @base must have passed following tests:
593645
* - it must point to a valid memory location
594646
* - base to base + next_offset must be accessible, i.e. not exceed allocated
595647
* length.
596648
*
649+
* A well-formed entry looks like this:
650+
*
651+
* ip(6)t_entry match [mtdata] match [mtdata] target [tgdata] ip(6)t_entry
652+
* e->elems[]-----' | |
653+
* matchsize | |
654+
* matchsize | |
655+
* | |
656+
* target_offset---------------------------------' |
657+
* next_offset---------------------------------------------------'
658+
*
659+
* elems[]: flexible array member at end of ip(6)/arpt_entry struct.
660+
* This is where matches (if any) and the target reside.
661+
* target_offset: beginning of target.
662+
* next_offset: start of the next rule; also: size of this rule.
663+
* Since targets have a minimum size, target_offset + minlen <= next_offset.
664+
*
665+
* Every match stores its size, sum of sizes must not exceed target_offset.
666+
*
597667
* Return: 0 on success, negative errno on failure.
598668
*/
599669
int xt_check_entry_offsets(const void *base,
@@ -623,7 +693,8 @@ int xt_check_entry_offsets(const void *base,
623693
target_offset + sizeof(struct xt_standard_target) != next_offset)
624694
return -EINVAL;
625695

626-
return 0;
696+
return xt_check_entry_match(elems, base + target_offset,
697+
__alignof__(struct xt_entry_match));
627698
}
628699
EXPORT_SYMBOL(xt_check_entry_offsets);
629700

0 commit comments

Comments
 (0)