On Sat, Jul 02, 2022 at 10:23:16AM -0700, Linus Torvalds wrote: > On Fri, Jul 1, 2022 at 7:25 AM Alexander Potapenko <glider@xxxxxxxxxx> wrote: > > > > Under certain circumstances initialization of `unsigned seq` and > > `struct inode *inode` passed into step_into() may be skipped. > > In particular, if the call to lookup_fast() in walk_component() > > returns NULL, and lookup_slow() returns a valid dentry, then the > > `seq` and `inode` will remain uninitialized until the call to > > step_into() (see [1] for more info). > So while I think this needs to be fixed, I think I'd really prefer to > make the initialization and/or usage rules stricter or at least > clearer. Disclaimer: the bits below are nowhere near what I consider a decent explanation; this might serve as the first approximation, but I really need to get some sleep before I get it into coherent shape. 4 hours of sleep today... The rules are * no pathname resolution without successful path_init(). IOW, path_init() failure is an instant fuck off. * path_init() success sets nd->inode. In all cases. * nd->inode must be set - LOOKUP_RCU or not, we simply cannot proceed without it. * in non-RCU mode nd->inode must be equal to nd->path.dentry->d_inode. * in RCU mode nd->inode must be equal to a value observed in nd->path.dentry->d_inode while nd->path.dentry->d_seq had been equal to nd->seq. * step_into() gets a dentry/inode/seq triple. In non-RCU mode inode and seq are ignored; in RCU mode they must satisfy the same relationship we have for nd->path.dentry/nd->inode/nd->seq. > Of course, sometimes the "only get used for LOOKUP_RCU" is very very > unclear, because even without being an RCU lookup, step_into() will > save it into nd->inode/seq. So the values were "used", and > initializing them makes them valid, but then *that* copy must not then > be used unless RCU was set. You are misreading that (and I admit that it badly needs documentation). The whole point of step_into() is to move over to new place. nd->inode *MUST* be set on success, no matter what. > - I look at that follow_dotdot*() caller case, and think "that looks > very similar to the lookup_fast() case, but then we have *very* > different initialization rules". follow_dotdot() might as well lose inodep and seqp arguments - everything would've worked just as well without those. We would've gotten the same complaints about uninitialized values passed to step_into(), though. This if (unlikely(!parent)) error = step_into(nd, WALK_NOFOLLOW, nd->path.dentry, nd->inode, nd->seq); in handle_dots() probably contributes to confusion - it's the "we have stepped on .. in the root, just jump into whatever's mounted on it" case. In non-RCU case it looks like a use of nd->seq in non-RCU mode; however, in that case step_into() will end up ignoring the last two arguments. I'll post something more coherent after I get some sleep. Sorry... ;-/