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.
Unified · Split
| @@ -8076,12 +8076,22 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, · 8075 unmodified lines | ||
| 8076 | 8076 | struct task_struct *p, u64 dsq_id, u64 enq_flags) |
| 8077 | 8077 | { |
| 8078 | 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 | 8080 | struct rq *this_rq, *src_rq, *locked_rq; |
| 8081 | 8081 | bool dispatched = false; |
| 8082 | 8082 | bool in_balance; |
| 8083 | 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 | 8095 | if (!scx_vet_enq_flags(sch, dsq_id, &enq_flags)) |
| 8086 | 8096 | return false; |
| 8087 | 8097 | |