diff mbox

[1/1] nfs-utils/statd: fix a segfault caused by improper usage of RPC interface

Message ID 1442989474-211924-2-git-send-email-shan.hai@windriver.com (mailing list archive)
State New, archived
Headers show

Commit Message

haishan Sept. 23, 2015, 6:24 a.m. UTC
There is a hack which uses the bottom-level RPC improperly as below
in the current statd implementation:
insert a socket in the svc_fdset without a corresponding transport handle
and passes the socket to the svc_getreqset subroutine, this usage causes
a segfault of statd on a huge amount of sm-notifications.

Fix the issue by separating the non-RPC-server socket from RPC dispatcher.

Below is the segfault:
Core was generated by `/usr/sbin/rpc.statd --name=master --port 32765 --outgoing-port 32766'.

Program terminated with signal 11, Segmentation fault.
0 __GI_svc_getreq_common (fd=<optimized out>) at svc.c:492
warning: Source file is more recent than executable.
492 if (SVC_RECV (xprt, &msg))

(gdb) bt
0 __GI_svc_getreq_common (fd=<optimized out>) at svc.c:492
1 0x0000003af9f13a82 in __GI_svc_getreqset (readfds=<optimized out>) at svc.c:439
2 0x0000000000404e64 in my_svc_run () at svc_run.c:129
3 0x00000000004035dd in main (argc=6, argv=<optimized out>) at statd.c:472

(gdb) info frame 1
Stack frame at 0x7fffdd256a40:
rip = 0x3af9f13a82 in __GI_svc_getreqset (svc.c:439); saved rip 0x404e64
called by frame at 0x7fffdd256b00, caller of frame at 0x7fffdd256a00

source language c.

Arglist at 0x7fffdd2569f8, args: readfds=<optimized out>
Locals at 0x7fffdd2569f8, Previous frame's sp is 0x7fffdd256a40

Signed-off-by: Shan Hai <shan.hai@windriver.com>
---
 utils/statd/rmtcall.c | 1 -
 utils/statd/statd.c   | 5 +++--
 utils/statd/statd.h   | 2 +-
 utils/statd/svc_run.c | 8 ++++++--
 4 files changed, 10 insertions(+), 6 deletions(-)

Comments

Steve Dickson Nov. 4, 2015, 9:47 p.m. UTC | #1
On 09/23/2015 02:24 AM, Shan Hai wrote:
> There is a hack which uses the bottom-level RPC improperly as below
> in the current statd implementation:
> insert a socket in the svc_fdset without a corresponding transport handle
> and passes the socket to the svc_getreqset subroutine, this usage causes
> a segfault of statd on a huge amount of sm-notifications.
> 
> Fix the issue by separating the non-RPC-server socket from RPC dispatcher.
> 
> Below is the segfault:
> Core was generated by `/usr/sbin/rpc.statd --name=master --port 32765 --outgoing-port 32766'.
> 
> Program terminated with signal 11, Segmentation fault.
> 0 __GI_svc_getreq_common (fd=<optimized out>) at svc.c:492
> warning: Source file is more recent than executable.
> 492 if (SVC_RECV (xprt, &msg))
> 
> (gdb) bt
> 0 __GI_svc_getreq_common (fd=<optimized out>) at svc.c:492
> 1 0x0000003af9f13a82 in __GI_svc_getreqset (readfds=<optimized out>) at svc.c:439
> 2 0x0000000000404e64 in my_svc_run () at svc_run.c:129
> 3 0x00000000004035dd in main (argc=6, argv=<optimized out>) at statd.c:472
> 
> (gdb) info frame 1
> Stack frame at 0x7fffdd256a40:
> rip = 0x3af9f13a82 in __GI_svc_getreqset (svc.c:439); saved rip 0x404e64
> called by frame at 0x7fffdd256b00, caller of frame at 0x7fffdd256a00
> 
> source language c.
> 
> Arglist at 0x7fffdd2569f8, args: readfds=<optimized out>
> Locals at 0x7fffdd2569f8, Previous frame's sp is 0x7fffdd256a40
> 
> Signed-off-by: Shan Hai <shan.hai@windriver.com>
Committed... 

steved.

