Commit c164a9ba0a8870c5c9d353f63085319931d69f23

Authored by Sridhar Samudrala
Committed by Greg Kroah-Hartman
1 parent ac185bdc02

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>

Showing 5 changed files with 23 additions and 53 deletions Side-by-side Diff

include/net/sctp/sctp.h
... ... @@ -404,19 +404,6 @@
404 404 return ((head->next != head) && (head->next == head->prev));
405 405 }
406 406  
407   -/* Calculate the size (in bytes) occupied by the data of an iovec. */
408   -static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
409   -{
410   - size_t retval = 0;
411   -
412   - for (; iovlen > 0; --iovlen) {
413   - retval += iov->iov_len;
414   - iov++;
415   - }
416   -
417   - return retval;
418   -}
419   -
420 407 /* Generate a random jitter in the range of -50% ~ +50% of input RTO. */
421 408 static inline __s32 sctp_jitter(__u32 rto)
422 409 {
include/net/sctp/sm.h
... ... @@ -221,8 +221,7 @@
221 221 const struct sctp_chunk *,
222 222 __u32 tsn);
223 223 struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *,
224   - const struct sctp_chunk *,
225   - const struct msghdr *);
  224 + const struct msghdr *, size_t msg_len);
226 225 struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *,
227 226 const struct sctp_chunk *,
228 227 const __u8 *,
net/sctp/sm_make_chunk.c
... ... @@ -806,38 +806,26 @@
806 806  
807 807 /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */
808 808 struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
809   - const struct sctp_chunk *chunk,
810   - const struct msghdr *msg)
  809 + const struct msghdr *msg,
  810 + size_t paylen)
811 811 {
812 812 struct sctp_chunk *retval;
813   - void *payload = NULL, *payoff;
814   - size_t paylen = 0;
815   - struct iovec *iov = NULL;
816   - int iovlen = 0;
  813 + void *payload = NULL;
  814 + int err;
817 815  
818   - if (msg) {
819   - iov = msg->msg_iov;
820   - iovlen = msg->msg_iovlen;
821   - paylen = get_user_iov_size(iov, iovlen);
822   - }
823   -
824   - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen);
  816 + retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen);
825 817 if (!retval)
826 818 goto err_chunk;
827 819  
828 820 if (paylen) {
829 821 /* Put the msg_iov together into payload. */
830   - payload = kmalloc(paylen, GFP_ATOMIC);
  822 + payload = kmalloc(paylen, GFP_KERNEL);
831 823 if (!payload)
832 824 goto err_payload;
833   - payoff = payload;
834 825  
835   - for (; iovlen > 0; --iovlen) {
836   - if (copy_from_user(payoff, iov->iov_base,iov->iov_len))
837   - goto err_copy;
838   - payoff += iov->iov_len;
839   - iov++;
840   - }
  826 + err = memcpy_fromiovec(payload, msg->msg_iov, paylen);
  827 + if (err < 0)
  828 + goto err_copy;
841 829 }
842 830  
843 831 sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
net/sctp/sm_statefuns.c
... ... @@ -4031,18 +4031,12 @@
4031 4031 * from its upper layer, but retransmits data to the far end
4032 4032 * if necessary to fill gaps.
4033 4033 */
4034   - struct msghdr *msg = arg;
4035   - struct sctp_chunk *abort;
  4034 + struct sctp_chunk *abort = arg;
4036 4035 sctp_disposition_t retval;
4037 4036  
4038 4037 retval = SCTP_DISPOSITION_CONSUME;
4039 4038  
4040   - /* Generate ABORT chunk to send the peer. */
4041   - abort = sctp_make_abort_user(asoc, NULL, msg);
4042   - if (!abort)
4043   - retval = SCTP_DISPOSITION_NOMEM;
4044   - else
4045   - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
  4039 + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
4046 4040  
4047 4041 /* Even if we can't send the ABORT due to low memory delete the
4048 4042 * TCB. This is a departure from our typical NOMEM handling.
... ... @@ -4166,8 +4160,7 @@
4166 4160 void *arg,
4167 4161 sctp_cmd_seq_t *commands)
4168 4162 {
4169   - struct msghdr *msg = arg;
4170   - struct sctp_chunk *abort;
  4163 + struct sctp_chunk *abort = arg;
4171 4164 sctp_disposition_t retval;
4172 4165  
4173 4166 /* Stop T1-init timer */
... ... @@ -4175,12 +4168,7 @@
4175 4168 SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
4176 4169 retval = SCTP_DISPOSITION_CONSUME;
4177 4170  
4178   - /* Generate ABORT chunk to send the peer */
4179   - abort = sctp_make_abort_user(asoc, NULL, msg);
4180   - if (!abort)
4181   - retval = SCTP_DISPOSITION_NOMEM;
4182   - else
4183   - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
  4171 + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
4184 4172  
4185 4173 sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
4186 4174 SCTP_STATE(SCTP_STATE_CLOSED));
... ... @@ -1520,8 +1520,16 @@
1520 1520 goto out_unlock;
1521 1521 }
1522 1522 if (sinfo_flags & SCTP_ABORT) {
  1523 + struct sctp_chunk *chunk;
  1524 +
  1525 + chunk = sctp_make_abort_user(asoc, msg, msg_len);
  1526 + if (!chunk) {
  1527 + err = -ENOMEM;
  1528 + goto out_unlock;
  1529 + }
  1530 +
1523 1531 SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
1524   - sctp_primitive_ABORT(asoc, msg);
  1532 + sctp_primitive_ABORT(asoc, chunk);
1525 1533 err = 0;
1526 1534 goto out_unlock;
1527 1535 }