scx_alloc_and_add_sched() publishes @sch through ops->priv before allocating the cgroup path. If that allocation fails, the unwind path clears ops->priv and frees @sch immediately. scx_prog_sched() callers can dereference ops->priv from RCU context the moment it is set, so freeing without a grace period can use-after-free a concurrent kfunc caller. Move the publication below the cgroup path allocation so that every failure path after publication frees @sch through kobject_put(), whose release path defers the freeing by a grace period.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -6878,11 +6878,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, · 6877 unmodified lines | |||
| 6878 | sch->ops = *cmd->ops; | 6878 | sch->ops = *cmd->ops; |
| 6879 | } | 6879 | } |
| 6880 | 6880 | ||
| 6881 | rcu_assign_pointer(ops->priv, sch); | ||
| 6882 | |||
| 6883 | sch->kobj.kset = scx_kset; | ||
| 6884 | INIT_LIST_HEAD(&sch->all); | ||
| 6885 | |||
| 6886 | #ifdef CONFIG_EXT_SUB_SCHED | 6881 | #ifdef CONFIG_EXT_SUB_SCHED |
| 6887 | char *buf = kzalloc(PATH_MAX, GFP_KERNEL); | 6882 | char *buf = kzalloc(PATH_MAX, GFP_KERNEL); |
| 6888 | if (!buf) { | 6883 | if (!buf) { |
| @@ -6900,7 +6895,19 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, · 11 unmodified lines | |||
| 6900 | sch->cgrp = cgrp; | 6895 | sch->cgrp = cgrp; |
| 6901 | INIT_LIST_HEAD(&sch->children); | 6896 | INIT_LIST_HEAD(&sch->children); |
| 6902 | INIT_LIST_HEAD(&sch->sibling); | 6897 | INIT_LIST_HEAD(&sch->sibling); |
| 6898 | #endif /* CONFIG_EXT_SUB_SCHED */ | ||
| 6903 | 6899 | ||
| 6900 | /* | ||
| 6901 | * Publishing makes @sch visible to scx_prog_sched() readers. Failure | ||
| 6902 | * paths after this point must free @sch through kobject_put() whose | ||
| 6903 | * release path defers the actual freeing by an RCU grace period. | ||
| 6904 | */ | ||
| 6905 | rcu_assign_pointer(ops->priv, sch); | ||
| 6906 | |||
| 6907 | sch->kobj.kset = scx_kset; | ||
| 6908 | INIT_LIST_HEAD(&sch->all); | ||
| 6909 | |||
| 6910 | #ifdef CONFIG_EXT_SUB_SCHED | ||
| 6904 | if (parent) { | 6911 | if (parent) { |
| 6905 | /* | 6912 | /* |
| 6906 | * Pin @parent for @sch's lifetime. The kobject hierarchy pins | 6913 | * Pin @parent for @sch's lifetime. The kobject hierarchy pins |
| @@ -6955,7 +6962,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, · 48 unmodified lines | |||
| 6955 | 6962 | ||
| 6956 | #ifdef CONFIG_EXT_SUB_SCHED | 6963 | #ifdef CONFIG_EXT_SUB_SCHED |
| 6957 | err_free_lb_resched: | 6964 | err_free_lb_resched: |
| 6958 | RCU_INIT_POINTER(ops->priv, NULL); | ||
| 6959 | free_cpumask_var(sch->bypass_lb_resched_cpumask); | 6965 | free_cpumask_var(sch->bypass_lb_resched_cpumask); |
| 6960 | #endif | 6966 | #endif |
| 6961 | err_free_lb_cpumask: | 6967 | err_free_lb_cpumask: |