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:
Mark Johnston 2018-09-10 19:00:29 +00:00
parent 7a364d458a
commit 54af3d0dac
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338571
3 changed files with 20 additions and 9 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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;