Skip to content

Commit 405c005

Browse files
nealcardwelldavem330
authored andcommitted
inet_diag: validate byte code to prevent oops in inet_diag_bc_run()
Add logic to validate INET_DIAG_BC_S_COND and INET_DIAG_BC_D_COND operations. Previously we did not validate the inet_diag_hostcond, address family, address length, and prefix length. So a malicious user could make the kernel read beyond the end of the bytecode array by claiming to have a whole inet_diag_hostcond when the bytecode was not long enough to contain a whole inet_diag_hostcond of the given address family. Or they could make the kernel read up to about 27 bytes beyond the end of a connection address by passing a prefix length that exceeded the length of addresses of the given family. Signed-off-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1c95df8 commit 405c005

File tree

1 file changed

+45
-3
lines changed

1 file changed

+45
-3
lines changed

net/ipv4/inet_diag.c

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,25 +513,67 @@ static int valid_cc(const void *bc, int len, int cc)
513513
return 0;
514514
}
515515

516+
/* Validate an inet_diag_hostcond. */
517+
static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
518+
int *min_len)
519+
{
520+
int addr_len;
521+
struct inet_diag_hostcond *cond;
522+
523+
/* Check hostcond space. */
524+
*min_len += sizeof(struct inet_diag_hostcond);
525+
if (len < *min_len)
526+
return false;
527+
cond = (struct inet_diag_hostcond *)(op + 1);
528+
529+
/* Check address family and address length. */
530+
switch (cond->family) {
531+
case AF_UNSPEC:
532+
addr_len = 0;
533+
break;
534+
case AF_INET:
535+
addr_len = sizeof(struct in_addr);
536+
break;
537+
case AF_INET6:
538+
addr_len = sizeof(struct in6_addr);
539+
break;
540+
default:
541+
return false;
542+
}
543+
*min_len += addr_len;
544+
if (len < *min_len)
545+
return false;
546+
547+
/* Check prefix length (in bits) vs address length (in bytes). */
548+
if (cond->prefix_len > 8 * addr_len)
549+
return false;
550+
551+
return true;
552+
}
553+
516554
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
517555
{
518556
const void *bc = bytecode;
519557
int len = bytecode_len;
520558

521559
while (len > 0) {
522560
const struct inet_diag_bc_op *op = bc;
561+
int min_len = sizeof(struct inet_diag_bc_op);
523562

524563
//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
525564
switch (op->code) {
526-
case INET_DIAG_BC_AUTO:
527565
case INET_DIAG_BC_S_COND:
528566
case INET_DIAG_BC_D_COND:
567+
if (!valid_hostcond(bc, len, &min_len))
568+
return -EINVAL;
569+
/* fall through */
570+
case INET_DIAG_BC_AUTO:
529571
case INET_DIAG_BC_S_GE:
530572
case INET_DIAG_BC_S_LE:
531573
case INET_DIAG_BC_D_GE:
532574
case INET_DIAG_BC_D_LE:
533575
case INET_DIAG_BC_JMP:
534-
if (op->no < 4 || op->no > len + 4 || op->no & 3)
576+
if (op->no < min_len || op->no > len + 4 || op->no & 3)
535577
return -EINVAL;
536578
if (op->no < len &&
537579
!valid_cc(bytecode, bytecode_len, len - op->no))
@@ -542,7 +584,7 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
542584
default:
543585
return -EINVAL;
544586
}
545-
if (op->yes < 4 || op->yes > len + 4 || op->yes & 3)
587+
if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
546588
return -EINVAL;
547589
bc += op->yes;
548590
len -= op->yes;

0 commit comments

Comments
 (0)