Skip to content

Commit 0d7df90

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: x_tables: ensure last rule in base chain matches underflow/policy
Harmless from kernel point of view, but again iptables assumes that this is true when decoding ruleset coming from kernel. If a (syzkaller generated) ruleset doesn't have the underflow/policy stored as the last rule in the base chain, then iptables will abort() because it doesn't find the chain policy. libiptc assumes that the policy is the last rule in the basechain, which is only true for iptables-generated rulesets. Unfortunately this needs code duplication -- the functions need the struct layout of the rule head, but that is different for ip/ip6/arptables. NB: pr_warn could be pr_debug but in case this break rulesets somehow its useful to know why blob was rejected. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 8937086 commit 0d7df90

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

net/ipv4/netfilter/arp_tables.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,13 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
309309
for (hook = 0; hook < NF_ARP_NUMHOOKS; hook++) {
310310
unsigned int pos = newinfo->hook_entry[hook];
311311
struct arpt_entry *e = entry0 + pos;
312+
unsigned int last_pos, depth;
312313

313314
if (!(valid_hooks & (1 << hook)))
314315
continue;
315316

317+
depth = 0;
318+
last_pos = pos;
316319
/* Set initial back pointer. */
317320
e->counters.pcnt = pos;
318321

@@ -343,6 +346,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
343346
pos = e->counters.pcnt;
344347
e->counters.pcnt = 0;
345348

349+
if (depth)
350+
--depth;
346351
/* We're at the start. */
347352
if (pos == oldpos)
348353
goto next;
@@ -367,6 +372,9 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
367372
if (!xt_find_jump_offset(offsets, newpos,
368373
newinfo->number))
369374
return 0;
375+
376+
if (entry0 + newpos != arpt_next_entry(e))
377+
++depth;
370378
} else {
371379
/* ... this is a fallthru */
372380
newpos = pos + e->next_offset;
@@ -377,8 +385,15 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
377385
e->counters.pcnt = pos;
378386
pos = newpos;
379387
}
388+
if (depth == 0)
389+
last_pos = pos;
390+
}
391+
next:
392+
if (last_pos != newinfo->underflow[hook]) {
393+
pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
394+
last_pos, newinfo->underflow[hook], hook);
395+
return 0;
380396
}
381-
next: ;
382397
}
383398
return 1;
384399
}

net/ipv4/netfilter/ip_tables.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,13 @@ mark_source_chains(const struct xt_table_info *newinfo,
378378
for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
379379
unsigned int pos = newinfo->hook_entry[hook];
380380
struct ipt_entry *e = entry0 + pos;
381+
unsigned int last_pos, depth;
381382

382383
if (!(valid_hooks & (1 << hook)))
383384
continue;
384385

386+
depth = 0;
387+
last_pos = pos;
385388
/* Set initial back pointer. */
386389
e->counters.pcnt = pos;
387390

@@ -410,6 +413,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
410413
pos = e->counters.pcnt;
411414
e->counters.pcnt = 0;
412415

416+
if (depth)
417+
--depth;
413418
/* We're at the start. */
414419
if (pos == oldpos)
415420
goto next;
@@ -434,6 +439,9 @@ mark_source_chains(const struct xt_table_info *newinfo,
434439
if (!xt_find_jump_offset(offsets, newpos,
435440
newinfo->number))
436441
return 0;
442+
443+
if (entry0 + newpos != ipt_next_entry(e))
444+
++depth;
437445
} else {
438446
/* ... this is a fallthru */
439447
newpos = pos + e->next_offset;
@@ -444,8 +452,15 @@ mark_source_chains(const struct xt_table_info *newinfo,
444452
e->counters.pcnt = pos;
445453
pos = newpos;
446454
}
455+
if (depth == 0)
456+
last_pos = pos;
457+
}
458+
next:
459+
if (last_pos != newinfo->underflow[hook]) {
460+
pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
461+
last_pos, newinfo->underflow[hook], hook);
462+
return 0;
447463
}
448-
next: ;
449464
}
450465
return 1;
451466
}

net/ipv6/netfilter/ip6_tables.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,13 @@ mark_source_chains(const struct xt_table_info *newinfo,
396396
for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
397397
unsigned int pos = newinfo->hook_entry[hook];
398398
struct ip6t_entry *e = entry0 + pos;
399+
unsigned int last_pos, depth;
399400

400401
if (!(valid_hooks & (1 << hook)))
401402
continue;
402403

404+
depth = 0;
405+
last_pos = pos;
403406
/* Set initial back pointer. */
404407
e->counters.pcnt = pos;
405408

@@ -428,6 +431,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
428431
pos = e->counters.pcnt;
429432
e->counters.pcnt = 0;
430433

434+
if (depth)
435+
--depth;
431436
/* We're at the start. */
432437
if (pos == oldpos)
433438
goto next;
@@ -452,6 +457,9 @@ mark_source_chains(const struct xt_table_info *newinfo,
452457
if (!xt_find_jump_offset(offsets, newpos,
453458
newinfo->number))
454459
return 0;
460+
461+
if (entry0 + newpos != ip6t_next_entry(e))
462+
++depth;
455463
} else {
456464
/* ... this is a fallthru */
457465
newpos = pos + e->next_offset;
@@ -462,8 +470,15 @@ mark_source_chains(const struct xt_table_info *newinfo,
462470
e->counters.pcnt = pos;
463471
pos = newpos;
464472
}
473+
if (depth == 0)
474+
last_pos = pos;
475+
}
476+
next:
477+
if (last_pos != newinfo->underflow[hook]) {
478+
pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
479+
last_pos, newinfo->underflow[hook], hook);
480+
return 0;
465481
}
466-
next: ;
467482
}
468483
return 1;
469484
}

0 commit comments

Comments
 (0)