Commit 031097d9 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

net: skmsg: fix TLS 1.3 crash with full sk_msg

TLS 1.3 started using the entry at the end of the SG array
for chaining-in the single byte content type entry. This mostly
works:

[ E E E E E E . . ]
  ^           ^
   start       end

                 E < content type
               /
[ E E E E E E C . ]
  ^           ^
   start       end

(Where E denotes a populated SG entry; C denotes a chaining entry.)

If the array is full, however, the end will point to the start:

[ E E E E E E E E ]
  ^
   start
   end

And we end up overwriting the start:

    E < content type
   /
[ C E E E E E E E ]
  ^
   start
   end

The sg array is supposed to be a circular buffer with start and
end markers pointing anywhere. In case where start > end
(i.e. the circular buffer has "wrapped") there is an extra entry
reserved at the end to chain the two halves together.

[ E E E E E E . . l ]

(Where l is the reserved entry for "looping" back to front.

As suggested by John, let's reserve another entry for chaining
SG entries after the main circular buffer. Note that this entry
has to be pointed to by the end entry so its position is not fixed.

Examples of full messages:

[ E E E E E E E E . l ]
  ^               ^
   start           end

   <---------------.
[ E E . E E E E E E l ]
      ^ ^
   end   start

Now the end will always point to an unused entry, so TLS 1.3
can always use it.

Fixes: 130b392c ("net: tls: Add tls 1.3 support")
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarSimon Horman <simon.horman@netronome.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d10523d0
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <net/strparser.h> #include <net/strparser.h>
#define MAX_MSG_FRAGS MAX_SKB_FRAGS #define MAX_MSG_FRAGS MAX_SKB_FRAGS
#define NR_MSG_FRAG_IDS (MAX_MSG_FRAGS + 1)
enum __sk_action { enum __sk_action {
__SK_DROP = 0, __SK_DROP = 0,
...@@ -29,13 +30,15 @@ struct sk_msg_sg { ...@@ -29,13 +30,15 @@ struct sk_msg_sg {
u32 size; u32 size;
u32 copybreak; u32 copybreak;
unsigned long copy; unsigned long copy;
/* The extra element is used for chaining the front and sections when /* The extra two elements:
* the list becomes partitioned (e.g. end < start). The crypto APIs * 1) used for chaining the front and sections when the list becomes
* require the chaining. * partitioned (e.g. end < start). The crypto APIs require the
* chaining;
* 2) to chain tailer SG entries after the message.
*/ */
struct scatterlist data[MAX_MSG_FRAGS + 1]; struct scatterlist data[MAX_MSG_FRAGS + 2];
}; };
static_assert(BITS_PER_LONG >= MAX_MSG_FRAGS); static_assert(BITS_PER_LONG >= NR_MSG_FRAG_IDS);
/* UAPI in filter.c depends on struct sk_msg_sg being first element. */ /* UAPI in filter.c depends on struct sk_msg_sg being first element. */
struct sk_msg { struct sk_msg {
...@@ -142,13 +145,13 @@ static inline void sk_msg_apply_bytes(struct sk_psock *psock, u32 bytes) ...@@ -142,13 +145,13 @@ static inline void sk_msg_apply_bytes(struct sk_psock *psock, u32 bytes)
static inline u32 sk_msg_iter_dist(u32 start, u32 end) static inline u32 sk_msg_iter_dist(u32 start, u32 end)
{ {
return end >= start ? end - start : end + (MAX_MSG_FRAGS - start); return end >= start ? end - start : end + (NR_MSG_FRAG_IDS - start);
} }
#define sk_msg_iter_var_prev(var) \ #define sk_msg_iter_var_prev(var) \
do { \ do { \
if (var == 0) \ if (var == 0) \
var = MAX_MSG_FRAGS - 1; \ var = NR_MSG_FRAG_IDS - 1; \
else \ else \
var--; \ var--; \
} while (0) } while (0)
...@@ -156,7 +159,7 @@ static inline u32 sk_msg_iter_dist(u32 start, u32 end) ...@@ -156,7 +159,7 @@ static inline u32 sk_msg_iter_dist(u32 start, u32 end)
#define sk_msg_iter_var_next(var) \ #define sk_msg_iter_var_next(var) \
do { \ do { \
var++; \ var++; \
if (var == MAX_MSG_FRAGS) \ if (var == NR_MSG_FRAG_IDS) \
var = 0; \ var = 0; \
} while (0) } while (0)
...@@ -173,9 +176,9 @@ static inline void sk_msg_clear_meta(struct sk_msg *msg) ...@@ -173,9 +176,9 @@ static inline void sk_msg_clear_meta(struct sk_msg *msg)
static inline void sk_msg_init(struct sk_msg *msg) static inline void sk_msg_init(struct sk_msg *msg)
{ {
BUILD_BUG_ON(ARRAY_SIZE(msg->sg.data) - 1 != MAX_MSG_FRAGS); BUILD_BUG_ON(ARRAY_SIZE(msg->sg.data) - 1 != NR_MSG_FRAG_IDS);
memset(msg, 0, sizeof(*msg)); memset(msg, 0, sizeof(*msg));
sg_init_marker(msg->sg.data, MAX_MSG_FRAGS); sg_init_marker(msg->sg.data, NR_MSG_FRAG_IDS);
} }
static inline void sk_msg_xfer(struct sk_msg *dst, struct sk_msg *src, static inline void sk_msg_xfer(struct sk_msg *dst, struct sk_msg *src,
...@@ -196,14 +199,11 @@ static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src) ...@@ -196,14 +199,11 @@ static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src)
static inline bool sk_msg_full(const struct sk_msg *msg) static inline bool sk_msg_full(const struct sk_msg *msg)
{ {
return (msg->sg.end == msg->sg.start) && msg->sg.size; return sk_msg_iter_dist(msg->sg.start, msg->sg.end) == MAX_MSG_FRAGS;
} }
static inline u32 sk_msg_elem_used(const struct sk_msg *msg) static inline u32 sk_msg_elem_used(const struct sk_msg *msg)
{ {
if (sk_msg_full(msg))
return MAX_MSG_FRAGS;
return sk_msg_iter_dist(msg->sg.start, msg->sg.end); return sk_msg_iter_dist(msg->sg.start, msg->sg.end);
} }
......
...@@ -2299,7 +2299,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, ...@@ -2299,7 +2299,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start,
WARN_ON_ONCE(last_sge == first_sge); WARN_ON_ONCE(last_sge == first_sge);
shift = last_sge > first_sge ? shift = last_sge > first_sge ?
last_sge - first_sge - 1 : last_sge - first_sge - 1 :
MAX_SKB_FRAGS - first_sge + last_sge - 1; NR_MSG_FRAG_IDS - first_sge + last_sge - 1;
if (!shift) if (!shift)
goto out; goto out;
...@@ -2308,8 +2308,8 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, ...@@ -2308,8 +2308,8 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start,
do { do {
u32 move_from; u32 move_from;
if (i + shift >= MAX_MSG_FRAGS) if (i + shift >= NR_MSG_FRAG_IDS)
move_from = i + shift - MAX_MSG_FRAGS; move_from = i + shift - NR_MSG_FRAG_IDS;
else else
move_from = i + shift; move_from = i + shift;
if (move_from == msg->sg.end) if (move_from == msg->sg.end)
...@@ -2323,7 +2323,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, ...@@ -2323,7 +2323,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start,
} while (1); } while (1);
msg->sg.end = msg->sg.end - shift > msg->sg.end ? msg->sg.end = msg->sg.end - shift > msg->sg.end ?
msg->sg.end - shift + MAX_MSG_FRAGS : msg->sg.end - shift + NR_MSG_FRAG_IDS :
msg->sg.end - shift; msg->sg.end - shift;
out: out:
msg->data = sg_virt(&msg->sg.data[first_sge]) + start - offset; msg->data = sg_virt(&msg->sg.data[first_sge]) + start - offset;
......
...@@ -421,7 +421,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb) ...@@ -421,7 +421,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb)
copied = skb->len; copied = skb->len;
msg->sg.start = 0; msg->sg.start = 0;
msg->sg.size = copied; msg->sg.size = copied;
msg->sg.end = num_sge == MAX_MSG_FRAGS ? 0 : num_sge; msg->sg.end = num_sge;
msg->skb = skb; msg->skb = skb;
sk_psock_queue_msg(psock, msg); sk_psock_queue_msg(psock, msg);
......
...@@ -301,7 +301,7 @@ EXPORT_SYMBOL_GPL(tcp_bpf_sendmsg_redir); ...@@ -301,7 +301,7 @@ EXPORT_SYMBOL_GPL(tcp_bpf_sendmsg_redir);
static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock,
struct sk_msg *msg, int *copied, int flags) struct sk_msg *msg, int *copied, int flags)
{ {
bool cork = false, enospc = msg->sg.start == msg->sg.end; bool cork = false, enospc = sk_msg_full(msg);
struct sock *sk_redir; struct sock *sk_redir;
u32 tosend, delta = 0; u32 tosend, delta = 0;
int ret; int ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment