DragonFlyBSD Kernel Audit
DF-0747 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/ipfw3_layer4/ip_fw3_layer4.c b/sys/net/ipfw3_layer4/ip_fw3_layer4.c
--- a/sys/net/ipfw3_layer4/ip_fw3_layer4.c
+++ b/sys/net/ipfw3_layer4/ip_fw3_layer4.c
@@ -169,20 +169,22 @@
 
 /*
  * match TCP packets which have all tcpflag except SYN.
+ *
+ * Use the cached f_id.flags (populated only for first/unfragmented TCP
+ * segments in ip_fw3_chk() at the offset==0 branch).  Non-first fragments
+ * have no TCP header in the mbuf, so re-reading L3HDR(tcphdr,ip)->th_flags
+ * would either dereference attacker-controlled fragment data or over-read
+ * past valid m_len into the mbuf backing store.
  */
 void check_established(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
 		struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
 {
 	struct ipfw_flow_id *fid;
-	struct mbuf *m = (*args)->m;
-	struct ip *ip = mtod(m, struct ip *);
 
 	*cmd_ctl = IP_FW_CTL_NO;
 	fid = &(*args)->f_id;
 	if (fid->proto == IPPROTO_TCP) {
-		/* offset == 0 && */
-		if ((L3HDR(struct tcphdr, ip)->th_flags &
-				(TH_RST | TH_ACK | TH_SYN)) != TH_SYN) {
+		if ((fid->flags & (TH_RST | TH_ACK | TH_SYN)) != TH_SYN) {
 			*cmd_val = IP_FW_MATCH;
 			return;
 		}