aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2009-04-23 19:32:25 -0400
committerJ. Bruce Fields <bfields@citi.umich.edu>2009-04-28 13:54:28 -0400
commit335c54bdc4d3bacdbd619ec95cd0b352435bd37f (patch)
tree922de1595031ab24a31ba263c1f07bf876797b05
parentea068bad27cefc71ab03230dbf01a8f8d98da5ba (diff)
NFSD: Prevent a buffer overflow in svc_xprt_names()
The svc_xprt_names() function can overflow its buffer if it's so near the end of the passed in buffer that the "name too long" string still doesn't fit. Of course, it could never tell if it was near the end of the passed in buffer, since its only caller passes in zero as the buffer length. Let's make this API a little safer. Change svc_xprt_names() so it *always* checks for a buffer overflow, and change its only caller to pass in the correct buffer length. If svc_xprt_names() does overflow its buffer, it now fails with an ENAMETOOLONG errno, instead of trying to write a message at the end of the buffer. I don't like this much, but I can't figure out a clean way that's always safe to return some of the names, *and* an indication that the buffer was not long enough. The displayed error when doing a 'cat /proc/fs/nfsd/portlist' is "File name too long". Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r--fs/nfsd/nfsctl.c2
-rw-r--r--include/linux/sunrpc/svc_xprt.h2
-rw-r--r--net/sunrpc/svc_xprt.c56
3 files changed, 41 insertions, 19 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index e051847b93fb..6a1cd908e6bc 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -918,7 +918,7 @@ static ssize_t __write_ports_names(char *buf)
918{ 918{
919 if (nfsd_serv == NULL) 919 if (nfsd_serv == NULL)
920 return 0; 920 return 0;
921 return svc_xprt_names(nfsd_serv, buf, 0); 921 return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
922} 922}
923 923
924/* 924/*
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index d790c52525cc..2223ae0b5ed5 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -83,7 +83,7 @@ int svc_port_is_privileged(struct sockaddr *sin);
83int svc_print_xprts(char *buf, int maxlen); 83int svc_print_xprts(char *buf, int maxlen);
84struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, 84struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
85 const sa_family_t af, const unsigned short port); 85 const sa_family_t af, const unsigned short port);
86int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen); 86int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen);
87 87
88static inline void svc_xprt_get(struct svc_xprt *xprt) 88static inline void svc_xprt_get(struct svc_xprt *xprt)
89{ 89{
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index f200393ac877..6f33d33cc064 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -1098,36 +1098,58 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
1098} 1098}
1099EXPORT_SYMBOL_GPL(svc_find_xprt); 1099EXPORT_SYMBOL_GPL(svc_find_xprt);
1100 1100
1101/* 1101static int svc_one_xprt_name(const struct svc_xprt *xprt,
1102 * Format a buffer with a list of the active transports. A zero for 1102 char *pos, int remaining)
1103 * the buflen parameter disables target buffer overflow checking. 1103{
1104 int len;
1105
1106 len = snprintf(pos, remaining, "%s %u\n",
1107 xprt->xpt_class->xcl_name,
1108 svc_xprt_local_port(xprt));
1109 if (len >= remaining)
1110 return -ENAMETOOLONG;
1111 return len;
1112}
1113
1114/**
1115 * svc_xprt_names - format a buffer with a list of transport names
1116 * @serv: pointer to an RPC service
1117 * @buf: pointer to a buffer to be filled in
1118 * @buflen: length of buffer to be filled in
1119 *
1120 * Fills in @buf with a string containing a list of transport names,
1121 * each name terminated with '\n'.
1122 *
1123 * Returns positive length of the filled-in string on success; otherwise
1124 * a negative errno value is returned if an error occurs.
1104 */ 1125 */
1105int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen) 1126int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen)
1106{ 1127{
1107 struct svc_xprt *xprt; 1128 struct svc_xprt *xprt;
1108 char xprt_str[64]; 1129 int len, totlen;
1109 int totlen = 0; 1130 char *pos;
1110 int len;
1111 1131
1112 /* Sanity check args */ 1132 /* Sanity check args */
1113 if (!serv) 1133 if (!serv)
1114 return 0; 1134 return 0;
1115 1135
1116 spin_lock_bh(&serv->sv_lock); 1136 spin_lock_bh(&serv->sv_lock);
1137
1138 pos = buf;
1139 totlen = 0;
1117 list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { 1140 list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
1118 len = snprintf(xprt_str, sizeof(xprt_str), 1141 len = svc_one_xprt_name(xprt, pos, buflen - totlen);
1119 "%s %d\n", xprt->xpt_class->xcl_name, 1142 if (len < 0) {
1120 svc_xprt_local_port(xprt)); 1143 *buf = '\0';
1121 /* If the string was truncated, replace with error string */ 1144 totlen = len;
1122 if (len >= sizeof(xprt_str)) 1145 }
1123 strcpy(xprt_str, "name-too-long\n"); 1146 if (len <= 0)
1124 /* Don't overflow buffer */
1125 len = strlen(xprt_str);
1126 if (buflen && (len + totlen >= buflen))
1127 break; 1147 break;
1128 strcpy(buf+totlen, xprt_str); 1148
1149 pos += len;
1129 totlen += len; 1150 totlen += len;
1130 } 1151 }
1152
1131 spin_unlock_bh(&serv->sv_lock); 1153 spin_unlock_bh(&serv->sv_lock);
1132 return totlen; 1154 return totlen;
1133} 1155}