> ---
>  utils/statd/rmtcall.c | 1 -
>  utils/statd/statd.c   | 5 +++--
>  utils/statd/statd.h   | 2 +-
>  utils/statd/svc_run.c | 8 ++++++--
>  4 files changed, 10 insertions(+), 6 deletions(-)
> 
> diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
> index 45c84f9..c4f6364 100644
> --- a/utils/statd/rmtcall.c
> +++ b/utils/statd/rmtcall.c
> @@ -113,7 +113,6 @@ statd_get_socket(void)
>  	if (sockfd < 0)
>  		return -1;
>  
> -	FD_SET(sockfd, &SVC_FDSET);
>  	return sockfd;
>  }
>  
> diff --git a/utils/statd/statd.c b/utils/statd/statd.c
> index 2b7a167..e5b4c98 100644
> --- a/utils/statd/statd.c
> +++ b/utils/statd/statd.c
> @@ -247,6 +247,7 @@ int main (int argc, char **argv)
>  	int port = 0, out_port = 0;
>  	int nlm_udp = 0, nlm_tcp = 0;
>  	struct rlimit rlim;
> +	int notify_sockfd;
>  
>  	/* Default: daemon mode, no other options */
>  	run_mode = 0;
> @@ -437,7 +438,7 @@ int main (int argc, char **argv)
>  		}
>  
>  	/* Make sure we have a privilege port for calling into the kernel */
> -	if (statd_get_socket() < 0)
> +	if ((notify_sockfd = statd_get_socket()) < 0)
>  		exit(1);
>  
>  	/* If sm-notify didn't take all the state files, load
> @@ -484,7 +485,7 @@ int main (int argc, char **argv)
>  		 * Handle incoming requests:  SM_NOTIFY socket requests, as
>  		 * well as callbacks from lockd.
>  		 */
> -		my_svc_run();	/* I rolled my own, Olaf made it better... */
> +		my_svc_run(notify_sockfd);	/* I rolled my own, Olaf made it better... */
>  
>  		/* Only get here when simulating a crash so we should probably
>  		 * start sm-notify running again.  As we have already dropped
> diff --git a/utils/statd/statd.h b/utils/statd/statd.h
> index a1d8035..231ac7e 100644
> --- a/utils/statd/statd.h
> +++ b/utils/statd/statd.h
> @@ -28,7 +28,7 @@ extern _Bool	statd_present_address(const struct sockaddr *sap, char *buf,
>  __attribute__((__malloc__))
>  extern char *	statd_canonical_name(const char *hostname);
>  
> -extern void	my_svc_run(void);
> +extern void	my_svc_run(int);
>  extern void	notify_hosts(void);
>  extern void	shuffle_dirs(void);
>  extern int	statd_get_socket(void);
> diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
> index d98ecee..28c1ad6 100644
> --- a/utils/statd/svc_run.c
> +++ b/utils/statd/svc_run.c
> @@ -78,7 +78,7 @@ my_svc_exit(void)
>   * The heart of the server.  A crib from libc for the most part...
>   */
>  void
> -my_svc_run(void)
> +my_svc_run(int sockfd)
>  {
>  	FD_SET_TYPE	readfds;
>  	int             selret;
> @@ -96,6 +96,8 @@ my_svc_run(void)
>  		}
>  
>  		readfds = SVC_FDSET;
> +		/* Set notify sockfd for waiting for reply */
> +		FD_SET(sockfd, &readfds);
>  		if (notify) {
>  			struct timeval	tv;
>  
> @@ -125,8 +127,10 @@ my_svc_run(void)
>  
>  		default:
>  			selret -= process_reply(&readfds);
> -			if (selret)
> +			if (selret) {
> +				FD_CLR(sockfd, &readfds);
>  				svc_getreqset(&readfds);
> +			}
>  		}
>  	}
>  }
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
index 45c84f9..c4f6364 100644
--- a/utils/statd/rmtcall.c
+++ b/utils/statd/rmtcall.c
@@ -113,7 +113,6 @@  statd_get_socket(void)
 	if (sockfd < 0)
 		return -1;
 
-	FD_SET(sockfd, &SVC_FDSET);
 	return sockfd;
 }
 
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 2b7a167..e5b4c98 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -247,6 +247,7 @@  int main (int argc, char **argv)
 	int port = 0, out_port = 0;
 	int nlm_udp = 0, nlm_tcp = 0;
 	struct rlimit rlim;
+	int notify_sockfd;
 
 	/* Default: daemon mode, no other options */
 	run_mode = 0;
@@ -437,7 +438,7 @@  int main (int argc, char **argv)
 		}
 
 	/* Make sure we have a privilege port for calling into the kernel */
-	if (statd_get_socket() < 0)
+	if ((notify_sockfd = statd_get_socket()) < 0)
 		exit(1);
 
 	/* If sm-notify didn't take all the state files, load
@@ -484,7 +485,7 @@  int main (int argc, char **argv)
 		 * Handle incoming requests:  SM_NOTIFY socket requests, as
 		 * well as callbacks from lockd.
 		 */
-		my_svc_run();	/* I rolled my own, Olaf made it better... */
+		my_svc_run(notify_sockfd);	/* I rolled my own, Olaf made it better... */
 
 		/* Only get here when simulating a crash so we should probably
 		 * start sm-notify running again.  As we have already dropped
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index a1d8035..231ac7e 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -28,7 +28,7 @@  extern _Bool	statd_present_address(const struct sockaddr *sap, char *buf,
 __attribute__((__malloc__))
 extern char *	statd_canonical_name(const char *hostname);
 
-extern void	my_svc_run(void);
+extern void	my_svc_run(int);
 extern void	notify_hosts(void);
 extern void	shuffle_dirs(void);
 extern int	statd_get_socket(void);
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
index d98ecee..28c1ad6 100644
--- a/utils/statd/svc_run.c
+++ b/utils/statd/svc_run.c
@@ -78,7 +78,7 @@  my_svc_exit(void)
  * The heart of the server.  A crib from libc for the most part...
  */
 void
-my_svc_run(void)
+my_svc_run(int sockfd)
 {
 	FD_SET_TYPE	readfds;
 	int             selret;
@@ -96,6 +96,8 @@  my_svc_run(void)
 		}
 
 		readfds = SVC_FDSET;
+		/* Set notify sockfd for waiting for reply */
+		FD_SET(sockfd, &readfds);
 		if (notify) {
 			struct timeval	tv;
 
@@ -125,8 +127,10 @@  my_svc_run(void)
 
 		default:
 			selret -= process_reply(&readfds);
-			if (selret)
+			if (selret) {
+				FD_CLR(sockfd, &readfds);
 				svc_getreqset(&readfds);
+			}
 		}
 	}
 }