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
@ -235,16 +235,26 @@ in_pcblbgroup_alloc(struct inpcblbgrouphead *hdr, u_char vflag,
|
|||||||
grp->il_lport = port;
|
grp->il_lport = port;
|
||||||
grp->il_dependladdr = *addr;
|
grp->il_dependladdr = *addr;
|
||||||
grp->il_inpsiz = size;
|
grp->il_inpsiz = size;
|
||||||
LIST_INSERT_HEAD(hdr, grp, il_list);
|
CK_LIST_INSERT_HEAD(hdr, grp, il_list);
|
||||||
return (grp);
|
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
|
static void
|
||||||
in_pcblbgroup_free(struct inpcblbgroup *grp)
|
in_pcblbgroup_free(struct inpcblbgroup *grp)
|
||||||
{
|
{
|
||||||
|
|
||||||
LIST_REMOVE(grp, il_list);
|
CK_LIST_REMOVE(grp, il_list);
|
||||||
free(grp, M_PCB);
|
epoch_call(net_epoch_preempt, &grp->il_epoch_ctx,
|
||||||
|
in_pcblbgroup_free_deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct inpcblbgroup *
|
static struct inpcblbgroup *
|
||||||
@ -347,7 +357,7 @@ in_pcbinslbgrouphash(struct inpcb *inp)
|
|||||||
hdr = &pcbinfo->ipi_lbgrouphashbase[
|
hdr = &pcbinfo->ipi_lbgrouphashbase[
|
||||||
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
||||||
pcbinfo->ipi_lbgrouphashmask)];
|
pcbinfo->ipi_lbgrouphashmask)];
|
||||||
LIST_FOREACH(grp, hdr, il_list) {
|
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||||
if (grp->il_vflag == inp->inp_vflag &&
|
if (grp->il_vflag == inp->inp_vflag &&
|
||||||
grp->il_lport == inp->inp_lport &&
|
grp->il_lport == inp->inp_lport &&
|
||||||
memcmp(&grp->il_dependladdr,
|
memcmp(&grp->il_dependladdr,
|
||||||
@ -409,7 +419,7 @@ in_pcbremlbgrouphash(struct inpcb *inp)
|
|||||||
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
|
||||||
pcbinfo->ipi_lbgrouphashmask)];
|
pcbinfo->ipi_lbgrouphashmask)];
|
||||||
|
|
||||||
LIST_FOREACH(grp, hdr, il_list) {
|
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||||
for (i = 0; i < grp->il_inpcnt; ++i) {
|
for (i = 0; i < grp->il_inpcnt; ++i) {
|
||||||
if (grp->il_inp[i] != inp)
|
if (grp->il_inp[i] != inp)
|
||||||
continue;
|
continue;
|
||||||
@ -1972,7 +1982,7 @@ in_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
|
|||||||
* - Load balanced group does not contain IPv4 mapped INET6 wild sockets
|
* - Load balanced group does not contain IPv4 mapped INET6 wild sockets
|
||||||
*/
|
*/
|
||||||
local_wild = NULL;
|
local_wild = NULL;
|
||||||
LIST_FOREACH(grp, hdr, il_list) {
|
CK_LIST_FOREACH(grp, hdr, il_list) {
|
||||||
#ifdef INET6
|
#ifdef INET6
|
||||||
if (!(grp->il_vflag & INP_IPV4))
|
if (!(grp->il_vflag & INP_IPV4))
|
||||||
continue;
|
continue;
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
*/
|
*/
|
||||||
CK_LIST_HEAD(inpcbhead, inpcb);
|
CK_LIST_HEAD(inpcbhead, inpcb);
|
||||||
CK_LIST_HEAD(inpcbporthead, inpcbport);
|
CK_LIST_HEAD(inpcbporthead, inpcbport);
|
||||||
|
CK_LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
|
||||||
typedef uint64_t inp_gen_t;
|
typedef uint64_t inp_gen_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -566,7 +567,8 @@ struct inpcbgroup {
|
|||||||
* is dynamically resized as processes bind/unbind to that specific group.
|
* is dynamically resized as processes bind/unbind to that specific group.
|
||||||
*/
|
*/
|
||||||
struct inpcblbgroup {
|
struct inpcblbgroup {
|
||||||
LIST_ENTRY(inpcblbgroup) il_list;
|
CK_LIST_ENTRY(inpcblbgroup) il_list;
|
||||||
|
struct epoch_context il_epoch_ctx;
|
||||||
uint16_t il_lport; /* (c) */
|
uint16_t il_lport; /* (c) */
|
||||||
u_char il_vflag; /* (c) */
|
u_char il_vflag; /* (c) */
|
||||||
u_char il_pad;
|
u_char il_pad;
|
||||||
@ -578,7 +580,6 @@ struct inpcblbgroup {
|
|||||||
uint32_t il_inpcnt; /* cur count in il_inp[] (h) */
|
uint32_t il_inpcnt; /* cur count in il_inp[] (h) */
|
||||||
struct inpcb *il_inp[]; /* (h) */
|
struct inpcb *il_inp[]; /* (h) */
|
||||||
};
|
};
|
||||||
LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
|
|
||||||
|
|
||||||
#define INP_LOCK_INIT(inp, d, t) \
|
#define INP_LOCK_INIT(inp, d, t) \
|
||||||
rw_init_flags(&(inp)->inp_lock, (t), RW_RECURSE | RW_DUPOK)
|
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 group does not contain jailed sockets.
|
||||||
* - Load balanced does not contain IPv4 mapped INET6 wild 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
|
#ifdef INET
|
||||||
if (!(grp->il_vflag & INP_IPV6))
|
if (!(grp->il_vflag & INP_IPV6))
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user