Plug a memory leak and potential NULL-pointer dereference introduced in r331214.
Each TCP connection that uses the system default cc_newreno(4) congestion control algorithm module leaks a "struct newreno" (8 bytes of memory) at connection initialisation time. The NULL-pointer dereference is only germane when using the ABE feature, which is disabled by default. While at it: - Defer the allocation of memory until it is actually needed given that ABE is optional and disabled by default. - Document the ENOMEM errno in getsockopt(2)/setsockopt(2). - Document ENOMEM and ENOBUFS in tcp(4) as being synonymous given that they are used interchangeably throughout the code. - Fix a few other nits also accidentally omitted from the original patch. Reported by: Harsh Jain on freebsd-net@ Tested by: tjh@ Differential Revision: https://reviews.freebsd.org/D15358
This commit is contained in:
parent
eff62dba61
commit
9891578a40
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)getsockopt.2 8.4 (Berkeley) 5/2/95
|
.\" @(#)getsockopt.2 8.4 (Berkeley) 5/2/95
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd January 18, 2017
|
.Dd May 9, 2018
|
||||||
.Dt GETSOCKOPT 2
|
.Dt GETSOCKOPT 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -548,6 +548,8 @@ is not in a valid part of the process address space.
|
|||||||
Installing an
|
Installing an
|
||||||
.Xr accept_filter 9
|
.Xr accept_filter 9
|
||||||
on a non-listening socket was attempted.
|
on a non-listening socket was attempted.
|
||||||
|
.It Bq Er ENOMEM
|
||||||
|
A memory allocation failed that was required to service the request.
|
||||||
.El
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr ioctl 2 ,
|
.Xr ioctl 2 ,
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
.\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93
|
.\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 6, 2017
|
.Dd May 9, 2018
|
||||||
.Dt TCP 4
|
.Dt TCP 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -599,7 +599,7 @@ A socket operation may fail with one of the following errors returned:
|
|||||||
.It Bq Er EISCONN
|
.It Bq Er EISCONN
|
||||||
when trying to establish a connection on a socket which
|
when trying to establish a connection on a socket which
|
||||||
already has one;
|
already has one;
|
||||||
.It Bq Er ENOBUFS
|
.It Bo Er ENOBUFS Bc or Bo Er ENOMEM Bc
|
||||||
when the system runs out of memory for
|
when the system runs out of memory for
|
||||||
an internal data structure;
|
an internal data structure;
|
||||||
.It Bq Er ETIMEDOUT
|
.It Bq Er ETIMEDOUT
|
||||||
|
@ -81,7 +81,7 @@ static MALLOC_DEFINE(M_NEWRENO, "newreno data",
|
|||||||
|
|
||||||
#define CAST_PTR_INT(X) (*((int*)(X)))
|
#define CAST_PTR_INT(X) (*((int*)(X)))
|
||||||
|
|
||||||
static int newreno_cb_init(struct cc_var *ccv);
|
static void newreno_cb_destroy(struct cc_var *ccv);
|
||||||
static void newreno_ack_received(struct cc_var *ccv, uint16_t type);
|
static void newreno_ack_received(struct cc_var *ccv, uint16_t type);
|
||||||
static void newreno_after_idle(struct cc_var *ccv);
|
static void newreno_after_idle(struct cc_var *ccv);
|
||||||
static void newreno_cong_signal(struct cc_var *ccv, uint32_t type);
|
static void newreno_cong_signal(struct cc_var *ccv, uint32_t type);
|
||||||
@ -95,7 +95,7 @@ static VNET_DEFINE(uint32_t, newreno_beta_ecn) = 80;
|
|||||||
|
|
||||||
struct cc_algo newreno_cc_algo = {
|
struct cc_algo newreno_cc_algo = {
|
||||||
.name = "newreno",
|
.name = "newreno",
|
||||||
.cb_init = newreno_cb_init,
|
.cb_destroy = newreno_cb_destroy,
|
||||||
.ack_received = newreno_ack_received,
|
.ack_received = newreno_ack_received,
|
||||||
.after_idle = newreno_after_idle,
|
.after_idle = newreno_after_idle,
|
||||||
.cong_signal = newreno_cong_signal,
|
.cong_signal = newreno_cong_signal,
|
||||||
@ -108,18 +108,28 @@ struct newreno {
|
|||||||
uint32_t beta_ecn;
|
uint32_t beta_ecn;
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
static inline struct newreno *
|
||||||
newreno_cb_init(struct cc_var *ccv)
|
newreno_malloc(struct cc_var *ccv)
|
||||||
{
|
{
|
||||||
struct newreno *nreno;
|
struct newreno *nreno;
|
||||||
|
|
||||||
nreno = malloc(sizeof(struct newreno), M_NEWRENO, M_NOWAIT|M_ZERO);
|
nreno = malloc(sizeof(struct newreno), M_NEWRENO, M_NOWAIT);
|
||||||
if (nreno != NULL) {
|
if (nreno != NULL) {
|
||||||
|
/* NB: nreno is not zeroed, so initialise all fields. */
|
||||||
nreno->beta = V_newreno_beta;
|
nreno->beta = V_newreno_beta;
|
||||||
nreno->beta_ecn = V_newreno_beta_ecn;
|
nreno->beta_ecn = V_newreno_beta_ecn;
|
||||||
|
ccv->cc_data = nreno;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0);
|
return (nreno);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
newreno_cb_destroy(struct cc_var *ccv)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ccv->cc_data != NULL)
|
||||||
|
free(ccv->cc_data, M_NEWRENO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -224,20 +234,18 @@ static void
|
|||||||
newreno_cong_signal(struct cc_var *ccv, uint32_t type)
|
newreno_cong_signal(struct cc_var *ccv, uint32_t type)
|
||||||
{
|
{
|
||||||
struct newreno *nreno;
|
struct newreno *nreno;
|
||||||
uint32_t cwin, factor;
|
uint32_t beta, beta_ecn, cwin, factor;
|
||||||
u_int mss;
|
u_int mss;
|
||||||
|
|
||||||
factor = V_newreno_beta;
|
|
||||||
nreno = ccv->cc_data;
|
|
||||||
if (nreno != NULL) {
|
|
||||||
if (V_cc_do_abe)
|
|
||||||
factor = (type == CC_ECN ? nreno->beta_ecn: nreno->beta);
|
|
||||||
else
|
|
||||||
factor = nreno->beta;
|
|
||||||
}
|
|
||||||
|
|
||||||
cwin = CCV(ccv, snd_cwnd);
|
cwin = CCV(ccv, snd_cwnd);
|
||||||
mss = CCV(ccv, t_maxseg);
|
mss = CCV(ccv, t_maxseg);
|
||||||
|
nreno = ccv->cc_data;
|
||||||
|
beta = (nreno == NULL) ? V_newreno_beta : nreno->beta;
|
||||||
|
beta_ecn = (nreno == NULL) ? V_newreno_beta_ecn : nreno->beta_ecn;
|
||||||
|
if (V_cc_do_abe && type == CC_ECN)
|
||||||
|
factor = beta_ecn;
|
||||||
|
else
|
||||||
|
factor = beta;
|
||||||
|
|
||||||
/* Catch algos which mistakenly leak private signal types. */
|
/* Catch algos which mistakenly leak private signal types. */
|
||||||
KASSERT((type & CC_SIGPRIVMASK) == 0,
|
KASSERT((type & CC_SIGPRIVMASK) == 0,
|
||||||
@ -253,8 +261,8 @@ newreno_cong_signal(struct cc_var *ccv, uint32_t type)
|
|||||||
V_cc_do_abe && V_cc_abe_frlossreduce)) {
|
V_cc_do_abe && V_cc_abe_frlossreduce)) {
|
||||||
CCV(ccv, snd_ssthresh) =
|
CCV(ccv, snd_ssthresh) =
|
||||||
((uint64_t)CCV(ccv, snd_ssthresh) *
|
((uint64_t)CCV(ccv, snd_ssthresh) *
|
||||||
(uint64_t)nreno->beta) /
|
(uint64_t)beta) /
|
||||||
(100ULL * (uint64_t)nreno->beta_ecn);
|
(100ULL * (uint64_t)beta_ecn);
|
||||||
}
|
}
|
||||||
if (!IN_CONGRECOVERY(CCV(ccv, t_flags)))
|
if (!IN_CONGRECOVERY(CCV(ccv, t_flags)))
|
||||||
CCV(ccv, snd_ssthresh) = cwin;
|
CCV(ccv, snd_ssthresh) = cwin;
|
||||||
@ -278,7 +286,6 @@ static void
|
|||||||
newreno_post_recovery(struct cc_var *ccv)
|
newreno_post_recovery(struct cc_var *ccv)
|
||||||
{
|
{
|
||||||
int pipe;
|
int pipe;
|
||||||
pipe = 0;
|
|
||||||
|
|
||||||
if (IN_FASTRECOVERY(CCV(ccv, t_flags))) {
|
if (IN_FASTRECOVERY(CCV(ccv, t_flags))) {
|
||||||
/*
|
/*
|
||||||
@ -302,7 +309,7 @@ newreno_post_recovery(struct cc_var *ccv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf)
|
newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf)
|
||||||
{
|
{
|
||||||
struct newreno *nreno;
|
struct newreno *nreno;
|
||||||
@ -313,9 +320,15 @@ newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf)
|
|||||||
|
|
||||||
nreno = ccv->cc_data;
|
nreno = ccv->cc_data;
|
||||||
opt = buf;
|
opt = buf;
|
||||||
|
|
||||||
switch (sopt->sopt_dir) {
|
switch (sopt->sopt_dir) {
|
||||||
case SOPT_SET:
|
case SOPT_SET:
|
||||||
|
/* We cannot set without cc_data memory. */
|
||||||
|
if (nreno == NULL) {
|
||||||
|
nreno = newreno_malloc(ccv);
|
||||||
|
if (nreno == NULL)
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
switch (opt->name) {
|
switch (opt->name) {
|
||||||
case CC_NEWRENO_BETA:
|
case CC_NEWRENO_BETA:
|
||||||
nreno->beta = opt->val;
|
nreno->beta = opt->val;
|
||||||
@ -328,17 +341,21 @@ newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf)
|
|||||||
default:
|
default:
|
||||||
return (ENOPROTOOPT);
|
return (ENOPROTOOPT);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case SOPT_GET:
|
case SOPT_GET:
|
||||||
switch (opt->name) {
|
switch (opt->name) {
|
||||||
case CC_NEWRENO_BETA:
|
case CC_NEWRENO_BETA:
|
||||||
opt->val = nreno->beta;
|
opt->val = (nreno == NULL) ?
|
||||||
|
V_newreno_beta : nreno->beta;
|
||||||
break;
|
break;
|
||||||
case CC_NEWRENO_BETA_ECN:
|
case CC_NEWRENO_BETA_ECN:
|
||||||
opt->val = nreno->beta_ecn;
|
opt->val = (nreno == NULL) ?
|
||||||
|
V_newreno_beta_ecn : nreno->beta_ecn;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return (ENOPROTOOPT);
|
return (ENOPROTOOPT);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -349,6 +366,7 @@ newreno_ctl_output(struct cc_var *ccv, struct sockopt *sopt, void *buf)
|
|||||||
static int
|
static int
|
||||||
newreno_beta_handler(SYSCTL_HANDLER_ARGS)
|
newreno_beta_handler(SYSCTL_HANDLER_ARGS)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (req->newptr != NULL ) {
|
if (req->newptr != NULL ) {
|
||||||
if (arg1 == &VNET_NAME(newreno_beta_ecn) && !V_cc_do_abe)
|
if (arg1 == &VNET_NAME(newreno_beta_ecn) && !V_cc_do_abe)
|
||||||
return (EACCES);
|
return (EACCES);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user