Skip to content

Commit 692dba9

Browse files
committed
bpf: Avoid iter->offset making backward progress in bpf_iter_udp
JIRA: https://issues.redhat.com/browse/RHEL-65787 commit 2242fd5 Author: Martin KaFai Lau <martin.lau@kernel.org> Date: Fri Jan 12 11:05:29 2024 -0800 bpf: Avoid iter->offset making backward progress in bpf_iter_udp There is a bug in the bpf_iter_udp_batch() function that stops the userspace from making forward progress. The case that triggers the bug is the userspace passed in a very small read buffer. When the bpf prog does bpf_seq_printf, the userspace read buffer is not enough to capture the whole bucket. When the read buffer is not large enough, the kernel will remember the offset of the bucket in iter->offset such that the next userspace read() can continue from where it left off. The kernel will skip the number (== "iter->offset") of sockets in the next read(). However, the code directly decrements the "--iter->offset". This is incorrect because the next read() may not consume the whole bucket either and then the next-next read() will start from offset 0. The net effect is the userspace will keep reading from the beginning of a bucket and the process will never finish. "iter->offset" must always go forward until the whole bucket is consumed. This patch fixes it by using a local variable "resume_offset" and "resume_bucket". "iter->offset" is always reset to 0 before it may be used. "iter->offset" will be advanced to the "resume_offset" when it continues from the "resume_bucket" (i.e. "state->bucket == resume_bucket"). This brings it closer to the bpf_iter_tcp's offset handling which does not suffer the same bug. Cc: Aditi Ghag <aditi.ghag@isovalent.com> Fixes: c96dac8 ("bpf: udp: Implement batching for sockets iterator") Acked-by: Yonghong Song <yonghong.song@linux.dev> Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> Reviewed-by: Aditi Ghag <aditi.ghag@isovalent.com> Link: https://lore.kernel.org/r/20240112190530.3751661-3-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
1 parent 61c0142 commit 692dba9

File tree

1 file changed

+10
-11
lines changed

1 file changed

+10
-11
lines changed

net/ipv4/udp.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3180,16 +3180,18 @@ static struct sock *bpf_iter_udp_batch(struct seq_file *seq)
31803180
struct bpf_udp_iter_state *iter = seq->private;
31813181
struct udp_iter_state *state = &iter->state;
31823182
struct net *net = seq_file_net(seq);
3183+
int resume_bucket, resume_offset;
31833184
struct udp_table *udptable;
31843185
unsigned int batch_sks = 0;
31853186
bool resized = false;
31863187
struct sock *sk;
31873188

3189+
resume_bucket = state->bucket;
3190+
resume_offset = iter->offset;
3191+
31883192
/* The current batch is done, so advance the bucket. */
3189-
if (iter->st_bucket_done) {
3193+
if (iter->st_bucket_done)
31903194
state->bucket++;
3191-
iter->offset = 0;
3192-
}
31933195

31943196
udptable = udp_get_table_seq(seq, net);
31953197

@@ -3209,19 +3211,19 @@ static struct sock *bpf_iter_udp_batch(struct seq_file *seq)
32093211
for (; state->bucket <= udptable->mask; state->bucket++) {
32103212
struct udp_hslot *hslot2 = &udptable->hash2[state->bucket];
32113213

3212-
if (hlist_empty(&hslot2->head)) {
3213-
iter->offset = 0;
3214+
if (hlist_empty(&hslot2->head))
32143215
continue;
3215-
}
32163216

3217+
iter->offset = 0;
32173218
spin_lock_bh(&hslot2->lock);
32183219
udp_portaddr_for_each_entry(sk, &hslot2->head) {
32193220
if (seq_sk_match(seq, sk)) {
32203221
/* Resume from the last iterated socket at the
32213222
* offset in the bucket before iterator was stopped.
32223223
*/
3223-
if (iter->offset) {
3224-
--iter->offset;
3224+
if (state->bucket == resume_bucket &&
3225+
iter->offset < resume_offset) {
3226+
++iter->offset;
32253227
continue;
32263228
}
32273229
if (iter->end_sk < iter->max_sk) {
@@ -3235,9 +3237,6 @@ static struct sock *bpf_iter_udp_batch(struct seq_file *seq)
32353237

32363238
if (iter->end_sk)
32373239
break;
3238-
3239-
/* Reset the current bucket's offset before moving to the next bucket. */
3240-
iter->offset = 0;
32413240
}
32423241

32433242
/* All done: no batch made. */

0 commit comments

Comments
 (0)