Re: [PATCH v16 04/10] tls: add TLS 1.3 hardware offload support

Jakub Kicinski <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <[email protected]>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
    tls: add TLS 1.3 hardware offload support

This drops the TLS_1_2_VERSION gates in tls_set_device_offload() and
tls_set_device_offload_rx(), teaches tls_device_record_close() to append
the TLS 1.3 content_type byte along with the tag, and switches the
device fallback encryption path to prot->prepend_size / prot->aad_size
so the 1.3 layouts work.  tls_device_init() also pre-populates
dummy_page with an identity byte map.

A few questions below.

> diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
> index 37bb06a8e8f5..bbb1aa733500 100644
> --- a/net/tls/tls_device.c
> +++ b/net/tls/tls_device.c
> @@ -317,25 +317,34 @@ static void tls_device_record_close(struct sock *sk,
>  				    unsigned char record_type)
>  {
>  	struct tls_prot_info *prot = &ctx->prot_info;
> -	struct page_frag dummy_tag_frag;
> -
> -	/* append tag
> -	 * device will fill in the tag, we just need to append a placeholder
> -	 * use socket memory to improve coalescing (re-using a single buffer
> -	 * increases frag count)
> -	 * if we can't allocate memory now use the dummy page
> +	int tail = prot->tag_size + prot->tail_size;
> +
> +	/* Append tail: tag for TLS 1.2, content_type + tag for TLS 1.3.
> +	 * Device fills in the tag, we just need to append a placeholder.
> +	 * Use socket memory to improve coalescing (re-using a single buffer
> +	 * increases frag count); if allocation fails use dummy_page
> +	 * (offset = record_type gives correct content_type byte via
> +	 * identity mapping)
>  	 */
> -	if (unlikely(pfrag->size - pfrag->offset < prot->tag_size) &&
> -	    !skb_page_frag_refill(prot->tag_size, pfrag, sk->sk_allocation)) {
> -		dummy_tag_frag.page = dummy_page;
> -		dummy_tag_frag.offset = 0;
> -		pfrag = &dummy_tag_frag;
> +	if (unlikely(pfrag->size - pfrag->offset < tail) &&
> +	    !skb_page_frag_refill(tail, pfrag, sk->sk_allocation)) {
> +		struct page_frag dummy_pfrag = {
> +			.page = dummy_page,
> +			.offset = record_type,
> +		};
> +		tls_append_frag(record, &dummy_pfrag, tail);

[Severity: Medium]
Here record_type is used directly as an offset into the shared global
dummy_page, and tail = prot->tag_size + prot->tail_size bytes are appended
starting from that offset.  tls_device_init() only writes the first 256
bytes of the page, and the page comes from a plain alloc_page(GFP_KERNEL),
so the rest is not zeroed.

With AES-GCM (tag_size 16) and TLS 1.3 (tail_size 1), a record_type of 240
or above makes the appended frag cover dummy_page bytes
[256, record_type + 17), which are never initialized.

record_type is taken verbatim from the user cmsg; tls_process_cmsg() in
tls_main.c checks only cmsg_len and MSG_MORE:

	case TLS_SET_RECORD_TYPE:
		if (cmsg->cmsg_len < CMSG_LEN(sizeof(*record_type)))
			return -EINVAL;
		if (msg->msg_flags & MSG_MORE)
			return -EINVAL;
		...
		*record_type = *(unsigned char *)CMSG_DATA(cmsg);

so all 256 offsets are reachable from an unprivileged process.  Can this
put uninitialized page contents into the tag placeholder area of the
record?  Would zeroing the page (__GFP_ZERO), initializing
256 + max tag bytes, or bounding the offset be preferable?

Related to this, the comment added in tls_device_init() says "By populating
all 256 values, we avoid needing to validate record_type at runtime", but
the identity map only covers the content_type byte, not the tag portion of
the tail that follows it.

> +	} else {
> +		if (prot->tail_size) {
> +			char *content_type_addr = page_address(pfrag->page) +
> +						  pfrag->offset;
> +			*content_type_addr = record_type;
> +		}
> +		tls_append_frag(record, pfrag, tail);
>  	}
> -	tls_append_frag(record, pfrag, prot->tag_size);

[Severity: High]
This isn't a bug introduced by this patch, but the else branch now
dereferences pfrag->page one step earlier than before, and pfrag->page is
never checked for NULL.

Consider the state skb_page_frag_refill() leaves behind on failure in
net/core/sock.c:

	pfrag->offset = 0;
	...
	pfrag->page = alloc_page(gfp);
	if (likely(pfrag->page)) {
		pfrag->size = PAGE_SIZE;
		return true;
	}
	return false;

offset is zeroed and page is NULL, but size keeps its previous non-zero
value, so pfrag->size - pfrag->offset is at least PAGE_SIZE and the
"< tail" guard here is false.  The dummy_page fallback is skipped and the
else branch runs.

Can tls_device_record_close() then be reached with that pfrag?  In
tls_push_data(), with an already-open record (record->len >
prot->prepend_size, e.g. after a previous MSG_MORE sendmsg),
tls_do_allocation() takes the sk_page_frag_refill(sk, pfrag) path; on
allocation failure it returns -ENOMEM, sk_stream_wait_memory() returns
non-zero (immediately -EAGAIN for MSG_DONTWAIT), and control does:

	} else if (record->len > prot->prepend_size) {
		goto last_record;
	}

which sets done = true and calls tls_device_record_close(sk, tls_ctx,
record, pfrag, record_type).

For TLS 1.3 (prot->tail_size == 1) that computes page_address(NULL) + 0 and
stores record_type through it, then tls_append_frag() does get_page(NULL).
The pre-patch code took the same false branch and crashed in
tls_append_frag(), so the NULL deref itself is older, but should this test
pfrag->page (or reset pfrag->size on refill failure) so the dummy_page
fallback that exists for exactly this situation is actually used?

>  
>  	/* fill prepend */
>  	tls_fill_prepend(ctx, skb_frag_address(&record->frags[0]),
> -			 record->len - prot->overhead_size,
> +			 record->len - prot->overhead_size + prot->tail_size,
>  			 record_type);
>  }
>  

