]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/netfilter/x_tables.c
netfilter: x_tables: validate all offsets and sizes in a rule
[karo-tx-linux.git] / net / netfilter / x_tables.c
index 7b46dd6a20a5981c25b46eaae710490773dc8798..3d2cdcd50832895ffdfae3bf87d7cdda8259aae0 100644 (file)
@@ -415,6 +415,47 @@ int xt_check_match(struct xt_mtchk_param *par,
 }
 EXPORT_SYMBOL_GPL(xt_check_match);
 
+/** xt_check_entry_match - check that matches end before start of target
+ *
+ * @match: beginning of xt_entry_match
+ * @target: beginning of this rules target (alleged end of matches)
+ * @alignment: alignment requirement of match structures
+ *
+ * Validates that all matches add up to the beginning of the target,
+ * and that each match covers at least the base structure size.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int xt_check_entry_match(const char *match, const char *target,
+                               const size_t alignment)
+{
+       const struct xt_entry_match *pos;
+       int length = target - match;
+
+       if (length == 0) /* no matches */
+               return 0;
+
+       pos = (struct xt_entry_match *)match;
+       do {
+               if ((unsigned long)pos % alignment)
+                       return -EINVAL;
+
+               if (length < (int)sizeof(struct xt_entry_match))
+                       return -EINVAL;
+
+               if (pos->u.match_size < sizeof(struct xt_entry_match))
+                       return -EINVAL;
+
+               if (pos->u.match_size > length)
+                       return -EINVAL;
+
+               length -= pos->u.match_size;
+               pos = ((void *)((char *)(pos) + (pos)->u.match_size));
+       } while (length > 0);
+
+       return 0;
+}
+
 #ifdef CONFIG_COMPAT
 int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta)
 {
@@ -570,7 +611,14 @@ int xt_compat_check_entry_offsets(const void *base, const char *elems,
            target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
                return -EINVAL;
 
-       return 0;
+       /* compat_xt_entry match has less strict aligment requirements,
+        * otherwise they are identical.  In case of padding differences
+        * we need to add compat version of xt_check_entry_match.
+        */
+       BUILD_BUG_ON(sizeof(struct compat_xt_entry_match) != sizeof(struct xt_entry_match));
+
+       return xt_check_entry_match(elems, base + target_offset,
+                                   __alignof__(struct compat_xt_entry_match));
 }
 EXPORT_SYMBOL(xt_compat_check_entry_offsets);
 #endif /* CONFIG_COMPAT */
@@ -583,17 +631,39 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
  * @target_offset: the arp/ip/ip6_t->target_offset
  * @next_offset: the arp/ip/ip6_t->next_offset
  *
- * validates that target_offset and next_offset are sane.
- * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
+ * validates that target_offset and next_offset are sane and that all
+ * match sizes (if any) align with the target offset.
  *
  * This function does not validate the targets or matches themselves, it
- * only tests that all the offsets and sizes are correct.
+ * only tests that all the offsets and sizes are correct, that all
+ * match structures are aligned, and that the last structure ends where
+ * the target structure begins.
+ *
+ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
  *
  * The arp/ip/ip6t_entry structure @base must have passed following tests:
  * - it must point to a valid memory location
  * - base to base + next_offset must be accessible, i.e. not exceed allocated
  *   length.
  *
+ * A well-formed entry looks like this:
+ *
+ * ip(6)t_entry   match [mtdata]  match [mtdata] target [tgdata] ip(6)t_entry
+ * e->elems[]-----'                              |               |
+ *                matchsize                      |               |
+ *                                matchsize      |               |
+ *                                               |               |
+ * target_offset---------------------------------'               |
+ * next_offset---------------------------------------------------'
+ *
+ * elems[]: flexible array member at end of ip(6)/arpt_entry struct.
+ *          This is where matches (if any) and the target reside.
+ * target_offset: beginning of target.
+ * next_offset: start of the next rule; also: size of this rule.
+ * Since targets have a minimum size, target_offset + minlen <= next_offset.
+ *
+ * Every match stores its size, sum of sizes must not exceed target_offset.
+ *
  * Return: 0 on success, negative errno on failure.
  */
 int xt_check_entry_offsets(const void *base,
@@ -623,7 +693,8 @@ int xt_check_entry_offsets(const void *base,
            target_offset + sizeof(struct xt_standard_target) != next_offset)
                return -EINVAL;
 
-       return 0;
+       return xt_check_entry_match(elems, base + target_offset,
+                                   __alignof__(struct xt_entry_match));
 }
 EXPORT_SYMBOL(xt_check_entry_offsets);