aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2019-11-04 18:36:57 -0500
committerDavid S. Miller <davem@davemloft.net>2019-11-05 21:07:47 -0500
commit683916f6a84023407761d843048f1aea486b2612 (patch)
tree909645fe5abf23144bbb2687c2216f39a6ed0eba /net
parent57d0f00dfeb3775eae88af1c4aeda6bd35943f20 (diff)
net/tls: fix sk_msg trim on fallback to copy mode
sk_msg_trim() tries to only update curr pointer if it falls into the trimmed region. The logic, however, does not take into the account pointer wrapping that sk_msg_iter_var_prev() does nor (as John points out) the fact that msg->sg is a ring buffer. This means that when the message was trimmed completely, the new curr pointer would have the value of MAX_MSG_FRAGS - 1, which is neither smaller than any other value, nor would it actually be correct. Special case the trimming to 0 length a little bit and rework the comparison between curr and end to take into account wrapping. This bug caused the TLS code to not copy all of the message, if zero copy filled in fewer sg entries than memcopy would need. Big thanks to Alexander Potapenko for the non-KMSAN reproducer. v2: - take into account that msg->sg is a ring buffer (John). Link: https://lore.kernel.org/netdev/20191030160542.30295-1-jakub.kicinski@netronome.com/ (v1) Fixes: d829e9c4112b ("tls: convert to generic sk_msg interface") Reported-by: syzbot+f8495bff23a879a6d0bd@syzkaller.appspotmail.com Reported-by: syzbot+6f50c99e8f6194bf363f@syzkaller.appspotmail.com Co-developed-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/skmsg.c20
1 files changed, 15 insertions, 5 deletions
diff --git a/net/core/skmsg.c b/net/core/skmsg.c
index cf390e0aa73d..ad31e4e53d0a 100644
--- a/net/core/skmsg.c
+++ b/net/core/skmsg.c
@@ -270,18 +270,28 @@ void sk_msg_trim(struct sock *sk, struct sk_msg *msg, int len)
270 270
271 msg->sg.data[i].length -= trim; 271 msg->sg.data[i].length -= trim;
272 sk_mem_uncharge(sk, trim); 272 sk_mem_uncharge(sk, trim);
273 /* Adjust copybreak if it falls into the trimmed part of last buf */
274 if (msg->sg.curr == i && msg->sg.copybreak > msg->sg.data[i].length)
275 msg->sg.copybreak = msg->sg.data[i].length;
273out: 276out:
274 /* If we trim data before curr pointer update copybreak and current 277 sk_msg_iter_var_next(i);
275 * so that any future copy operations start at new copy location. 278 msg->sg.end = i;
279
280 /* If we trim data a full sg elem before curr pointer update
281 * copybreak and current so that any future copy operations
282 * start at new copy location.
276 * However trimed data that has not yet been used in a copy op 283 * However trimed data that has not yet been used in a copy op
277 * does not require an update. 284 * does not require an update.
278 */ 285 */
279 if (msg->sg.curr >= i) { 286 if (!msg->sg.size) {
287 msg->sg.curr = msg->sg.start;
288 msg->sg.copybreak = 0;
289 } else if (sk_msg_iter_dist(msg->sg.start, msg->sg.curr) >=
290 sk_msg_iter_dist(msg->sg.start, msg->sg.end)) {
291 sk_msg_iter_var_prev(i);
280 msg->sg.curr = i; 292 msg->sg.curr = i;
281 msg->sg.copybreak = msg->sg.data[i].length; 293 msg->sg.copybreak = msg->sg.data[i].length;
282 } 294 }
283 sk_msg_iter_var_next(i);
284 msg->sg.end = i;
285} 295}
286EXPORT_SYMBOL_GPL(sk_msg_trim); 296EXPORT_SYMBOL_GPL(sk_msg_trim);
287 297