diff options
Diffstat (limited to 'net/compat.c')
| -rw-r--r-- | net/compat.c | 44 |
1 files changed, 26 insertions, 18 deletions
diff --git a/net/compat.c b/net/compat.c index d99ab9695893..e593dace2fdb 100644 --- a/net/compat.c +++ b/net/compat.c | |||
| @@ -135,13 +135,14 @@ static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *ms | |||
| 135 | * thus placement) of cmsg headers and length are different for | 135 | * thus placement) of cmsg headers and length are different for |
| 136 | * 32-bit apps. -DaveM | 136 | * 32-bit apps. -DaveM |
| 137 | */ | 137 | */ |
| 138 | int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, | 138 | int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk, |
| 139 | unsigned char *stackbuf, int stackbuf_size) | 139 | unsigned char *stackbuf, int stackbuf_size) |
| 140 | { | 140 | { |
| 141 | struct compat_cmsghdr __user *ucmsg; | 141 | struct compat_cmsghdr __user *ucmsg; |
| 142 | struct cmsghdr *kcmsg, *kcmsg_base; | 142 | struct cmsghdr *kcmsg, *kcmsg_base; |
| 143 | compat_size_t ucmlen; | 143 | compat_size_t ucmlen; |
| 144 | __kernel_size_t kcmlen, tmp; | 144 | __kernel_size_t kcmlen, tmp; |
| 145 | int err = -EFAULT; | ||
| 145 | 146 | ||
| 146 | kcmlen = 0; | 147 | kcmlen = 0; |
| 147 | kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; | 148 | kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; |
| @@ -156,6 +157,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, | |||
| 156 | 157 | ||
| 157 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + | 158 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + |
| 158 | CMSG_ALIGN(sizeof(struct cmsghdr))); | 159 | CMSG_ALIGN(sizeof(struct cmsghdr))); |
| 160 | tmp = CMSG_ALIGN(tmp); | ||
| 159 | kcmlen += tmp; | 161 | kcmlen += tmp; |
| 160 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); | 162 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); |
| 161 | } | 163 | } |
| @@ -167,30 +169,34 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, | |||
| 167 | * until we have successfully copied over all of the data | 169 | * until we have successfully copied over all of the data |
| 168 | * from the user. | 170 | * from the user. |
| 169 | */ | 171 | */ |
| 170 | if(kcmlen > stackbuf_size) | 172 | if (kcmlen > stackbuf_size) |
| 171 | kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); | 173 | kcmsg_base = kcmsg = sock_kmalloc(sk, kcmlen, GFP_KERNEL); |
| 172 | if(kcmsg == NULL) | 174 | if (kcmsg == NULL) |
| 173 | return -ENOBUFS; | 175 | return -ENOBUFS; |
| 174 | 176 | ||
| 175 | /* Now copy them over neatly. */ | 177 | /* Now copy them over neatly. */ |
| 176 | memset(kcmsg, 0, kcmlen); | 178 | memset(kcmsg, 0, kcmlen); |
| 177 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); | 179 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); |
| 178 | while(ucmsg != NULL) { | 180 | while(ucmsg != NULL) { |
| 179 | __get_user(ucmlen, &ucmsg->cmsg_len); | 181 | if (__get_user(ucmlen, &ucmsg->cmsg_len)) |
| 182 | goto Efault; | ||
| 183 | if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) | ||
| 184 | goto Einval; | ||
| 180 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + | 185 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + |
| 181 | CMSG_ALIGN(sizeof(struct cmsghdr))); | 186 | CMSG_ALIGN(sizeof(struct cmsghdr))); |
| 187 | if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp)) | ||
| 188 | goto Einval; | ||
| 182 | kcmsg->cmsg_len = tmp; | 189 | kcmsg->cmsg_len = tmp; |
| 183 | __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); | 190 | tmp = CMSG_ALIGN(tmp); |
| 184 | __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); | 191 | if (__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level) || |
| 185 | 192 | __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type) || | |
| 186 | /* Copy over the data. */ | 193 | copy_from_user(CMSG_DATA(kcmsg), |
| 187 | if(copy_from_user(CMSG_DATA(kcmsg), | 194 | CMSG_COMPAT_DATA(ucmsg), |
| 188 | CMSG_COMPAT_DATA(ucmsg), | 195 | (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) |
| 189 | (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) | 196 | goto Efault; |
| 190 | goto out_free_efault; | ||
| 191 | 197 | ||
| 192 | /* Advance. */ | 198 | /* Advance. */ |
| 193 | kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); | 199 | kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp); |
| 194 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); | 200 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); |
| 195 | } | 201 | } |
| 196 | 202 | ||
| @@ -199,10 +205,12 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, | |||
| 199 | kmsg->msg_controllen = kcmlen; | 205 | kmsg->msg_controllen = kcmlen; |
| 200 | return 0; | 206 | return 0; |
| 201 | 207 | ||
| 202 | out_free_efault: | 208 | Einval: |
| 203 | if(kcmsg_base != (struct cmsghdr *)stackbuf) | 209 | err = -EINVAL; |
| 204 | kfree(kcmsg_base); | 210 | Efault: |
| 205 | return -EFAULT; | 211 | if (kcmsg_base != (struct cmsghdr *)stackbuf) |
| 212 | sock_kfree_s(sk, kcmsg_base, kcmlen); | ||
| 213 | return err; | ||
| 206 | } | 214 | } |
| 207 | 215 | ||
| 208 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) | 216 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) |
