From 80c3c3833271671c8b0851fb8b51402464a4bbdc Mon Sep 17 00:00:00 2001 From: David Weinehall Date: Fri, 23 Nov 2007 15:12:21 -0500 Subject: [PATCH] Linux 2.0.39pre7 o Fix a bug in af_unix that wrote to (Michael Deutschmann) a socket after freeing it (aka the Win9x-related oops) o Fixed typo in Documentation (Martin Douda) --- Documentation/modules.txt | 2 +- net/unix/af_unix.c | 217 ++++++++++++++++++++------------------ 2 files changed, 115 insertions(+), 104 deletions(-) diff --git a/Documentation/modules.txt b/Documentation/modules.txt index 3b57122e7adc..6c8f2acdb227 100644 --- a/Documentation/modules.txt +++ b/Documentation/modules.txt @@ -121,7 +121,7 @@ And, yes, there _are_ man-pages for all this... To use modprobe successfully, you generally place the following command in your /etc/rc.d/rc.S script. (Read more about this in the -"rc.hints" file in the module utilities package, "modules-x.y.z.tar.gz".) +"rc.hints" file in the module utilities package, "modutils-x.y.z.tar.gz".) /sbin/depmod -a diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3bb14b3c0b9b..296e4d02bca7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -29,20 +29,29 @@ * Alan Cox : Started proper garbage collector * Heiko EiBfeldt : Missing verify_area check * Alan Cox : Shutdown() bug + * Michael Deutschmann : release was writing to the socket + * structure after freeing it. * * Known differences from reference BSD that was tested: * * [TO FIX] - * ECONNREFUSED is not returned from one end of a connected() socket to the - * other the moment one end closes. - * fstat() doesn't return st_dev=NODEV, and give the blksize as high water mark - * and a fake inode identifier (nor the BSD first socket fstat twice bug). + * ECONNREFUSED is not returned from one end of a connected() socket + * to the other the moment one end closes. + * + * fstat() doesn't return st_dev=NODEV, and give the blksize as + * high water mark and a fake inode identifier (nor the BSD first socket + * fstat twice bug). + * * [NOT TO FIX] * accept() returns a path name even if the connecting socket has closed - * in the meantime (BSD loses the path and gives up). - * accept() returns 0 length path for an unbound connector. BSD returns 16 - * and a null first byte in the path (but not for gethost/peername - BSD bug ??) + * in the meantime (BSD loses the path and gives up). + * + * accept() returns 0 length path for an unbound connector. BSD returns + * 16 and a null first byte in the path (but not for gethost/peername + * - BSD bug ??) + * * socketpair(...SOCK_RAW..) doesn't panic the kernel. + * * BSD af_unix apparently has connect forgetting to block properly. */ @@ -79,7 +88,7 @@ unix_socket *unix_socket_list=NULL; /* * Make sure the unix name is null-terminated. */ - + static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len) { if (len >= sizeof(*sunaddr)) @@ -92,11 +101,11 @@ static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len) * handler using this technique. They can be added although we do not * use this facility. */ - + static void unix_remove_socket(unix_socket *sk) { unix_socket **s; - + cli(); s=&unix_socket_list; @@ -153,16 +162,16 @@ static void unix_destroy_timer(unsigned long data) sk_free(sk); return; } - + /* * Retry; */ - + sk->timer.expires=jiffies+10*HZ; /* No real hurry try it every 10 seconds or so */ add_timer(&sk->timer); } - - + + static void unix_delayed_delete(unix_socket *sk) { sk->timer.data=(unsigned long)sk; @@ -170,13 +179,15 @@ static void unix_delayed_delete(unix_socket *sk) sk->timer.function=unix_destroy_timer; add_timer(&sk->timer); } - + static void unix_destroy_socket(unix_socket *sk) { struct sk_buff *skb; unix_remove_socket(sk); - + + sk->socket = NULL; + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) { if(sk->state==TCP_LISTEN) @@ -185,7 +196,7 @@ static void unix_destroy_socket(unix_socket *sk) osk->state=TCP_CLOSE; kfree_skb(skb, FREE_WRITE); /* Now surplus - free the skb first before the socket */ osk->state_change(osk); /* So the connect wakes and cleans up (if any) */ - /* osk will be destroyed when it gets to close or the timer fires */ + /* osk will be destroyed when it gets to close or the timer fires */ } else { @@ -193,13 +204,13 @@ static void unix_destroy_socket(unix_socket *sk) kfree_skb(skb,FREE_WRITE); } } - + if(sk->protinfo.af_unix.inode!=NULL) { iput(sk->protinfo.af_unix.inode); sk->protinfo.af_unix.inode=NULL; } - + if(--sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0) { if(sk->protinfo.af_unix.name) @@ -216,7 +227,7 @@ static void unix_destroy_socket(unix_socket *sk) /* * Fixme: We need async I/O on AF_UNIX doing next. */ - + static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) { return -EINVAL; @@ -225,13 +236,13 @@ static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) /* * Yes socket options work with the new unix domain socketry!!!!!!! */ - + static int unix_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { unix_socket *sk=sock->data; if(level!=SOL_SOCKET) return -EOPNOTSUPP; - return sock_setsockopt(sk,level,optname,optval,optlen); + return sock_setsockopt(sk,level,optname,optval,optlen); } static int unix_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) @@ -337,12 +348,12 @@ static int unix_release(struct socket *sock, struct socket *peer) { unix_socket *sk=sock->data; unix_socket *skpair; - + /* May not have data attached */ - + if(sk==NULL) return 0; - + sk->state_change(sk); sk->dead=1; skpair=(unix_socket *)sk->protinfo.af_unix.other; /* Person we send to (default) */ @@ -355,16 +366,16 @@ static int unix_release(struct socket *sock, struct socket *peer) skpair->protinfo.af_unix.locks--; /* It may now die */ sk->protinfo.af_unix.other=NULL; /* No pair */ unix_destroy_socket(sk); /* Try to flush out this socket. Throw out buffers at least */ - unix_gc(); /* Garbage collect fds */ + unix_gc(); /* Garbage collect fds */ /* - * FIXME: BSD difference: In BSD all sockets connected to use get ECONNRESET and we die on the spot. In - * Linux we behave like files and pipes do and wait for the last dereference. + * FIXME: BSD difference: In BSD all sockets connected to use get + * ECONNRESET and we die on the spot. In Linux we behave like files + * and pipes do and wait for the last dereference. */ - + sock->data = NULL; - sk->socket = NULL; - + return 0; } @@ -375,7 +386,7 @@ static unix_socket *unix_find_other(char *path, int *error) int err; struct inode *inode; unix_socket *u; - + old_fs=get_fs(); set_fs(get_ds()); err = open_namei(path, 2, S_IFSOCK, &inode, NULL); @@ -402,10 +413,10 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) unix_socket *sk=sock->data; int old_fs; int err; - + if(sk->protinfo.af_unix.name) return -EINVAL; /* Already bound */ - + if(addr_len>sizeof(struct sockaddr_un) || addr_len<3 || sunaddr->sun_family!=AF_UNIX) return -EINVAL; unix_mkname(sunaddr, addr_len); @@ -414,21 +425,21 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) */ if(sk->protinfo.af_unix.inode!=NULL) return -EINVAL; - + sk->protinfo.af_unix.name=kmalloc(addr_len+1, GFP_KERNEL); if(sk->protinfo.af_unix.name==NULL) return -ENOMEM; memcpy(sk->protinfo.af_unix.name, sunaddr->sun_path, addr_len+1); - + old_fs=get_fs(); set_fs(get_ds()); - + err=do_mknod(sk->protinfo.af_unix.name,S_IFSOCK|S_IRWXUGO,0); if(err==0) err=open_namei(sk->protinfo.af_unix.name, 2, S_IFSOCK, &sk->protinfo.af_unix.inode, NULL); - + set_fs(old_fs); - + if(err<0) { kfree_s(sk->protinfo.af_unix.name,addr_len+1); @@ -438,9 +449,9 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) else return err; } - + return 0; - + } static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) @@ -471,12 +482,12 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le * Drop through the connect up logic to the wait. */ } - + if(addr_len < sizeof(sunaddr->sun_family)+1 || sunaddr->sun_family!=AF_UNIX) return -EINVAL; - + unix_mkname(sunaddr, addr_len); - + if(sk->type==SOCK_DGRAM) { if(sk->protinfo.af_unix.other) @@ -496,14 +507,14 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->state=TCP_ESTABLISHED; return 0; /* Done */ } - + if(sock->state==SS_UNCONNECTED) { /* * Now ready to connect */ - + skb=sock_alloc_send_skb(sk, 0, 0, 0, &err); /* Marker object */ if(skb==NULL) return err; @@ -530,12 +541,12 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->state=TCP_SYN_SENT; sock->state=SS_CONNECTING; sti(); - other->data_ready(other,0); /* Wake up ! */ + other->data_ready(other,0); /* Wake up ! */ } - - + + /* Wait for an accept */ - + cli(); while(sk->state==TCP_SYN_SENT) { @@ -551,11 +562,11 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le return -ERESTARTSYS; } } - + /* * Has the other end closed on us ? */ - + if(sk->state==TCP_CLOSE) { sk->protinfo.af_unix.other->protinfo.af_unix.locks--; @@ -564,21 +575,21 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sti(); return -ECONNREFUSED; } - + /* * Amazingly it has worked */ - + sock->state=SS_CONNECTED; sti(); return 0; - + } static int unix_socketpair(struct socket *a, struct socket *b) { - unix_socket *ska,*skb; - + unix_socket *ska,*skb; + ska=a->data; skb=b->data; @@ -597,7 +608,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags) unix_socket *sk=sock->data; unix_socket *newsk, *tsk; struct sk_buff *skb; - + if(sk->type!=SOCK_STREAM) { return -EOPNOTSUPP; @@ -606,7 +617,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags) { return -EINVAL; } - + newsk=newsock->data; if(sk->protinfo.af_unix.name!=NULL) { @@ -615,7 +626,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags) return -ENOMEM; strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name); } - + do { cli(); @@ -657,7 +668,7 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_ { unix_socket *sk=sock->data; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; - + if(peer) { if(sk->protinfo.af_unix.other==NULL) @@ -679,7 +690,7 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_ /* * Support routines for struct cmsghdr handling */ - + static struct cmsghdr *unix_copyrights(void *userp, int len) /* AT&T ? */ { struct cmsghdr *cm; @@ -694,7 +705,7 @@ static struct cmsghdr *unix_copyrights(void *userp, int len) /* AT&T ? */ /* * Return a header block */ - + static void unix_returnrights(void *userp, int len, struct cmsghdr *cm) { memcpy_tofs(userp, cm, len); @@ -705,7 +716,7 @@ static void unix_returnrights(void *userp, int len, struct cmsghdr *cm) * Copy file descriptors into system space. * Return number copied or negative error code */ - + static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp) { int num=cmsg->cmsg_len-sizeof(struct cmsghdr); @@ -715,22 +726,22 @@ static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp) num /= sizeof(int); /* Odd bytes are forgotten in BSD not errored */ if (num >= UNIX_MAX_FD) return -EINVAL; - + /* * Verify the descriptors. */ - + for(i=0; i< num; i++) { int fd; - - fd = fdp[i]; + + fd = fdp[i]; if (fd < 0 || fd >= NR_OPEN) return -EBADF; if (current->files->fd[fd]==NULL) return -EBADF; } - + /* add another reference to these files */ for(i=0; i< num; i++) { @@ -738,7 +749,7 @@ static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp) fp[i]->f_count++; unix_inflight(fp[i]); } - + return num; } @@ -778,7 +789,7 @@ static void unix_detach_fds(struct sk_buff *skb, struct cmsghdr *cmsg) cmnum = (cmsg->cmsg_len-sizeof(struct cmsghdr)) / sizeof(int); cmfptr = (int *)&cmsg->cmsg_data; } - + fdnum = *(int *)skb->h.filp; fp = (struct file **)(skb->h.filp+sizeof(long)); @@ -816,7 +827,7 @@ static void unix_destruct_fds(struct sk_buff *skb) { unix_detach_fds(skb,NULL); } - + /* * Attach the file descriptor array to an sk_buff */ @@ -835,7 +846,7 @@ static void unix_attach_fds(int fpnum,struct file **fp,struct sk_buff *skb) /* * Send AF_UNIX data. */ - + static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) { unix_socket *sk=sock->data; @@ -856,17 +867,17 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no if(flags&MSG_OOB) return -EOPNOTSUPP; - + if(flags) /* For now */ { return -EINVAL; } - + if(sk->shutdown&SEND_SHUTDOWN) { send_sig(SIGPIPE,current,0); return -EPIPE; } - + if(sunaddr!=NULL) { if(sock->type==SOCK_STREAM) @@ -887,9 +898,9 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no /* * A control message has been attached. */ - if(msg->msg_control) + if(msg->msg_control) { - struct cmsghdr *cm=unix_copyrights(msg->msg_control, + struct cmsghdr *cm=unix_copyrights(msg->msg_control, msg->msg_controllen); if(cm==NULL || msg->msg_controllencmsg_type!=SCM_RIGHTS || @@ -897,7 +908,7 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no msg->msg_controllen!=cm->cmsg_len) { kfree(cm); - return -EINVAL; + return -EINVAL; } fpnum=unix_fd_copy(sk,cm,fp); kfree(cm); @@ -909,10 +920,10 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no while(sent < len) { /* - * Optimisation for the fact that under 0.01% of X messages typically - * need breaking up. + * Optimisation for the fact that under 0.01% of X messages + * typically need breaking up. */ - + size=len-sent; if(size>(sk->sndbuf-sizeof(struct sk_buff))/2) /* Keep two messages in the pipe so it schedules better */ @@ -938,9 +949,9 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no /* * Grab a buffer */ - + skb=sock_alloc_send_skb(sk,size,limit,nonblock, &err); - + if(skb==NULL) { unix_fd_free(sk,fp,fpnum); @@ -955,7 +966,7 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no skb->sk=sk; skb->free=1; - + if(fpnum) { unix_attach_fds(fpnum,fp,skb); @@ -1026,7 +1037,7 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no /* * Sleep until data has arrive. But check for races.. */ - + static void unix_data_wait(unix_socket * sk) { cli(); @@ -1053,19 +1064,19 @@ static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n if(flags&MSG_OOB) return -EOPNOTSUPP; - + if(addr_len) *addr_len=0; - + if(sk->err) return sock_error(sk); - if(msg->msg_control) + if(msg->msg_control) { - cm=unix_copyrights(msg->msg_control, + cm=unix_copyrights(msg->msg_control, msg->msg_controllen); if(msg->msg_controllencmsg_type!=SCM_RIGHTS || @@ -1076,10 +1087,10 @@ static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n { kfree(cm); /* printk("recvmsg: Bad msg_control\n");*/ - return -EINVAL; + return -EINVAL; } } - + down(&sk->protinfo.af_unix.readsem); /* Lock the socket */ while(ct--) { @@ -1087,7 +1098,7 @@ static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n sp=iov->iov_base; len=iov->iov_len; iov++; - + while(donedata; unix_socket *other=sk->protinfo.af_unix.other; - + mode++; - + if(mode&SEND_SHUTDOWN) { sk->shutdown|=SEND_SHUTDOWN; @@ -1183,7 +1194,7 @@ static int unix_shutdown(struct socket *sock, int mode) return 0; } - + static int unix_select(struct socket *sock, int sel_type, select_table *wait) { return datagram_select(sock->data,sel_type,wait); @@ -1194,10 +1205,10 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) unix_socket *sk=sock->data; int err; long amount=0; - + switch(cmd) { - + case TIOCOUTQ: err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); if(err) @@ -1236,10 +1247,10 @@ static int unix_get_info(char *buffer, char **start, off_t offset, int length, i off_t begin=0; int len=0; unix_socket *s=unix_socket_list; - + len+= sprintf(buffer,"Num RefCount Protocol Flags Type St " "Inode Path\n"); - + while(s!=NULL) { len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld", @@ -1254,7 +1265,7 @@ static int unix_get_info(char *buffer, char **start, off_t offset, int length, i len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name); else buffer[len++]='\n'; - + pos=begin+len; if(pos