diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 13:16:24 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:19 -0400 |
commit | 7e06b53d796a3740307b54aa2799077f8a0c84e7 (patch) | |
tree | 95c00126400914bfd68f84ed7669272e581e9386 | |
parent | e053d1ab62c8ef0eff3dd4c95448cad3c6d2fbf4 (diff) |
[PATCH] RPC: fix accounting bug in the case of a truncated RPC message
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | include/linux/sunrpc/xdr.h | 2 | ||||
-rw-r--r-- | net/sunrpc/xdr.c | 22 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 35 |
3 files changed, 42 insertions, 17 deletions
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 0f5b7a5a7432..5d1eed2b58a1 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h | |||
@@ -160,7 +160,7 @@ typedef struct { | |||
160 | 160 | ||
161 | typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); | 161 | typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); |
162 | 162 | ||
163 | extern int xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, | 163 | extern ssize_t xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, |
164 | skb_reader_t *, skb_read_actor_t); | 164 | skb_reader_t *, skb_read_actor_t); |
165 | 165 | ||
166 | struct socket; | 166 | struct socket; |
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 65b268d39782..b3ac3f72bf9c 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -176,21 +176,23 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, | |||
176 | xdr->buflen += len; | 176 | xdr->buflen += len; |
177 | } | 177 | } |
178 | 178 | ||
179 | int | 179 | ssize_t |
180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | 180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, |
181 | skb_reader_t *desc, | 181 | skb_reader_t *desc, |
182 | skb_read_actor_t copy_actor) | 182 | skb_read_actor_t copy_actor) |
183 | { | 183 | { |
184 | struct page **ppage = xdr->pages; | 184 | struct page **ppage = xdr->pages; |
185 | unsigned int len, pglen = xdr->page_len; | 185 | unsigned int len, pglen = xdr->page_len; |
186 | ssize_t copied = 0; | ||
186 | int ret; | 187 | int ret; |
187 | 188 | ||
188 | len = xdr->head[0].iov_len; | 189 | len = xdr->head[0].iov_len; |
189 | if (base < len) { | 190 | if (base < len) { |
190 | len -= base; | 191 | len -= base; |
191 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); | 192 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); |
193 | copied += ret; | ||
192 | if (ret != len || !desc->count) | 194 | if (ret != len || !desc->count) |
193 | return 0; | 195 | goto out; |
194 | base = 0; | 196 | base = 0; |
195 | } else | 197 | } else |
196 | base -= len; | 198 | base -= len; |
@@ -214,8 +216,11 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
214 | * are small by default but can get huge. */ | 216 | * are small by default but can get huge. */ |
215 | if (unlikely(*ppage == NULL)) { | 217 | if (unlikely(*ppage == NULL)) { |
216 | *ppage = alloc_page(GFP_ATOMIC); | 218 | *ppage = alloc_page(GFP_ATOMIC); |
217 | if (unlikely(*ppage == NULL)) | 219 | if (unlikely(*ppage == NULL)) { |
218 | return -ENOMEM; | 220 | if (copied == 0) |
221 | copied = -ENOMEM; | ||
222 | goto out; | ||
223 | } | ||
219 | } | 224 | } |
220 | 225 | ||
221 | len = PAGE_CACHE_SIZE; | 226 | len = PAGE_CACHE_SIZE; |
@@ -233,16 +238,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
233 | } | 238 | } |
234 | flush_dcache_page(*ppage); | 239 | flush_dcache_page(*ppage); |
235 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); | 240 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); |
241 | copied += ret; | ||
236 | if (ret != len || !desc->count) | 242 | if (ret != len || !desc->count) |
237 | return 0; | 243 | goto out; |
238 | ppage++; | 244 | ppage++; |
239 | } while ((pglen -= len) != 0); | 245 | } while ((pglen -= len) != 0); |
240 | copy_tail: | 246 | copy_tail: |
241 | len = xdr->tail[0].iov_len; | 247 | len = xdr->tail[0].iov_len; |
242 | if (base < len) | 248 | if (base < len) |
243 | copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); | 249 | copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); |
244 | 250 | out: | |
245 | return 0; | 251 | return copied; |
246 | } | 252 | } |
247 | 253 | ||
248 | 254 | ||
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index a180ed4952d6..ef941e7de8bf 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -823,10 +823,15 @@ tcp_copy_data(skb_reader_t *desc, void *p, size_t len) | |||
823 | { | 823 | { |
824 | if (len > desc->count) | 824 | if (len > desc->count) |
825 | len = desc->count; | 825 | len = desc->count; |
826 | if (skb_copy_bits(desc->skb, desc->offset, p, len)) | 826 | if (skb_copy_bits(desc->skb, desc->offset, p, len)) { |
827 | dprintk("RPC: failed to copy %zu bytes from skb. %zu bytes remain\n", | ||
828 | len, desc->count); | ||
827 | return 0; | 829 | return 0; |
830 | } | ||
828 | desc->offset += len; | 831 | desc->offset += len; |
829 | desc->count -= len; | 832 | desc->count -= len; |
833 | dprintk("RPC: copied %zu bytes from skb. %zu bytes remain\n", | ||
834 | len, desc->count); | ||
830 | return len; | 835 | return len; |
831 | } | 836 | } |
832 | 837 | ||
@@ -865,6 +870,8 @@ tcp_read_fraghdr(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
865 | static void | 870 | static void |
866 | tcp_check_recm(struct rpc_xprt *xprt) | 871 | tcp_check_recm(struct rpc_xprt *xprt) |
867 | { | 872 | { |
873 | dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u, tcp_flags = %lx\n", | ||
874 | xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_flags); | ||
868 | if (xprt->tcp_offset == xprt->tcp_reclen) { | 875 | if (xprt->tcp_offset == xprt->tcp_reclen) { |
869 | xprt->tcp_flags |= XPRT_COPY_RECM; | 876 | xprt->tcp_flags |= XPRT_COPY_RECM; |
870 | xprt->tcp_offset = 0; | 877 | xprt->tcp_offset = 0; |
@@ -909,7 +916,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
909 | struct rpc_rqst *req; | 916 | struct rpc_rqst *req; |
910 | struct xdr_buf *rcvbuf; | 917 | struct xdr_buf *rcvbuf; |
911 | size_t len; | 918 | size_t len; |
912 | int r; | 919 | ssize_t r; |
913 | 920 | ||
914 | /* Find and lock the request corresponding to this xid */ | 921 | /* Find and lock the request corresponding to this xid */ |
915 | spin_lock(&xprt->sock_lock); | 922 | spin_lock(&xprt->sock_lock); |
@@ -932,15 +939,17 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
932 | my_desc.count = len; | 939 | my_desc.count = len; |
933 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, | 940 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, |
934 | &my_desc, tcp_copy_data); | 941 | &my_desc, tcp_copy_data); |
935 | desc->count -= len; | 942 | desc->count -= r; |
936 | desc->offset += len; | 943 | desc->offset += r; |
937 | } else | 944 | } else |
938 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, | 945 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, |
939 | desc, tcp_copy_data); | 946 | desc, tcp_copy_data); |
940 | xprt->tcp_copied += len; | ||
941 | xprt->tcp_offset += len; | ||
942 | 947 | ||
943 | if (r < 0) { | 948 | if (r > 0) { |
949 | xprt->tcp_copied += r; | ||
950 | xprt->tcp_offset += r; | ||
951 | } | ||
952 | if (r != len) { | ||
944 | /* Error when copying to the receive buffer, | 953 | /* Error when copying to the receive buffer, |
945 | * usually because we weren't able to allocate | 954 | * usually because we weren't able to allocate |
946 | * additional buffer pages. All we can do now | 955 | * additional buffer pages. All we can do now |
@@ -951,9 +960,18 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
951 | * be discarded. | 960 | * be discarded. |
952 | */ | 961 | */ |
953 | xprt->tcp_flags &= ~XPRT_COPY_DATA; | 962 | xprt->tcp_flags &= ~XPRT_COPY_DATA; |
963 | dprintk("RPC: XID %08x truncated request\n", | ||
964 | ntohl(xprt->tcp_xid)); | ||
965 | dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u\n", | ||
966 | xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen); | ||
954 | goto out; | 967 | goto out; |
955 | } | 968 | } |
956 | 969 | ||
970 | dprintk("RPC: XID %08x read %u bytes\n", | ||
971 | ntohl(xprt->tcp_xid), r); | ||
972 | dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u\n", | ||
973 | xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen); | ||
974 | |||
957 | if (xprt->tcp_copied == req->rq_private_buf.buflen) | 975 | if (xprt->tcp_copied == req->rq_private_buf.buflen) |
958 | xprt->tcp_flags &= ~XPRT_COPY_DATA; | 976 | xprt->tcp_flags &= ~XPRT_COPY_DATA; |
959 | else if (xprt->tcp_offset == xprt->tcp_reclen) { | 977 | else if (xprt->tcp_offset == xprt->tcp_reclen) { |
@@ -961,12 +979,12 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
961 | xprt->tcp_flags &= ~XPRT_COPY_DATA; | 979 | xprt->tcp_flags &= ~XPRT_COPY_DATA; |
962 | } | 980 | } |
963 | 981 | ||
982 | out: | ||
964 | if (!(xprt->tcp_flags & XPRT_COPY_DATA)) { | 983 | if (!(xprt->tcp_flags & XPRT_COPY_DATA)) { |
965 | dprintk("RPC: %4d received reply complete\n", | 984 | dprintk("RPC: %4d received reply complete\n", |
966 | req->rq_task->tk_pid); | 985 | req->rq_task->tk_pid); |
967 | xprt_complete_rqst(xprt, req, xprt->tcp_copied); | 986 | xprt_complete_rqst(xprt, req, xprt->tcp_copied); |
968 | } | 987 | } |
969 | out: | ||
970 | spin_unlock(&xprt->sock_lock); | 988 | spin_unlock(&xprt->sock_lock); |
971 | tcp_check_recm(xprt); | 989 | tcp_check_recm(xprt); |
972 | } | 990 | } |
@@ -985,6 +1003,7 @@ tcp_read_discard(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
985 | desc->count -= len; | 1003 | desc->count -= len; |
986 | desc->offset += len; | 1004 | desc->offset += len; |
987 | xprt->tcp_offset += len; | 1005 | xprt->tcp_offset += len; |
1006 | dprintk("RPC: discarded %u bytes\n", len); | ||
988 | tcp_check_recm(xprt); | 1007 | tcp_check_recm(xprt); |
989 | } | 1008 | } |
990 | 1009 | ||