diff options
Diffstat (limited to 'arch/sparc64/solaris/socket.c')
| -rw-r--r-- | arch/sparc64/solaris/socket.c | 193 |
1 files changed, 119 insertions, 74 deletions
diff --git a/arch/sparc64/solaris/socket.c b/arch/sparc64/solaris/socket.c index 06740582717e..d3a66ea74a7f 100644 --- a/arch/sparc64/solaris/socket.c +++ b/arch/sparc64/solaris/socket.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/net.h> | 16 | #include <linux/net.h> |
| 17 | #include <linux/compat.h> | 17 | #include <linux/compat.h> |
| 18 | #include <net/compat.h> | 18 | #include <net/compat.h> |
| 19 | #include <net/sock.h> | ||
| 19 | 20 | ||
| 20 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
| 21 | #include <asm/string.h> | 22 | #include <asm/string.h> |
| @@ -297,121 +298,165 @@ asmlinkage int solaris_sendmsg(int fd, struct sol_nmsghdr __user *user_msg, unsi | |||
| 297 | { | 298 | { |
| 298 | struct socket *sock; | 299 | struct socket *sock; |
| 299 | char address[MAX_SOCK_ADDR]; | 300 | char address[MAX_SOCK_ADDR]; |
| 300 | struct iovec iov[UIO_FASTIOV]; | 301 | struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; |
| 301 | unsigned char ctl[sizeof(struct cmsghdr) + 20]; | 302 | unsigned char ctl[sizeof(struct cmsghdr) + 20]; |
| 302 | unsigned char *ctl_buf = ctl; | 303 | unsigned char *ctl_buf = ctl; |
| 303 | struct msghdr kern_msg; | 304 | struct msghdr msg_sys; |
| 304 | int err, total_len; | 305 | int err, ctl_len, iov_size, total_len; |
| 305 | 306 | ||
| 306 | if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) | 307 | err = -EFAULT; |
| 307 | return -EFAULT; | 308 | if (msghdr_from_user32_to_kern(&msg_sys, user_msg)) |
| 308 | if(kern_msg.msg_iovlen > UIO_MAXIOV) | 309 | goto out; |
| 309 | return -EINVAL; | 310 | |
| 310 | err = verify_compat_iovec(&kern_msg, iov, address, VERIFY_READ); | 311 | sock = sockfd_lookup(fd, &err); |
| 311 | if (err < 0) | 312 | if (!sock) |
| 312 | goto out; | 313 | goto out; |
| 314 | |||
| 315 | /* do not move before msg_sys is valid */ | ||
| 316 | err = -EMSGSIZE; | ||
| 317 | if (msg_sys.msg_iovlen > UIO_MAXIOV) | ||
| 318 | goto out_put; | ||
| 319 | |||
| 320 | /* Check whether to allocate the iovec area*/ | ||
| 321 | err = -ENOMEM; | ||
| 322 | iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); | ||
| 323 | if (msg_sys.msg_iovlen > UIO_FASTIOV) { | ||
| 324 | iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); | ||
| 325 | if (!iov) | ||
| 326 | goto out_put; | ||
| 327 | } | ||
| 328 | |||
| 329 | err = verify_compat_iovec(&msg_sys, iov, address, VERIFY_READ); | ||
| 330 | if (err < 0) | ||
| 331 | goto out_freeiov; | ||
| 313 | total_len = err; | 332 | total_len = err; |
| 314 | 333 | ||
| 315 | if(kern_msg.msg_controllen) { | 334 | err = -ENOBUFS; |
| 316 | struct sol_cmsghdr __user *ucmsg = kern_msg.msg_control; | 335 | if (msg_sys.msg_controllen > INT_MAX) |
| 336 | goto out_freeiov; | ||
| 337 | |||
| 338 | ctl_len = msg_sys.msg_controllen; | ||
| 339 | if (ctl_len) { | ||
| 340 | struct sol_cmsghdr __user *ucmsg = msg_sys.msg_control; | ||
| 317 | unsigned long *kcmsg; | 341 | unsigned long *kcmsg; |
| 318 | compat_size_t cmlen; | 342 | compat_size_t cmlen; |
| 319 | 343 | ||
| 320 | if (kern_msg.msg_controllen <= sizeof(compat_size_t)) | 344 | err = -EINVAL; |
| 321 | return -EINVAL; | 345 | if (ctl_len <= sizeof(compat_size_t)) |
| 346 | goto out_freeiov; | ||
| 322 | 347 | ||
| 323 | if(kern_msg.msg_controllen > sizeof(ctl)) { | 348 | if (ctl_len > sizeof(ctl)) { |
| 324 | err = -ENOBUFS; | 349 | err = -ENOBUFS; |
| 325 | ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); | 350 | ctl_buf = kmalloc(ctl_len, GFP_KERNEL); |
| 326 | if(!ctl_buf) | 351 | if (!ctl_buf) |
| 327 | goto out_freeiov; | 352 | goto out_freeiov; |
| 328 | } | 353 | } |
| 329 | __get_user(cmlen, &ucmsg->cmsg_len); | 354 | __get_user(cmlen, &ucmsg->cmsg_len); |
| 330 | kcmsg = (unsigned long *) ctl_buf; | 355 | kcmsg = (unsigned long *) ctl_buf; |
| 331 | *kcmsg++ = (unsigned long)cmlen; | 356 | *kcmsg++ = (unsigned long)cmlen; |
| 332 | err = -EFAULT; | 357 | err = -EFAULT; |
| 333 | if(copy_from_user(kcmsg, &ucmsg->cmsg_level, | 358 | if (copy_from_user(kcmsg, &ucmsg->cmsg_level, |
| 334 | kern_msg.msg_controllen - sizeof(compat_size_t))) | 359 | ctl_len - sizeof(compat_size_t))) |
| 335 | goto out_freectl; | 360 | goto out_freectl; |
| 336 | kern_msg.msg_control = ctl_buf; | 361 | msg_sys.msg_control = ctl_buf; |
| 337 | } | 362 | } |
| 338 | kern_msg.msg_flags = solaris_to_linux_msgflags(user_flags); | 363 | msg_sys.msg_flags = solaris_to_linux_msgflags(user_flags); |
| 339 | 364 | ||
| 340 | lock_kernel(); | 365 | if (sock->file->f_flags & O_NONBLOCK) |
| 341 | sock = sockfd_lookup(fd, &err); | 366 | msg_sys.msg_flags |= MSG_DONTWAIT; |
| 342 | if (sock != NULL) { | 367 | err = sock_sendmsg(sock, &msg_sys, total_len); |
| 343 | if (sock->file->f_flags & O_NONBLOCK) | ||
| 344 | kern_msg.msg_flags |= MSG_DONTWAIT; | ||
| 345 | err = sock_sendmsg(sock, &kern_msg, total_len); | ||
| 346 | sockfd_put(sock); | ||
| 347 | } | ||
| 348 | unlock_kernel(); | ||
| 349 | 368 | ||
| 350 | out_freectl: | 369 | out_freectl: |
| 351 | /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ | 370 | if (ctl_buf != ctl) |
| 352 | if(ctl_buf != ctl) | 371 | sock_kfree_s(sock->sk, ctl_buf, ctl_len); |
| 353 | kfree(ctl_buf); | ||
| 354 | out_freeiov: | 372 | out_freeiov: |
| 355 | if(kern_msg.msg_iov != iov) | 373 | if (iov != iovstack) |
| 356 | kfree(kern_msg.msg_iov); | 374 | sock_kfree_s(sock->sk, iov, iov_size); |
| 357 | out: | 375 | out_put: |
| 376 | sockfd_put(sock); | ||
| 377 | out: | ||
| 358 | return err; | 378 | return err; |
| 359 | } | 379 | } |
| 360 | 380 | ||
| 361 | asmlinkage int solaris_recvmsg(int fd, struct sol_nmsghdr __user *user_msg, unsigned int user_flags) | 381 | asmlinkage int solaris_recvmsg(int fd, struct sol_nmsghdr __user *user_msg, unsigned int user_flags) |
| 362 | { | 382 | { |
| 363 | struct iovec iovstack[UIO_FASTIOV]; | ||
| 364 | struct msghdr kern_msg; | ||
| 365 | char addr[MAX_SOCK_ADDR]; | ||
| 366 | struct socket *sock; | 383 | struct socket *sock; |
| 384 | struct iovec iovstack[UIO_FASTIOV]; | ||
| 367 | struct iovec *iov = iovstack; | 385 | struct iovec *iov = iovstack; |
| 386 | struct msghdr msg_sys; | ||
| 387 | unsigned long cmsg_ptr; | ||
| 388 | int err, iov_size, total_len, len; | ||
| 389 | |||
| 390 | /* kernel mode address */ | ||
| 391 | char addr[MAX_SOCK_ADDR]; | ||
| 392 | |||
| 393 | /* user mode address pointers */ | ||
| 368 | struct sockaddr __user *uaddr; | 394 | struct sockaddr __user *uaddr; |
| 369 | int __user *uaddr_len; | 395 | int __user *uaddr_len; |
| 370 | unsigned long cmsg_ptr; | ||
| 371 | int err, total_len, len = 0; | ||
| 372 | 396 | ||
| 373 | if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) | 397 | if (msghdr_from_user32_to_kern(&msg_sys, user_msg)) |
| 374 | return -EFAULT; | 398 | return -EFAULT; |
| 375 | if(kern_msg.msg_iovlen > UIO_MAXIOV) | ||
| 376 | return -EINVAL; | ||
| 377 | 399 | ||
| 378 | uaddr = kern_msg.msg_name; | 400 | sock = sockfd_lookup(fd, &err); |
| 401 | if (!sock) | ||
| 402 | goto out; | ||
| 403 | |||
| 404 | err = -EMSGSIZE; | ||
| 405 | if (msg_sys.msg_iovlen > UIO_MAXIOV) | ||
| 406 | goto out_put; | ||
| 407 | |||
| 408 | /* Check whether to allocate the iovec area*/ | ||
| 409 | err = -ENOMEM; | ||
| 410 | iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); | ||
| 411 | if (msg_sys.msg_iovlen > UIO_FASTIOV) { | ||
| 412 | iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); | ||
| 413 | if (!iov) | ||
| 414 | goto out_put; | ||
| 415 | } | ||
| 416 | |||
| 417 | /* | ||
| 418 | * Save the user-mode address (verify_iovec will change the | ||
| 419 | * kernel msghdr to use the kernel address space) | ||
| 420 | */ | ||
| 421 | |||
| 422 | uaddr = (void __user *) msg_sys.msg_name; | ||
| 379 | uaddr_len = &user_msg->msg_namelen; | 423 | uaddr_len = &user_msg->msg_namelen; |
| 380 | err = verify_compat_iovec(&kern_msg, iov, addr, VERIFY_WRITE); | 424 | err = verify_compat_iovec(&msg_sys, iov, addr, VERIFY_WRITE); |
| 381 | if (err < 0) | 425 | if (err < 0) |
| 382 | goto out; | 426 | goto out_freeiov; |
| 383 | total_len = err; | 427 | total_len = err; |
| 384 | 428 | ||
| 385 | cmsg_ptr = (unsigned long) kern_msg.msg_control; | 429 | cmsg_ptr = (unsigned long) msg_sys.msg_control; |
| 386 | kern_msg.msg_flags = 0; | 430 | msg_sys.msg_flags = MSG_CMSG_COMPAT; |
| 387 | 431 | ||
| 388 | lock_kernel(); | 432 | if (sock->file->f_flags & O_NONBLOCK) |
| 389 | sock = sockfd_lookup(fd, &err); | 433 | user_flags |= MSG_DONTWAIT; |
| 390 | if (sock != NULL) { | 434 | |
| 391 | if (sock->file->f_flags & O_NONBLOCK) | 435 | err = sock_recvmsg(sock, &msg_sys, total_len, user_flags); |
| 392 | user_flags |= MSG_DONTWAIT; | 436 | if(err < 0) |
| 393 | err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); | 437 | goto out_freeiov; |
| 394 | if(err >= 0) | 438 | |
| 395 | len = err; | 439 | len = err; |
| 396 | sockfd_put(sock); | 440 | |
| 397 | } | 441 | if (uaddr != NULL) { |
| 398 | unlock_kernel(); | 442 | err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len); |
| 399 | 443 | if (err < 0) | |
| 400 | if(uaddr != NULL && err >= 0) | 444 | goto out_freeiov; |
| 401 | err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); | ||
| 402 | if(err >= 0) { | ||
| 403 | err = __put_user(linux_to_solaris_msgflags(kern_msg.msg_flags), &user_msg->msg_flags); | ||
| 404 | if(!err) { | ||
| 405 | /* XXX Convert cmsg back into userspace 32-bit format... */ | ||
| 406 | err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, | ||
| 407 | &user_msg->msg_controllen); | ||
| 408 | } | ||
| 409 | } | 445 | } |
| 446 | err = __put_user(linux_to_solaris_msgflags(msg_sys.msg_flags), &user_msg->msg_flags); | ||
| 447 | if (err) | ||
| 448 | goto out_freeiov; | ||
| 449 | err = __put_user((unsigned long)msg_sys.msg_control - cmsg_ptr, | ||
| 450 | &user_msg->msg_controllen); | ||
| 451 | if (err) | ||
| 452 | goto out_freeiov; | ||
| 453 | err = len; | ||
| 410 | 454 | ||
| 411 | if(kern_msg.msg_iov != iov) | 455 | out_freeiov: |
| 412 | kfree(kern_msg.msg_iov); | 456 | if (iov != iovstack) |
| 457 | sock_kfree_s(sock->sk, iov, iov_size); | ||
| 458 | out_put: | ||
| 459 | sockfd_put(sock); | ||
| 413 | out: | 460 | out: |
| 414 | if(err < 0) | 461 | return err; |
| 415 | return err; | ||
| 416 | return len; | ||
| 417 | } | 462 | } |
