When bpf_trampoline_update() fails before modify_fentry_multi()/ unregister_fentry_multi() is called, cur_image is unchanged (cur_image == old_image) and ftrace still calls into it. Freeing old_image in that case causes a UAF. Only free old_image when it differs from cur_image.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -1632,7 +1632,17 @@ static void bpf_trampoline_multi_attach_init(struct bpf_trampoline *tr) · 1631 unmodified lines | |||
| 1632 | 1632 | ||
| 1633 | static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr) | 1633 | static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr) |
| 1634 | { | 1634 | { |
| 1635 | if (tr->multi_attach.old_image) | 1635 | /* |
| 1636 | * Only free old_image if it is no longer the active image. | ||
| 1637 | * When bpf_trampoline_update() fails before modify_fentry_multi()/ | ||
| 1638 | * unregister_fentry_multi() is called, cur_image is unchanged | ||
| 1639 | * (cur_image == old_image) and ftrace still points to it. Freeing | ||
| 1640 | * it would cause a UAF when ftrace calls into the freed memory. | ||
| 1641 | * On success, cur_image is either a new image or NULL, so | ||
| 1642 | * old_image != cur_image means the image is stale. | ||
| 1643 | */ | ||
| 1644 | if (tr->multi_attach.old_image && | ||
| 1645 | tr->multi_attach.old_image != tr->cur_image) | ||
| 1636 | bpf_tramp_image_put(tr->multi_attach.old_image); | 1646 | bpf_tramp_image_put(tr->multi_attach.old_image); |
| 1637 | 1647 | ||
| 1638 | tr->multi_attach.old_image = NULL; | 1648 | tr->multi_attach.old_image = NULL; |