Fix synchronization of LB group access.
Lookups are protected by an epoch section, so the LB group linkage must be a CK_LIST rather than a plain LIST. Furthermore, we were not deferring LB group frees, so in_pcbremlbgrouphash() could race with readers and cause a use-after-free. Reviewed by: sbruno, Johannes Lundberg <johalun0@gmail.com> Tested by: gallatin Approved by: re (gjb) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D17031
This commit is contained in:
parent
7a364d458a
commit
54af3d0dac
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338571
@ -235,16 +235,26 @@ in_pcblbgroup_alloc(struct inpcblbgrouphead *hdr, u_char vflag,
|
||||
grp->il_lport = port;
|
||||
grp->il_dependladdr = *addr;
|
||||
grp->il_inpsiz = size;
|
||||
LIST_INSERT_HEAD(hdr, grp, il_list);
|
||||
CK_LIST_INSERT_HEAD(hdr, grp, il_list);
|
||||
return (grp);
|
||||
}
|
||||
|
||||
static void
|
||||
in_pcblbgroup_free_deferred(epoch_context_t ctx)
|
||||
{
|
||||
struct inpcblbgroup *grp;
|
||||
|
||||
grp = __containerof(ctx, struct inpcblbgroup, il_epoch_ctx);
|
||||
free(grp, M_PCB);
|
||||
}
|
||||
|
||||
static void
|
||||
in_pcblbgroup_free(struct inpcblbgroup *grp)
|
||||
{
|
||||
|
||||
LIST_REMOVE(grp, il_list);
|
||||
free(grp, M_PCB);
|
||||
CK_LIST_REMOVE(grp, il_list);
|
||||
epoch_call(net_epoch_preempt, &grp->il_epoch_ctx,
|
||||
in_pcblbgroup_free_deferred);
|
||||
}
|
||||
|
||||
static struct inpcblbgroup *
|
||||
@ -347,7 +357,7 @@ in_pcbinslbgrouphash(struct inpcb *inp)
|
||||
hdr = &pcbinfo->ipi_lbgrouphashbase[
|
||||
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
||||
pcbinfo->ipi_lbgrouphashmask)];
|
||||
LIST_FOREACH(grp, hdr, il_list) {
|
||||
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||
if (grp->il_vflag == inp->inp_vflag &&
|
||||
grp->il_lport == inp->inp_lport &&
|
||||
memcmp(&grp->il_dependladdr,
|
||||
@ -409,7 +419,7 @@ in_pcbremlbgrouphash(struct inpcb *inp)
|
||||
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
||||
pcbinfo->ipi_lbgrouphashmask)];
|
||||
|
||||
LIST_FOREACH(grp, hdr, il_list) {
|
||||
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||
for (i = 0; i < grp->il_inpcnt; ++i) {
|
||||
if (grp->il_inp[i] != inp)
|
||||
continue;
|
||||
@ -1972,7 +1982,7 @@ in_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
|
||||
* - Load balanced group does not contain IPv4 mapped INET6 wild sockets
|
||||
*/
|
||||
local_wild = NULL;
|
||||
LIST_FOREACH(grp, hdr, il_list) {
|
||||
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||
#ifdef INET6
|
||||
if (!(grp->il_vflag & INP_IPV4))
|
||||
continue;
|
||||
|
@ -70,6 +70,7 @@
|
||||
*/
|
||||
CK_LIST_HEAD(inpcbhead, inpcb);
|
||||
CK_LIST_HEAD(inpcbporthead, inpcbport);
|
||||
CK_LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
|
||||
typedef uint64_t inp_gen_t;
|
||||
|
||||
/*
|
||||
@ -566,7 +567,8 @@ struct inpcbgroup {
|
||||
* is dynamically resized as processes bind/unbind to that specific group.
|
||||
*/
|
||||
struct inpcblbgroup {
|
||||
LIST_ENTRY(inpcblbgroup) il_list;
|
||||
CK_LIST_ENTRY(inpcblbgroup) il_list;
|
||||
struct epoch_context il_epoch_ctx;
|
||||
uint16_t il_lport; /* (c) */
|
||||
u_char il_vflag; /* (c) */
|
||||
u_char il_pad;
|
||||
@ -578,7 +580,6 @@ struct inpcblbgroup {
|
||||
uint32_t il_inpcnt; /* cur count in il_inp[] (h) */
|
||||
struct inpcb *il_inp[]; /* (h) */
|
||||
};
|
||||
LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
|
||||
|
||||
#define INP_LOCK_INIT(inp, d, t) \
|
||||
rw_init_flags(&(inp)->inp_lock, (t), RW_RECURSE | RW_DUPOK)
|
||||
|
@ -889,7 +889,7 @@ in6_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
|
||||
* - Load balanced group does not contain jailed sockets.
|
||||
* - Load balanced does not contain IPv4 mapped INET6 wild sockets.
|
||||
*/
|
||||
LIST_FOREACH(grp, hdr, il_list) {
|
||||
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||
#ifdef INET
|
||||
if (!(grp->il_vflag & INP_IPV6))
|
||||
continue;
|
||||
|
Loading…
Reference in New Issue
Block a user