Commit 96ec9da3 authored by Sridhar Samudrala's avatar Sridhar Samudrala Committed by Greg Kroah-Hartman

Fix sctp privilege elevation (CVE-2006-3745)

sctp_make_abort_user() now takes the msg_len along with the msg
so that we don't have to recalculate the bytes in iovec.
It also uses memcpy_fromiovec() so that we don't go beyond the
length allocated.

It is good to have this fix even if verify_iovec() is fixed to
return error on overflow.
Signed-off-by: default avatarSridhar Samudrala <sri@us.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4c4f4930
...@@ -405,19 +405,6 @@ static inline int sctp_list_single_entry(struct list_head *head) ...@@ -405,19 +405,6 @@ static inline int sctp_list_single_entry(struct list_head *head)
return ((head->next != head) && (head->next == head->prev)); return ((head->next != head) && (head->next == head->prev));
} }
/* Calculate the size (in bytes) occupied by the data of an iovec. */
static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
{
size_t retval = 0;
for (; iovlen > 0; --iovlen) {
retval += iov->iov_len;
iov++;
}
return retval;
}
/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */ /* Generate a random jitter in the range of -50% ~ +50% of input RTO. */
static inline __s32 sctp_jitter(__u32 rto) static inline __s32 sctp_jitter(__u32 rto)
{ {
......
...@@ -221,8 +221,7 @@ struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *, ...@@ -221,8 +221,7 @@ struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *,
const struct sctp_chunk *, const struct sctp_chunk *,
__u32 tsn); __u32 tsn);
struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *, struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *,
const struct sctp_chunk *, const struct msghdr *, size_t msg_len);
const struct msghdr *);
struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *, struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *,
const struct sctp_chunk *, const struct sctp_chunk *,
const __u8 *, const __u8 *,
......
...@@ -806,38 +806,26 @@ struct sctp_chunk *sctp_make_abort_no_data( ...@@ -806,38 +806,26 @@ struct sctp_chunk *sctp_make_abort_no_data(
/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */
struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
const struct sctp_chunk *chunk, const struct msghdr *msg,
const struct msghdr *msg) size_t paylen)
{ {
struct sctp_chunk *retval; struct sctp_chunk *retval;
void *payload = NULL, *payoff; void *payload = NULL;
size_t paylen = 0; int err;
struct iovec *iov = NULL;
int iovlen = 0;
if (msg) {
iov = msg->msg_iov;
iovlen = msg->msg_iovlen;
paylen = get_user_iov_size(iov, iovlen);
}
retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen); retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen);
if (!retval) if (!retval)
goto err_chunk; goto err_chunk;
if (paylen) { if (paylen) {
/* Put the msg_iov together into payload. */ /* Put the msg_iov together into payload. */
payload = kmalloc(paylen, GFP_ATOMIC); payload = kmalloc(paylen, GFP_KERNEL);
if (!payload) if (!payload)
goto err_payload; goto err_payload;
payoff = payload;
for (; iovlen > 0; --iovlen) { err = memcpy_fromiovec(payload, msg->msg_iov, paylen);
if (copy_from_user(payoff, iov->iov_base,iov->iov_len)) if (err < 0)
goto err_copy; goto err_copy;
payoff += iov->iov_len;
iov++;
}
} }
sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
......
...@@ -4026,18 +4026,12 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( ...@@ -4026,18 +4026,12 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
* from its upper layer, but retransmits data to the far end * from its upper layer, but retransmits data to the far end
* if necessary to fill gaps. * if necessary to fill gaps.
*/ */
struct msghdr *msg = arg; struct sctp_chunk *abort = arg;
struct sctp_chunk *abort;
sctp_disposition_t retval; sctp_disposition_t retval;
retval = SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer. */ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort)
retval = SCTP_DISPOSITION_NOMEM;
else
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
/* Even if we can't send the ABORT due to low memory delete the /* Even if we can't send the ABORT due to low memory delete the
* TCB. This is a departure from our typical NOMEM handling. * TCB. This is a departure from our typical NOMEM handling.
...@@ -4161,8 +4155,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( ...@@ -4161,8 +4155,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
struct msghdr *msg = arg; struct sctp_chunk *abort = arg;
struct sctp_chunk *abort;
sctp_disposition_t retval; sctp_disposition_t retval;
/* Stop T1-init timer */ /* Stop T1-init timer */
...@@ -4170,12 +4163,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( ...@@ -4170,12 +4163,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
retval = SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer */ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort)
retval = SCTP_DISPOSITION_NOMEM;
else
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED)); SCTP_STATE(SCTP_STATE_CLOSED));
......
...@@ -1477,8 +1477,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1477,8 +1477,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_unlock; goto out_unlock;
} }
if (sinfo_flags & SCTP_ABORT) { if (sinfo_flags & SCTP_ABORT) {
struct sctp_chunk *chunk;
chunk = sctp_make_abort_user(asoc, msg, msg_len);
if (!chunk) {
err = -ENOMEM;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
sctp_primitive_ABORT(asoc, msg); sctp_primitive_ABORT(asoc, chunk);
err = 0; err = 0;
goto out_unlock; goto out_unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment