aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/inet_diag.c
diff options
context:
space:
mode:
authorNeal Cardwell <ncardwell@google.com>2012-12-09 06:09:54 -0500
committerDavid S. Miller <davem@davemloft.net>2012-12-09 19:00:48 -0500
commit5e1f54201cb481f40a04bc47e1bc8c093a189e23 (patch)
treef5fa04ab4f52467197692f6ddf3cb56d5908595e /net/ipv4/inet_diag.c
parentf67caec9068cee426ec23cf9005a1dee2ecad187 (diff)
inet_diag: validate port comparison byte code to prevent unsafe reads
Add logic to verify that a port comparison byte code operation actually has the second inet_diag_bc_op from which we read the port for such operations. Previously the code blindly referenced op[1] without first checking whether a second inet_diag_bc_op struct could fit there. So a malicious user could make the kernel read 4 bytes beyond the end of the bytecode array by claiming to have a whole port comparison byte code (2 inet_diag_bc_op structs) when in fact the bytecode was not long enough to hold both. Signed-off-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/inet_diag.c')
-rw-r--r--net/ipv4/inet_diag.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 95f1a458371d..e23e16dc501d 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -557,6 +557,17 @@ static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
557 return true; 557 return true;
558} 558}
559 559
560/* Validate a port comparison operator. */
561static inline bool valid_port_comparison(const struct inet_diag_bc_op *op,
562 int len, int *min_len)
563{
564 /* Port comparisons put the port in a follow-on inet_diag_bc_op. */
565 *min_len += sizeof(struct inet_diag_bc_op);
566 if (len < *min_len)
567 return false;
568 return true;
569}
570
560static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) 571static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
561{ 572{
562 const void *bc = bytecode; 573 const void *bc = bytecode;
@@ -572,24 +583,30 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
572 case INET_DIAG_BC_D_COND: 583 case INET_DIAG_BC_D_COND:
573 if (!valid_hostcond(bc, len, &min_len)) 584 if (!valid_hostcond(bc, len, &min_len))
574 return -EINVAL; 585 return -EINVAL;
575 /* fall through */ 586 break;
576 case INET_DIAG_BC_AUTO:
577 case INET_DIAG_BC_S_GE: 587 case INET_DIAG_BC_S_GE:
578 case INET_DIAG_BC_S_LE: 588 case INET_DIAG_BC_S_LE:
579 case INET_DIAG_BC_D_GE: 589 case INET_DIAG_BC_D_GE:
580 case INET_DIAG_BC_D_LE: 590 case INET_DIAG_BC_D_LE:
581 case INET_DIAG_BC_JMP: 591 if (!valid_port_comparison(bc, len, &min_len))
582 if (op->no < min_len || op->no > len + 4 || op->no & 3)
583 return -EINVAL;
584 if (op->no < len &&
585 !valid_cc(bytecode, bytecode_len, len - op->no))
586 return -EINVAL; 592 return -EINVAL;
587 break; 593 break;
594 case INET_DIAG_BC_AUTO:
595 case INET_DIAG_BC_JMP:
588 case INET_DIAG_BC_NOP: 596 case INET_DIAG_BC_NOP:
589 break; 597 break;
590 default: 598 default:
591 return -EINVAL; 599 return -EINVAL;
592 } 600 }
601
602 if (op->code != INET_DIAG_BC_NOP) {
603 if (op->no < min_len || op->no > len + 4 || op->no & 3)
604 return -EINVAL;
605 if (op->no < len &&
606 !valid_cc(bytecode, bytecode_len, len - op->no))
607 return -EINVAL;
608 }
609
593 if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) 610 if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
594 return -EINVAL; 611 return -EINVAL;
595 bc += op->yes; 612 bc += op->yes;