Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51c739d1 authored by David S. Miller's avatar David S. Miller
Browse files

[NET]: Fix incorrect sg_mark_end() calls.



This fixes scatterlist corruptions added by

	commit 68e3f5dd
	[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: default avatarDavid S. Miller <davem@davemloft.net>
parent 07afa040
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -2028,8 +2028,8 @@ void __init skb_init(void)
 *	Fill the specified scatter-gather list with mappings/pointers into a
 *	region of the buffer space attached to a socket buffer.
 */
int
skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
static int
__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
{
	int start = skb_headlen(skb);
	int i, copy = start - offset;
@@ -2078,7 +2078,8 @@ skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
			if ((copy = end - offset) > 0) {
				if (copy > len)
					copy = len;
				elt += skb_to_sgvec(list, sg+elt, offset - start, copy);
				elt += __skb_to_sgvec(list, sg+elt, offset - start,
						      copy);
				if ((len -= copy) == 0)
					return elt;
				offset += copy;
@@ -2090,6 +2091,15 @@ skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
	return elt;
}

int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
{
	int nsg = __skb_to_sgvec(skb, sg, offset, len);

	__sg_mark_end(&sg[nsg - 1]);

	return nsg;
}

/**
 *	skb_cow_data - Check that a socket buffer's data buffers are writable
 *	@skb: The socket buffer to check.
+7 −5
Original line number Diff line number Diff line
@@ -111,9 +111,10 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
				goto unlock;
		}
		sg_init_table(sg, nfrags);
		sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data +
		skb_to_sgvec(skb, sg,
			     esph->enc_data +
			     esp->conf.ivlen -
						      skb->data, clen));
			     skb->data, clen);
		err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
		if (unlikely(sg != &esp->sgbuf[0]))
			kfree(sg);
@@ -205,8 +206,9 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
			goto out;
	}
	sg_init_table(sg, nfrags);
	sg_mark_end(sg, skb_to_sgvec(skb, sg, sizeof(*esph) + esp->conf.ivlen,
				     elen));
	skb_to_sgvec(skb, sg,
		     sizeof(*esph) + esp->conf.ivlen,
		     elen);
	err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
	if (unlikely(sg != &esp->sgbuf[0]))
		kfree(sg);
+1 −1
Original line number Diff line number Diff line
@@ -1083,7 +1083,7 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
	sg_set_buf(&sg[block++], key->key, key->keylen);
	nbytes += key->keylen;

	sg_mark_end(sg, block);
	__sg_mark_end(&sg[block - 1]);

	/* Now store the Hash into the packet */
	err = crypto_hash_init(desc);
+7 −6
Original line number Diff line number Diff line
@@ -110,9 +110,10 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
				goto unlock;
		}
		sg_init_table(sg, nfrags);
		sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data +
		skb_to_sgvec(skb, sg,
			     esph->enc_data +
			     esp->conf.ivlen -
						      skb->data, clen));
			     skb->data, clen);
		err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
		if (unlikely(sg != &esp->sgbuf[0]))
			kfree(sg);
@@ -209,9 +210,9 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
			}
		}
		sg_init_table(sg, nfrags);
		sg_mark_end(sg, skb_to_sgvec(skb, sg,
		skb_to_sgvec(skb, sg,
			     sizeof(*esph) + esp->conf.ivlen,
					     elen));
			     elen);
		ret = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
		if (unlikely(sg != &esp->sgbuf[0]))
			kfree(sg);
+1 −1
Original line number Diff line number Diff line
@@ -781,7 +781,7 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
	sg_set_buf(&sg[block++], key->key, key->keylen);
	nbytes += key->keylen;

	sg_mark_end(sg, block);
	__sg_mark_end(&sg[block - 1]);

	/* Now store the hash into the packet */
	err = crypto_hash_init(desc);
Loading