Commit c164a9ba0a8870c5c9d353f63085319931d69f23
Committed by
Greg Kroah-Hartman
1 parent
ac185bdc02
Exists in
master
and in
7 other branches
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)); |
net/sctp/socket.c
... | ... | @@ -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 | } |