Skip to content

Commit 8a86562

Browse files
Florian Westphalgregkh
authored andcommitted
netfilter: x_tables: check for bogus target offset
commit ce683e5f9d045e5d67d1312a42b359cb2ab2a13c upstream. We're currently asserting that targetoff + targetsize <= nextoff. Extend it to also check that targetoff is >= sizeof(xt_entry). Since this is generic code, add an argument pointing to the start of the match/target, we can then derive the base structure size from the delta. We also need the e->elems pointer in a followup change to validate matches. 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 2066499 commit 8a86562

5 files changed

Lines changed: 26 additions & 10 deletions

File tree

include/linux/netfilter/x_tables.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ void xt_unregister_match(struct xt_match *target);
239239
int xt_register_matches(struct xt_match *match, unsigned int n);
240240
void xt_unregister_matches(struct xt_match *match, unsigned int n);
241241

242-
int xt_check_entry_offsets(const void *base,
242+
int xt_check_entry_offsets(const void *base, const char *elems,
243243
unsigned int target_offset,
244244
unsigned int next_offset);
245245

@@ -492,7 +492,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
492492
unsigned int *size);
493493
int xt_compat_target_to_user(const struct xt_entry_target *t,
494494
void __user **dstptr, unsigned int *size);
495-
int xt_compat_check_entry_offsets(const void *base,
495+
int xt_compat_check_entry_offsets(const void *base, const char *elems,
496496
unsigned int target_offset,
497497
unsigned int next_offset);
498498

net/ipv4/netfilter/arp_tables.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,8 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
592592
if (!arp_checkentry(&e->arp))
593593
return -EINVAL;
594594

595-
err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
595+
err = xt_check_entry_offsets(e, e->elems, e->target_offset,
596+
e->next_offset);
596597
if (err)
597598
return err;
598599

@@ -1253,7 +1254,7 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
12531254
if (!arp_checkentry(&e->arp))
12541255
return -EINVAL;
12551256

1256-
ret = xt_compat_check_entry_offsets(e, e->target_offset,
1257+
ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
12571258
e->next_offset);
12581259
if (ret)
12591260
return ret;

net/ipv4/netfilter/ip_tables.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,8 @@ check_entry_size_and_hooks(struct ipt_entry *e,
754754
if (!ip_checkentry(&e->ip))
755755
return -EINVAL;
756756

757-
err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
757+
err = xt_check_entry_offsets(e, e->elems, e->target_offset,
758+
e->next_offset);
758759
if (err)
759760
return err;
760761

@@ -1512,7 +1513,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
15121513
if (!ip_checkentry(&e->ip))
15131514
return -EINVAL;
15141515

1515-
ret = xt_compat_check_entry_offsets(e,
1516+
ret = xt_compat_check_entry_offsets(e, e->elems,
15161517
e->target_offset, e->next_offset);
15171518
if (ret)
15181519
return ret;

net/ipv6/netfilter/ip6_tables.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,8 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
766766
if (!ip6_checkentry(&e->ipv6))
767767
return -EINVAL;
768768

769-
err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
769+
err = xt_check_entry_offsets(e, e->elems, e->target_offset,
770+
e->next_offset);
770771
if (err)
771772
return err;
772773

@@ -1524,7 +1525,7 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
15241525
if (!ip6_checkentry(&e->ipv6))
15251526
return -EINVAL;
15261527

1527-
ret = xt_compat_check_entry_offsets(e,
1528+
ret = xt_compat_check_entry_offsets(e, e->elems,
15281529
e->target_offset, e->next_offset);
15291530
if (ret)
15301531
return ret;

net/netfilter/x_tables.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,14 +545,17 @@ struct compat_xt_standard_target {
545545
compat_uint_t verdict;
546546
};
547547

548-
/* see xt_check_entry_offsets */
549-
int xt_compat_check_entry_offsets(const void *base,
548+
int xt_compat_check_entry_offsets(const void *base, const char *elems,
550549
unsigned int target_offset,
551550
unsigned int next_offset)
552551
{
552+
long size_of_base_struct = elems - (const char *)base;
553553
const struct compat_xt_entry_target *t;
554554
const char *e = base;
555555

556+
if (target_offset < size_of_base_struct)
557+
return -EINVAL;
558+
556559
if (target_offset + sizeof(*t) > next_offset)
557560
return -EINVAL;
558561

@@ -576,12 +579,16 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
576579
* xt_check_entry_offsets - validate arp/ip/ip6t_entry
577580
*
578581
* @base: pointer to arp/ip/ip6t_entry
582+
* @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
579583
* @target_offset: the arp/ip/ip6_t->target_offset
580584
* @next_offset: the arp/ip/ip6_t->next_offset
581585
*
582586
* validates that target_offset and next_offset are sane.
583587
* Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
584588
*
589+
* This function does not validate the targets or matches themselves, it
590+
* only tests that all the offsets and sizes are correct.
591+
*
585592
* The arp/ip/ip6t_entry structure @base must have passed following tests:
586593
* - it must point to a valid memory location
587594
* - base to base + next_offset must be accessible, i.e. not exceed allocated
@@ -590,12 +597,18 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
590597
* Return: 0 on success, negative errno on failure.
591598
*/
592599
int xt_check_entry_offsets(const void *base,
600+
const char *elems,
593601
unsigned int target_offset,
594602
unsigned int next_offset)
595603
{
604+
long size_of_base_struct = elems - (const char *)base;
596605
const struct xt_entry_target *t;
597606
const char *e = base;
598607

608+
/* target start is within the ip/ip6/arpt_entry struct */
609+
if (target_offset < size_of_base_struct)
610+
return -EINVAL;
611+
599612
if (target_offset + sizeof(*t) > next_offset)
600613
return -EINVAL;
601614

0 commit comments

Comments
 (0)