bpf_iter_scx_dsq_new() clears kit->dsq on failure and bpf_iter_scx_dsq_{next,destroy}() guard against that. scx_dsq_move() doesn't - it dereferences kit->dsq immediately, so a BPF program that calls scx_bpf_dsq_move[_vtime]() after a failed iter_new oopses the kernel. Return false if kit->dsq is NULL.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -8076,12 +8076,22 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, · 8075 unmodified lines | |||
| 8076 | struct task_struct *p, u64 dsq_id, u64 enq_flags) | 8076 | struct task_struct *p, u64 dsq_id, u64 enq_flags) |
| 8077 | { | 8077 | { |
| 8078 | struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq; | 8078 | struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq; |
| 8079 | struct scx_sched *sch = src_dsq->sched; | 8079 | struct scx_sched *sch; |
| 8080 | struct rq *this_rq, *src_rq, *locked_rq; | 8080 | struct rq *this_rq, *src_rq, *locked_rq; |
| 8081 | bool dispatched = false; | 8081 | bool dispatched = false; |
| 8082 | bool in_balance; | 8082 | bool in_balance; |
| 8083 | unsigned long flags; | 8083 | unsigned long flags; |
| 8084 | 8084 | ||
| 8085 | /* | ||
| 8086 | * The verifier considers an iterator slot initialized on any | ||
| 8087 | * KF_ITER_NEW return, so a BPF program may legally reach here after | ||
| 8088 | * bpf_iter_scx_dsq_new() failed and left @kit->dsq NULL. | ||
| 8089 | */ | ||
| 8090 | if (unlikely(!src_dsq)) | ||
| 8091 | return false; | ||
| 8092 | |||
| 8093 | sch = src_dsq->sched; | ||
| 8094 | |||
| 8085 | if (!scx_vet_enq_flags(sch, dsq_id, &enq_flags)) | 8095 | if (!scx_vet_enq_flags(sch, dsq_id, &enq_flags)) |
| 8086 | return false; | 8096 | return false; |
| 8087 | 8097 | ||