diff --git a/sys/vfs/hammer/hammer_undo.c b/sys/vfs/hammer/hammer_undo.c index 0000000..1111111 100644 --- a/sys/vfs/hammer/hammer_undo.c +++ b/sys/vfs/hammer/hammer_undo.c @@ -121,16 +121,27 @@ /* * Enter the offset into our undo history. If there is an existing * undo we do not have to generate a new one. + * + * The undo history (rb_undo_root / undo_lru_list / undo_alloc) is + * shared across all frontends and must be mutated under undo_lock. + * Acquire the lock BEFORE hammer_enter_undo_history() and hold it + * across the FIFO lay-down below (it is released at function exit). + * Previously this lock was taken AFTER the history call, leaving the + * RB tree, TAILQ LRU list and undo_alloc counter unprotected against + * concurrent frontends -> KKASSERT(onode==NULL) panic on INVARIANTS + * kernels and TAILQ corruption (CWE-787) otherwise. (DF-0893) */ - if (hammer_enter_undo_history(hmp, zone_off, len) == EALREADY) + hammer_lock_ex(&hmp->undo_lock); + if (hammer_enter_undo_history(hmp, zone_off, len) == EALREADY) { + hammer_unlock(&hmp->undo_lock); return(0); + } root_volume = trans->rootvol; undomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX]; /* no undo recursion */ hammer_modify_volume_noundo(NULL, root_volume); - hammer_lock_ex(&hmp->undo_lock); /* undo had better not roll over (loose test) */ if (hammer_undo_space(trans) < len + HAMMER_BUFSIZE*3)