audit_remove_watch_rule() drops watch's reference to parent but then continues to work with it. That is not safe as parent can get freed once we drop our reference. The following is a trivial reproducer: mount -o loop image /mnt touch /mnt/file auditctl -w /mnt/file -p wax umount /mnt auditctl -D <crash in fsnotify_destroy_mark()> Grab our own reference in audit_remove_watch_rule() earlier to make sure mark does not get freed under us.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -457,13 +457,15 @@ void audit_remove_watch_rule(struct audit_krule *krule) · 456 unmodified lines | ||
| 457 | 457 | list_del(&krule->rlist); |
| 458 | 458 | |
| 459 | 459 | if (list_empty(&watch->rules)) { |
| 460 | + /* | |
| 461 | + * audit_remove_watch() drops our reference to 'parent' which | |
| 462 | + * can get freed. Grab our own reference to be safe. | |
| 463 | + */ | |
| 464 | + audit_get_parent(parent); | |
| 460 | 465 | audit_remove_watch(watch); |
| 461 | − | |
| 462 | − if (list_empty(&parent->watches)) { | |
| 463 | − audit_get_parent(parent); | |
| 466 | + if (list_empty(&parent->watches)) | |
| 464 | 467 | fsnotify_destroy_mark(&parent->mark, audit_watch_group); |
| 465 | − audit_put_parent(parent); | |
| 466 | − } | |
| 468 | + audit_put_parent(parent); | |
| 467 | 469 | } |
| 468 | 470 | } |
| 469 | 471 | |