Hello, On Tue, Aug 09, 2022 at 06:47:54PM +0200, Hrvoje Popovski wrote: > Hi all, > > I'm running > OpenBSD 7.2-beta (GENERIC.MP) #651: Tue Jul 26 23:11:26 MDT 2022 > dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP > > on production firewall and for few weeks it was stable. Firewall panic > today and I will sysupgrade it, but maybe this panic message is > interesting so I'm sending it here. > > > bcbnfw1# uvm_fault(0xffffffff823a1a20, 0x0, 0, 1) -> e > kernel: page fault trap, code=0 > Stopped at pf_state_export+0x38: movq 0(%rax),%rcx > TID PID UID PRFLAGS PFLAGS CPU COMMAND > 309438 83954 0 0x14000 0x200 1 softnet > 486408 53515 0 0x14000 0x200 3 softnet > * 80122 54608 0 0x14000 0x200 2 softnet > pf_state_export(fffffd806152f9dc,fffffd8664eb12b0) at pf_state_export+0x38 > pfsync_sendout() at pfsync_sendout+0x5e4 > pfsync_update_state(fffffd8728968d40) at pfsync_update_state+0x15b > pf_test(2,1,ffff800000bbb000,ffff800020c336d8) at pf_test+0x117a > ip_input_if(ffff800020c336d8,ffff800020c336e4,4,0,ffff800000bbb000) at > ip_input_if+0xcd > ipv4_input(ffff800000bbb000,fffffd80661d5300) at ipv4_input+0x39 > ether_input(ffff800000bbb000,fffffd80661d5300) at ether_input+0x3b1 > carp_input(ffff800000bd2000,fffffd80661d5300,5e000101) at carp_input+0x196 > ether_input(ffff800000bd2000,fffffd80661d5300) at ether_input+0x1d9 > vlan_input(ffff800000b9d000,fffffd80661d5300,ffff800020c3390c) at > vlan_input+0x23d > ether_input(ffff800000b9d000,fffffd80661d5300) at ether_input+0x85 > if_input_process(ffff80000048b048,ffff800020c339a8) at if_input_process+0x6f > ifiq_process(ffff80000048ea00) at ifiq_process+0x69 > taskq_thread(ffff800000035080) at taskq_thread+0x100 > end trace frame: 0x0, count: 1 > https://urldefense.com/v3/__https://www.openbsd.org/ddb.html__;!!ACWV5N9M2RV99hQ!IOiwmHap_PoZFHkbnG9qtmjA5iScoEpS7aLV1rsiWFh15vslrJlIXq44u3rPHpnKd7BcyB9J7lkrPKVEADwkHi4$ > describes the minimum info required in > bug reports. Insufficient info makes it difficult to find and fix bugs. > > > ddb{2}> show reg > rdi 0xfffffd806152fae4 > rsi 0 > rbp 0xffff800020c33340 > rbx 0x19c > rdx 0x4 > rcx 0 > rax 0 > r8 0x104 > r9 0x7d788a8c5153bdc > r10 0x92a5ce4f38be8823 > r11 0xfffffd806152f9dc > r12 0xfffffd8664eb12b0 > r13 0 > r14 0xfffffd806152f9dc > r15 0xfffffd8664eb12b0 > rip 0xffffffff81387678 pf_state_export+0x38 > cs 0x8 > rflags 0x10246 __ALIGN_SIZE+0xf246 > rsp 0xffff800020c33300 > ss 0x10 > pf_state_export+0x38: movq 0(%rax),%rcx >
this is a NULL pointer dereference panic. I think we've seen it few months back. patch below was applied to one of your test machines if I remember correct. can you give it a try again to see if it will help? the change adds a mutex to pf_state structure to protect references to keys attached to state. we also have to take into account a fact that pf_state_export() may be presented with state which keys got detached. Hence we have to skip such state when doing export. Therefore pf_state_export() indicates a failure to hint caller whether data were written (success) and we should move to next free slot in output buffer. Or nothing got written (failure) and current slot in output buffer is still free. thanks and regards sashan --------8<---------------8<---------------8<------------------8<-------- diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c index c78ca62766e..b039a50a7bb 100644 --- a/sys/net/if_pfsync.c +++ b/sys/net/if_pfsync.c @@ -157,16 +157,16 @@ struct { }; struct pfsync_q { - void (*write)(struct pf_state *, void *); + int (*write)(struct pf_state *, void *); size_t len; u_int8_t action; }; /* we have one of these for every PFSYNC_S_ */ -void pfsync_out_state(struct pf_state *, void *); -void pfsync_out_iack(struct pf_state *, void *); -void pfsync_out_upd_c(struct pf_state *, void *); -void pfsync_out_del(struct pf_state *, void *); +int pfsync_out_state(struct pf_state *, void *); +int pfsync_out_iack(struct pf_state *, void *); +int pfsync_out_upd_c(struct pf_state *, void *); +int pfsync_out_del(struct pf_state *, void *); struct pfsync_q pfsync_qs[] = { { pfsync_out_iack, sizeof(struct pfsync_ins_ack), PFSYNC_ACT_INS_ACK }, @@ -516,10 +516,10 @@ pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, return (0); } -void +int pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) { - pf_state_export(sp, st); + return (pf_state_export(sp, st)); } int @@ -680,6 +680,7 @@ pfsync_state_import(struct pfsync_state *sp, int flags) st->sync_state = PFSYNC_S_NONE; refcnt_init(&st->refcnt); + mtx_init(&st->mtx, IPL_NET); /* XXX when we have anchors, use STATE_INC_COUNTERS */ r->states_cur++; @@ -1529,24 +1530,25 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (0); } -void +int pfsync_out_state(struct pf_state *st, void *buf) { struct pfsync_state *sp = buf; - pfsync_state_export(sp, st); + return (pfsync_state_export(sp, st)); } -void +int pfsync_out_iack(struct pf_state *st, void *buf) { struct pfsync_ins_ack *iack = buf; iack->id = st->id; iack->creatorid = st->creatorid; + return (0); } -void +int pfsync_out_upd_c(struct pf_state *st, void *buf) { struct pfsync_upd_c *up = buf; @@ -1557,9 +1559,10 @@ pfsync_out_upd_c(struct pf_state *st, void *buf) pf_state_peer_hton(&st->dst, &up->dst); up->creatorid = st->creatorid; up->timeout = st->timeout; + return (0); } -void +int pfsync_out_del(struct pf_state *st, void *buf) { struct pfsync_del_c *dp = buf; @@ -1568,6 +1571,7 @@ pfsync_out_del(struct pf_state *st, void *buf) dp->creatorid = st->creatorid; SET(st->state_flags, PFSTATE_NOSYNC); + return (0); } void @@ -1881,8 +1885,8 @@ pfsync_sendout(void) KASSERT(st->snapped == 1); st->sync_state = PFSYNC_S_NONE; st->snapped = 0; - pfsync_qs[q].write(st, m->m_data + offset); - offset += pfsync_qs[q].len; + if (pfsync_qs[q].write(st, m->m_data + offset) == 0) + offset += pfsync_qs[q].len; pf_state_unref(st); count++; diff --git a/sys/net/if_pfsync.h b/sys/net/if_pfsync.h index bee6c77f228..4440a561854 100644 --- a/sys/net/if_pfsync.h +++ b/sys/net/if_pfsync.h @@ -326,7 +326,7 @@ int pfsync_sysctl(int *, u_int, void *, size_t *, #define PFSYNC_SI_CKSUM 0x02 #define PFSYNC_SI_ACK 0x04 int pfsync_state_import(struct pfsync_state *, int); -void pfsync_state_export(struct pfsync_state *, +int pfsync_state_export(struct pfsync_state *, struct pf_state *); void pfsync_insert_state(struct pf_state *); diff --git a/sys/net/pf.c b/sys/net/pf.c index 7183db91254..6e41ef49176 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -185,7 +185,8 @@ int pf_translate_icmp_af(struct pf_pdesc*, int, void *); void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, int, sa_family_t, struct pf_rule *, u_int); void pf_detach_state(struct pf_state *); -void pf_state_key_detach(struct pf_state *, int); +void pf_state_key_detach(struct pf_state *, + struct pf_state_key *); u_int32_t pf_tcp_iss(struct pf_pdesc *); void pf_rule_to_actions(struct pf_rule *, struct pf_rule_actions *); @@ -260,6 +261,9 @@ void pf_state_key_unlink_inpcb(struct pf_state_key *); void pf_inpcb_unlink_state_key(struct inpcb *); void pf_pktenqueue_delayed(void *); int32_t pf_state_expires(const struct pf_state *, uint8_t); +void pf_state_keys_take(struct pf_state *, + struct pf_state_key **); +void pf_state_keys_rele(struct pf_state_key **); #if NPFLOG > 0 void pf_log_matches(struct pf_pdesc *, struct pf_rule *, @@ -779,7 +783,8 @@ pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) s->key[idx] = sk; if ((si = pool_get(&pf_state_item_pl, PR_NOWAIT)) == NULL) { - pf_state_key_detach(s, idx); + pf_state_key_detach(s, s->key[idx]); + s->key[idx] = NULL; return (-1); } si->s = s; @@ -799,42 +804,50 @@ pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) void pf_detach_state(struct pf_state *s) { - if (s->key[PF_SK_WIRE] == s->key[PF_SK_STACK]) - s->key[PF_SK_WIRE] = NULL; + struct pf_state_key *key[2]; + + mtx_enter(&s->mtx); + key[PF_SK_WIRE] = s->key[PF_SK_WIRE]; + key[PF_SK_STACK] = s->key[PF_SK_STACK]; + s->key[PF_SK_WIRE] = NULL; + s->key[PF_SK_STACK] = NULL; + mtx_leave(&s->mtx); + + if (key[PF_SK_WIRE] == key[PF_SK_STACK]) + key[PF_SK_WIRE] = NULL; - if (s->key[PF_SK_STACK] != NULL) - pf_state_key_detach(s, PF_SK_STACK); + if (key[PF_SK_STACK] != NULL) + pf_state_key_detach(s, key[PF_SK_STACK]); - if (s->key[PF_SK_WIRE] != NULL) - pf_state_key_detach(s, PF_SK_WIRE); + if (key[PF_SK_WIRE] != NULL) + pf_state_key_detach(s, key[PF_SK_WIRE]); } void -pf_state_key_detach(struct pf_state *s, int idx) +pf_state_key_detach(struct pf_state *s, struct pf_state_key *key) { struct pf_state_item *si; - struct pf_state_key *sk; - if (s->key[idx] == NULL) + PF_STATE_ASSERT_LOCKED(); + + if (key == NULL) return; - si = TAILQ_FIRST(&s->key[idx]->states); + si = TAILQ_FIRST(&key->states); while (si && si->s != s) si = TAILQ_NEXT(si, entry); if (si) { - TAILQ_REMOVE(&s->key[idx]->states, si, entry); + TAILQ_REMOVE(&key->states, si, entry); pool_put(&pf_state_item_pl, si); } - sk = s->key[idx]; - s->key[idx] = NULL; - if (TAILQ_EMPTY(&sk->states)) { - RB_REMOVE(pf_state_tree, &pf_statetbl, sk); - sk->removed = 1; - pf_state_key_unlink_reverse(sk); - pf_state_key_unlink_inpcb(sk); - pf_state_key_unref(sk); + if (TAILQ_EMPTY(&key->states)) { + RB_REMOVE(pf_state_tree, &pf_statetbl, key); + key->removed = 1; + pf_state_key_unlink_reverse(key); + pf_state_key_unlink_inpcb(key); + pf_state_key_unref(key); } } @@ -997,7 +1010,9 @@ pf_state_insert(struct pfi_kif *kif, struct pf_state_key **skw, } *skw = s->key[PF_SK_WIRE]; if (pf_state_key_attach(*sks, s, PF_SK_STACK)) { - pf_state_key_detach(s, PF_SK_WIRE); + pf_state_key_detach(s, s->key[PF_SK_WIRE]); + s->key[PF_SK_WIRE] = NULL; + *skw = NULL; PF_STATE_EXIT_WRITE(); return (-1); } @@ -1187,30 +1202,35 @@ pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more) return (ret ? ret->s : NULL); } -void +int pf_state_export(struct pfsync_state *sp, struct pf_state *st) { int32_t expire; + struct pf_state_key *key[2] = { NULL, NULL }; memset(sp, 0, sizeof(struct pfsync_state)); /* copy from state key */ - sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0]; - sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1]; - sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0]; - sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1]; - sp->key[PF_SK_WIRE].rdomain = htons(st->key[PF_SK_WIRE]->rdomain); - sp->key[PF_SK_WIRE].af = st->key[PF_SK_WIRE]->af; - sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0]; - sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1]; - sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0]; - sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1]; - sp->key[PF_SK_STACK].rdomain = htons(st->key[PF_SK_STACK]->rdomain); - sp->key[PF_SK_STACK].af = st->key[PF_SK_STACK]->af; + pf_state_keys_take(st, key); + if ((key[PF_SK_WIRE] == NULL) || (key[PF_SK_STACK] == NULL)) + return (-1); + + sp->key[PF_SK_WIRE].addr[0] = key[PF_SK_WIRE]->addr[0]; + sp->key[PF_SK_WIRE].addr[1] = key[PF_SK_WIRE]->addr[1]; + sp->key[PF_SK_WIRE].port[0] = key[PF_SK_WIRE]->port[0]; + sp->key[PF_SK_WIRE].port[1] = key[PF_SK_WIRE]->port[1]; + sp->key[PF_SK_WIRE].rdomain = htons(key[PF_SK_WIRE]->rdomain); + sp->key[PF_SK_WIRE].af = key[PF_SK_WIRE]->af; + sp->key[PF_SK_STACK].addr[0] = key[PF_SK_STACK]->addr[0]; + sp->key[PF_SK_STACK].addr[1] = key[PF_SK_STACK]->addr[1]; + sp->key[PF_SK_STACK].port[0] = key[PF_SK_STACK]->port[0]; + sp->key[PF_SK_STACK].port[1] = key[PF_SK_STACK]->port[1]; + sp->key[PF_SK_STACK].rdomain = htons(key[PF_SK_STACK]->rdomain); + sp->key[PF_SK_STACK].af = key[PF_SK_STACK]->af; sp->rtableid[PF_SK_WIRE] = htonl(st->rtableid[PF_SK_WIRE]); sp->rtableid[PF_SK_STACK] = htonl(st->rtableid[PF_SK_STACK]); - sp->proto = st->key[PF_SK_WIRE]->proto; - sp->af = st->key[PF_SK_WIRE]->af; + sp->proto = key[PF_SK_WIRE]->proto; + sp->af = key[PF_SK_WIRE]->af; /* copy from state */ strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); @@ -1257,6 +1277,10 @@ pf_state_export(struct pfsync_state *sp, struct pf_state *st) sp->set_tos = st->set_tos; sp->set_prio[0] = st->set_prio[0]; sp->set_prio[1] = st->set_prio[1]; + + pf_state_keys_rele(key); + + return (0); } /* END state table stuff */ @@ -4110,6 +4134,7 @@ pf_create_state(struct pf_pdesc *pd, struct pf_rule *r, struct pf_rule *a, * pf_state_inserts() grabs reference for pfsync! */ refcnt_init(&s->refcnt); + mtx_init(&s->mtx, IPL_NET); switch (pd->proto) { case IPPROTO_TCP: @@ -7780,3 +7805,19 @@ pf_pktenqueue_delayed(void *arg) pool_put(&pf_pktdelay_pl, pdy); } + +void +pf_state_keys_take(struct pf_state *st, struct pf_state_key *keys[]) +{ + mtx_enter(&st->mtx); + keys[PF_SK_WIRE] = pf_state_key_ref(st->key[PF_SK_WIRE]); + keys[PF_SK_STACK] = pf_state_key_ref(st->key[PF_SK_STACK]); + mtx_leave(&st->mtx); +} + +void +pf_state_keys_rele(struct pf_state_key *keys[]) +{ + pf_state_key_unref(keys[PF_SK_WIRE]); + pf_state_key_unref(keys[PF_SK_STACK]); +} diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 6f88ff687ed..986fa329b16 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -764,6 +764,7 @@ struct pf_state { struct pf_sn_head src_nodes; struct pf_state_key *key[2]; /* addresses stack and wire */ struct pfi_kif *kif; + struct mutex mtx; u_int64_t packets[2]; u_int64_t bytes[2]; int32_t creation; @@ -1738,7 +1739,7 @@ void pf_state_rm_src_node(struct pf_state *, extern struct pf_state *pf_find_state_byid(struct pf_state_cmp *); extern struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, u_int, int *); -extern void pf_state_export(struct pfsync_state *, +extern int pf_state_export(struct pfsync_state *, struct pf_state *); extern void pf_print_state(struct pf_state *); extern void pf_print_flags(u_int8_t);