[ ... ]

> @@ -1091,11 +1100,6 @@ int tls_set_device_offload(struct sock *sk)
>  	}
>  
>  	crypto_info = &ctx->crypto_send.info;
> -	if (crypto_info->version != TLS_1_2_VERSION) {
> -		rc = -EOPNOTSUPP;
> -		goto release_netdev;
> -	}
> -
>  	cipher_desc = get_cipher_desc(crypto_info->cipher_type);
>  	if (!cipher_desc || !cipher_desc->offloadable) {
>  		rc = -EINVAL;

[Severity: Low]
Should Documentation/networking/tls-offload.rst be updated along with this?
Its Rekey section still says:

	Offload does not currently support TLS 1.3, therefore key rotation
	is not a concern for offloaded connections at this point.

Removing the version gates here and in tls_set_device_offload_rx() makes
the first half untrue, and the later patches in the series ("tls: device:
add TX KeyUpdate support", "tls: device: add RX KeyUpdate support") make
the second half untrue.  No patch in the series touches that file, so the
sentence is still there at the end of the series.

>  
> @@ -1410,12 +1411,22 @@ static struct notifier_block tls_dev_notifier = {
>  
>  int __init tls_device_init(void)
>  {
> -	int err;
> +	unsigned char *page_addr;
> +	int err, i;
>  
>  	dummy_page = alloc_page(GFP_KERNEL);
>  	if (!dummy_page)
>  		return -ENOMEM;
>  
> +	/* Pre-populate dummy_page with identity mapping for all byte values.
> +	 * This is used as fallback for TLS 1.3 content type when memory
> +	 * allocation fails. By populating all 256 values, we avoid needing
> +	 * to validate record_type at runtime.
> +	 */
> +	page_addr = page_address(dummy_page);
> +	for (i = 0; i < 256; i++)
> +		page_addr[i] = (unsigned char)i;
> +
>  	destruct_wq = alloc_workqueue("ktls_device_destruct", WQ_PERCPU, 0);
>  	if (!destruct_wq) {
>  		err = -ENOMEM;
-- 
pw-bot: cr
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.