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: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
ac185bdc02
commit
c164a9ba0a
@ -404,19 +404,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 *,
|
|||||||
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 @@ no_mem:
|
|||||||
|
|
||||||
/* 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) {
|
retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen);
|
||||||
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);
|
|
||||||
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);
|
||||||
|
@ -4031,18 +4031,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.
|
||||||
@ -4166,8 +4160,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 */
|
||||||
@ -4175,12 +4168,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));
|
||||||
|
@ -1520,8 +1520,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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user