Skip to content

Commit abaeb09

Browse files
jrfastabAlexei Starovoitov
authored andcommitted
bpf: sockmap, fix error handling in redirect failures
When a redirect failure happens we release the buffers in-flight without calling a sk_mem_uncharge(), the uncharge is called before dropping the sock lock for the redirecte, however we missed updating the ring start index. When no apply actions are in progress this is OK because we uncharge the entire buffer before the redirect. But, when we have apply logic running its possible that only a portion of the buffer is being redirected. In this case we only do memory accounting for the buffer slice being redirected and expect to be able to loop over the BPF program again and/or if a sock is closed uncharge the memory at sock destruct time. With an invalid start index however the program logic looks at the start pointer index, checks the length, and when seeing the length is zero (from the initial release and failure to update the pointer) aborts without uncharging/releasing the remaining memory. The fix for this is simply to update the start index. To avoid fixing this error in two locations we do a small refactor and remove one case where it is open-coded. Then fix it in the single function. Signed-off-by: John Fastabend <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent fec51d4 commit abaeb09

File tree

1 file changed

+12
-16
lines changed

1 file changed

+12
-16
lines changed

kernel/bpf/sockmap.c

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ static void return_mem_sg(struct sock *sk, int bytes, struct sk_msg_buff *md)
393393
} while (i != md->sg_end);
394394
}
395395

396-
static void free_bytes_sg(struct sock *sk, int bytes, struct sk_msg_buff *md)
396+
static void free_bytes_sg(struct sock *sk, int bytes,
397+
struct sk_msg_buff *md, bool charge)
397398
{
398399
struct scatterlist *sg = md->sg_data;
399400
int i = md->sg_start, free;
@@ -403,11 +404,13 @@ static void free_bytes_sg(struct sock *sk, int bytes, struct sk_msg_buff *md)
403404
if (bytes < free) {
404405
sg[i].length -= bytes;
405406
sg[i].offset += bytes;
406-
sk_mem_uncharge(sk, bytes);
407+
if (charge)
408+
sk_mem_uncharge(sk, bytes);
407409
break;
408410
}
409411

410-
sk_mem_uncharge(sk, sg[i].length);
412+
if (charge)
413+
sk_mem_uncharge(sk, sg[i].length);
411414
put_page(sg_page(&sg[i]));
412415
bytes -= sg[i].length;
413416
sg[i].length = 0;
@@ -418,6 +421,7 @@ static void free_bytes_sg(struct sock *sk, int bytes, struct sk_msg_buff *md)
418421
if (i == MAX_SKB_FRAGS)
419422
i = 0;
420423
}
424+
md->sg_start = i;
421425
}
422426

423427
static int free_sg(struct sock *sk, int start, struct sk_msg_buff *md)
@@ -576,10 +580,10 @@ static int bpf_tcp_sendmsg_do_redirect(struct sock *sk, int send,
576580
struct sk_msg_buff *md,
577581
int flags)
578582
{
583+
bool ingress = !!(md->flags & BPF_F_INGRESS);
579584
struct smap_psock *psock;
580585
struct scatterlist *sg;
581-
int i, err, free = 0;
582-
bool ingress = !!(md->flags & BPF_F_INGRESS);
586+
int err = 0;
583587

584588
sg = md->sg_data;
585589

@@ -607,16 +611,8 @@ static int bpf_tcp_sendmsg_do_redirect(struct sock *sk, int send,
607611
out_rcu:
608612
rcu_read_unlock();
609613
out:
610-
i = md->sg_start;
611-
while (sg[i].length) {
612-
free += sg[i].length;
613-
put_page(sg_page(&sg[i]));
614-
sg[i].length = 0;
615-
i++;
616-
if (i == MAX_SKB_FRAGS)
617-
i = 0;
618-
}
619-
return free;
614+
free_bytes_sg(NULL, send, md, false);
615+
return err;
620616
}
621617

622618
static inline void bpf_md_init(struct smap_psock *psock)
@@ -720,7 +716,7 @@ static int bpf_exec_tx_verdict(struct smap_psock *psock,
720716
break;
721717
case __SK_DROP:
722718
default:
723-
free_bytes_sg(sk, send, m);
719+
free_bytes_sg(sk, send, m, true);
724720
apply_bytes_dec(psock, send);
725721
*copied -= send;
726722
psock->sg_size -= send;

0 commit comments

Comments
 (0)