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 | 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 | 1646 | bpf_tramp_image_put(tr->multi_attach.old_image); |
| 1637 | 1647 | |
| 1638 | 1648 | tr->multi_attach.old_image = NULL; |