diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-10-31 00:29:29 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-31 00:29:29 -0400 |
commit | 51c739d1f484b2562040a3e496dc8e1670d4e279 (patch) | |
tree | 87b12c2330f2951deb1a435367907d15a5d938c3 | |
parent | 07afa040252eb41f91f46f8e538b434a63122999 (diff) |
[NET]: Fix incorrect sg_mark_end() calls.
This fixes scatterlist corruptions added by
commit 68e3f5dd4db62619fdbe520d36c9ebf62e672256
[CRYPTO] users: Fix up scatterlist conversion errors
The issue is that the code calls sg_mark_end() which clobbers the
sg_page() pointer of the final scatterlist entry.
The first part fo the fix makes skb_to_sgvec() do __sg_mark_end().
After considering all skb_to_sgvec() call sites the most correct
solution is to call __sg_mark_end() in skb_to_sgvec() since that is
what all of the callers would end up doing anyways.
I suspect this might have fixed some problems in virtio_net which is
the sole non-crypto user of skb_to_sgvec().
Other similar sg_mark_end() cases were converted over to
__sg_mark_end() as well.
Arguably sg_mark_end() is a poorly named function because it doesn't
just "mark", it clears out the page pointer as a side effect, which is
what led to these bugs in the first place.
The one remaining plain sg_mark_end() call is in scsi_alloc_sgtable()
and arguably it could be converted to __sg_mark_end() if only so that
we can delete this confusing interface from linux/scatterlist.h
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/skbuff.c | 16 | ||||
-rw-r--r-- | net/ipv4/esp4.c | 12 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 2 | ||||
-rw-r--r-- | net/ipv6/esp6.c | 13 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 2 | ||||
-rw-r--r-- | net/rxrpc/rxkad.c | 9 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_crypto.c | 6 |
7 files changed, 37 insertions, 23 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 573e1724019..64b50ff7a41 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -2028,8 +2028,8 @@ void __init skb_init(void) | |||
2028 | * Fill the specified scatter-gather list with mappings/pointers into a | 2028 | * Fill the specified scatter-gather list with mappings/pointers into a |
2029 | * region of the buffer space attached to a socket buffer. | 2029 | * region of the buffer space attached to a socket buffer. |
2030 | */ | 2030 | */ |
2031 | int | 2031 | static int |
2032 | skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) | 2032 | __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) |
2033 | { | 2033 | { |
2034 | int start = skb_headlen(skb); | 2034 | int start = skb_headlen(skb); |
2035 | int i, copy = start - offset; | 2035 | int i, copy = start - offset; |
@@ -2078,7 +2078,8 @@ skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) | |||
2078 | if ((copy = end - offset) > 0) { | 2078 | if ((copy = end - offset) > 0) { |
2079 | if (copy > len) | 2079 | if (copy > len) |
2080 | copy = len; | 2080 | copy = len; |
2081 | elt += skb_to_sgvec(list, sg+elt, offset - start, copy); | 2081 | elt += __skb_to_sgvec(list, sg+elt, offset - start, |
2082 | copy); | ||
2082 | if ((len -= copy) == 0) | 2083 | if ((len -= copy) == 0) |
2083 | return elt; | 2084 | return elt; |
2084 | offset += copy; | 2085 | offset += copy; |
@@ -2090,6 +2091,15 @@ skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) | |||
2090 | return elt; | 2091 | return elt; |
2091 | } | 2092 | } |
2092 | 2093 | ||
2094 | int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) | ||
2095 | { | ||
2096 | int nsg = __skb_to_sgvec(skb, sg, offset, len); | ||
2097 | |||
2098 | __sg_mark_end(&sg[nsg - 1]); | ||
2099 | |||
2100 | return nsg; | ||
2101 | } | ||
2102 | |||
2093 | /** | 2103 | /** |
2094 | * skb_cow_data - Check that a socket buffer's data buffers are writable | 2104 | * skb_cow_data - Check that a socket buffer's data buffers are writable |
2095 | * @skb: The socket buffer to check. | 2105 | * @skb: The socket buffer to check. |
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index cad4278025a..c31bccb9b52 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c | |||
@@ -111,9 +111,10 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) | |||
111 | goto unlock; | 111 | goto unlock; |
112 | } | 112 | } |
113 | sg_init_table(sg, nfrags); | 113 | sg_init_table(sg, nfrags); |
114 | sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data + | 114 | skb_to_sgvec(skb, sg, |
115 | esp->conf.ivlen - | 115 | esph->enc_data + |
116 | skb->data, clen)); | 116 | esp->conf.ivlen - |
117 | skb->data, clen); | ||
117 | err = crypto_blkcipher_encrypt(&desc, sg, sg, clen); | 118 | err = crypto_blkcipher_encrypt(&desc, sg, sg, clen); |
118 | if (unlikely(sg != &esp->sgbuf[0])) | 119 | if (unlikely(sg != &esp->sgbuf[0])) |
119 | kfree(sg); | 120 | kfree(sg); |
@@ -205,8 +206,9 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) | |||
205 | goto out; | 206 | goto out; |
206 | } | 207 | } |
207 | sg_init_table(sg, nfrags); | 208 | sg_init_table(sg, nfrags); |
208 | sg_mark_end(sg, skb_to_sgvec(skb, sg, sizeof(*esph) + esp->conf.ivlen, | 209 | skb_to_sgvec(skb, sg, |
209 | elen)); | 210 | sizeof(*esph) + esp->conf.ivlen, |
211 | elen); | ||
210 | err = crypto_blkcipher_decrypt(&desc, sg, sg, elen); | 212 | err = crypto_blkcipher_decrypt(&desc, sg, sg, elen); |
211 | if (unlikely(sg != &esp->sgbuf[0])) | 213 | if (unlikely(sg != &esp->sgbuf[0])) |
212 | kfree(sg); | 214 | kfree(sg); |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d3d8d5dfcee..eec02b29ffc 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -1083,7 +1083,7 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | |||
1083 | sg_set_buf(&sg[block++], key->key, key->keylen); | 1083 | sg_set_buf(&sg[block++], key->key, key->keylen); |
1084 | nbytes += key->keylen; | 1084 | nbytes += key->keylen; |
1085 | 1085 | ||
1086 | sg_mark_end(sg, block); | 1086 | __sg_mark_end(&sg[block - 1]); |
1087 | 1087 | ||
1088 | /* Now store the Hash into the packet */ | 1088 | /* Now store the Hash into the packet */ |
1089 | err = crypto_hash_init(desc); | 1089 | err = crypto_hash_init(desc); |
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index ab17b5e6235..7db66f10e00 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c | |||
@@ -110,9 +110,10 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
110 | goto unlock; | 110 | goto unlock; |
111 | } | 111 | } |
112 | sg_init_table(sg, nfrags); | 112 | sg_init_table(sg, nfrags); |
113 | sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data + | 113 | skb_to_sgvec(skb, sg, |
114 | esp->conf.ivlen - | 114 | esph->enc_data + |
115 | skb->data, clen)); | 115 | esp->conf.ivlen - |
116 | skb->data, clen); | ||
116 | err = crypto_blkcipher_encrypt(&desc, sg, sg, clen); | 117 | err = crypto_blkcipher_encrypt(&desc, sg, sg, clen); |
117 | if (unlikely(sg != &esp->sgbuf[0])) | 118 | if (unlikely(sg != &esp->sgbuf[0])) |
118 | kfree(sg); | 119 | kfree(sg); |
@@ -209,9 +210,9 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
209 | } | 210 | } |
210 | } | 211 | } |
211 | sg_init_table(sg, nfrags); | 212 | sg_init_table(sg, nfrags); |
212 | sg_mark_end(sg, skb_to_sgvec(skb, sg, | 213 | skb_to_sgvec(skb, sg, |
213 | sizeof(*esph) + esp->conf.ivlen, | 214 | sizeof(*esph) + esp->conf.ivlen, |
214 | elen)); | 215 | elen); |
215 | ret = crypto_blkcipher_decrypt(&desc, sg, sg, elen); | 216 | ret = crypto_blkcipher_decrypt(&desc, sg, sg, elen); |
216 | if (unlikely(sg != &esp->sgbuf[0])) | 217 | if (unlikely(sg != &esp->sgbuf[0])) |
217 | kfree(sg); | 218 | kfree(sg); |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f1523b82cac..4b903288095 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -781,7 +781,7 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | |||
781 | sg_set_buf(&sg[block++], key->key, key->keylen); | 781 | sg_set_buf(&sg[block++], key->key, key->keylen); |
782 | nbytes += key->keylen; | 782 | nbytes += key->keylen; |
783 | 783 | ||
784 | sg_mark_end(sg, block); | 784 | __sg_mark_end(&sg[block - 1]); |
785 | 785 | ||
786 | /* Now store the hash into the packet */ | 786 | /* Now store the hash into the packet */ |
787 | err = crypto_hash_init(desc); | 787 | err = crypto_hash_init(desc); |
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index eebefb6ef13..c387cf68a08 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c | |||
@@ -237,7 +237,8 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, | |||
237 | len = data_size + call->conn->size_align - 1; | 237 | len = data_size + call->conn->size_align - 1; |
238 | len &= ~(call->conn->size_align - 1); | 238 | len &= ~(call->conn->size_align - 1); |
239 | 239 | ||
240 | sg_init_table(sg, skb_to_sgvec(skb, sg, 0, len)); | 240 | sg_init_table(sg, nsg); |
241 | skb_to_sgvec(skb, sg, 0, len); | ||
241 | crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); | 242 | crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); |
242 | 243 | ||
243 | _leave(" = 0"); | 244 | _leave(" = 0"); |
@@ -344,7 +345,7 @@ static int rxkad_verify_packet_auth(const struct rxrpc_call *call, | |||
344 | goto nomem; | 345 | goto nomem; |
345 | 346 | ||
346 | sg_init_table(sg, nsg); | 347 | sg_init_table(sg, nsg); |
347 | sg_mark_end(sg, skb_to_sgvec(skb, sg, 0, 8)); | 348 | skb_to_sgvec(skb, sg, 0, 8); |
348 | 349 | ||
349 | /* start the decryption afresh */ | 350 | /* start the decryption afresh */ |
350 | memset(&iv, 0, sizeof(iv)); | 351 | memset(&iv, 0, sizeof(iv)); |
@@ -426,7 +427,7 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, | |||
426 | } | 427 | } |
427 | 428 | ||
428 | sg_init_table(sg, nsg); | 429 | sg_init_table(sg, nsg); |
429 | sg_mark_end(sg, skb_to_sgvec(skb, sg, 0, skb->len)); | 430 | skb_to_sgvec(skb, sg, 0, skb->len); |
430 | 431 | ||
431 | /* decrypt from the session key */ | 432 | /* decrypt from the session key */ |
432 | payload = call->conn->key->payload.data; | 433 | payload = call->conn->key->payload.data; |
@@ -701,7 +702,7 @@ static void rxkad_sg_set_buf2(struct scatterlist sg[2], | |||
701 | nsg++; | 702 | nsg++; |
702 | } | 703 | } |
703 | 704 | ||
704 | sg_mark_end(sg, nsg); | 705 | __sg_mark_end(&sg[nsg - 1]); |
705 | 706 | ||
706 | ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); | 707 | ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); |
707 | } | 708 | } |
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index 91cd8f0d1e1..ab7cbd6575c 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c | |||
@@ -211,8 +211,8 @@ encryptor(struct scatterlist *sg, void *data) | |||
211 | if (thislen == 0) | 211 | if (thislen == 0) |
212 | return 0; | 212 | return 0; |
213 | 213 | ||
214 | sg_mark_end(desc->infrags, desc->fragno); | 214 | __sg_mark_end(&desc->infrags[desc->fragno - 1]); |
215 | sg_mark_end(desc->outfrags, desc->fragno); | 215 | __sg_mark_end(&desc->outfrags[desc->fragno - 1]); |
216 | 216 | ||
217 | ret = crypto_blkcipher_encrypt_iv(&desc->desc, desc->outfrags, | 217 | ret = crypto_blkcipher_encrypt_iv(&desc->desc, desc->outfrags, |
218 | desc->infrags, thislen); | 218 | desc->infrags, thislen); |
@@ -293,7 +293,7 @@ decryptor(struct scatterlist *sg, void *data) | |||
293 | if (thislen == 0) | 293 | if (thislen == 0) |
294 | return 0; | 294 | return 0; |
295 | 295 | ||
296 | sg_mark_end(desc->frags, desc->fragno); | 296 | __sg_mark_end(&desc->frags[desc->fragno - 1]); |
297 | 297 | ||
298 | ret = crypto_blkcipher_decrypt_iv(&desc->desc, desc->frags, | 298 | ret = crypto_blkcipher_decrypt_iv(&desc->desc, desc->frags, |
299 | desc->frags, thislen); | 299 | desc->frags, thislen); |