No subject
Sun Mar 29 03:45:02 AEDT 2026
overlayfs/backing-file design, specifically how overlayfs hides
multiple files under a single file so that it can plug into the
existing VFS/userspace paradigm of a single file. While the design
and abstraction are no doubt very clever things, and for the most part
they "just work", there are definitely some corner cases that require
special handling, e.g. LSM access controls around mprotect(). In my
opinion, the burden of hiding any ugliness associated with this
special handling lies with the subsystem implementing the abstraction,
which is why I was pushing for a solution where the VFS and/or
backing-file layer would provide the user file (or a stand-in) for the
LSMs to use. Unfortunately, that was not something the overlayfs and
VFS communities were willing to tolerate, so those of us in the LSM
space were left with a terrible choice: accept that the overlayfs/VFS
folks don't care and hack around the shortcomings of overlayfs, or
leave a public vulnerability for an unknown period of time while the
overlayfs/VFS folks argue over a solution, with the a non-trivial
chance that the LSMs would need to hack around the problem anyway.
That's my view of were things were at, and why I begrudgingly took
this approach. I'm sure you have your own perspecitve, and I'm not
going to be surprised if you view this primarily as a LSM problem;
it's a common viewpoint amongst Linux kernel maintainers not
responsible for a LSM.
> It is also what Miklos had initially suggested.
Perhaps I've lost the mail, but going back to when this issue was
first discovered, I don't see anything from Miklos relating to this in
either my inbox or the mailing lists.
> > diff --git a/fs/file_table.c b/fs/file_table.c
> > index aaa5faaace1e..0bdc26cae138 100644
> > --- a/fs/file_table.c
> > +++ b/fs/file_table.c
> > @@ -50,6 +50,7 @@ struct backing_file {
> > struct path user_path;
> > freeptr_t bf_freeptr;
> > };
>
> Shouldn't we wrap this with
> #ifdef CONFIG_SECURITY
Sure, I'll change that.
> > + void *security;
>
> please initialize it in init_file()
We lack a clean way to access the backing_file struct in init_file().
I placed the security_backing_file_alloc() initializer in
alloc_empty_backing_file() as it was the first place where we could
both allocate the necessary LSM blob and initialize it with the data
from the user file at the same time.
If you want to intialize backing_file->security in init_file(),
init_file() we will need to add a FMODE_BACKING check in init_file and
split the security_backing_file_alloc() hook into two: one in
init_file() to do the allocation and basic init, one in
alloc_empty_backing_file() to capture the necessary user file data.
Do you still prefer to move the backing_file->security initializtion
into init_file()?
> > +void *backing_file_security(const struct file *f)
> > +{
> > + return backing_file(f)->security;
>
> I think LSM code should be completely responsible for this ptr
> assignment/free so you should export
>
> void **backing_file_security_ptr(const struct file *f)
> {
> return &backing_file(f)->security;
> }
Doing so would require us to also move the backing_file struct
definition into include/linux/backing-file.h (or similar), which I
tried very hard to avoid as I suspected you would not approve of that.
I figured if you had wanted to expose the struct definition you would
have defined it in backing-file.h as opposed to file_table.c.
Would you like me to move the backing_file struct definition into
include/linux/backing-file.h?
> > @@ -73,8 +79,11 @@ static inline void file_free(struct file *f)
> > percpu_counter_dec(&nr_files);
> > put_cred(f->f_cred);
> > if (unlikely(f->f_mode & FMODE_BACKING)) {
> > - path_put(backing_file_user_path(f));
> > - kmem_cache_free(bfilp_cachep, backing_file(f));
> > + struct backing_file *ff =3D backing_file(f);
> > +
> > + security_backing_file_free(&ff->security);
>
> Why do you need to add this in vfs code?
>
> Can't you do the same in security_file_free(f)?
> if (unlikely(f->f_mode & FMODE_BACKING))
> security_backing_file_free(backing_file_security_ptr(f));
See my comments above regarding the visibility of the backing_file struct.
> > + path_put(&ff->user_path);
> > + kmem_cache_free(bfilp_cachep, ff);
> > } else {
> > kmem_cache_free(filp_cachep, f);
> > }
> > @@ -290,7 +299,8 @@ struct file *alloc_empty_file_noaccount(int flags, =
const struct cred *cred)
> > * This is only for kernel internal use, and the allocate file must no=
t be
> > * installed into file tables or such.
> > */
> > -struct file *alloc_empty_backing_file(int flags, const struct cred *cr=
ed)
> > +struct file *alloc_empty_backing_file(int flags, const struct cred *cr=
ed,
> > + const struct file *user_file)
> > {
> > struct backing_file *ff;
> > int error;
> > @@ -306,6 +316,11 @@ struct file *alloc_empty_backing_file(int flags, c=
onst struct cred *cred)
> > }
> >
> > ff->file.f_mode |=3D FMODE_BACKING | FMODE_NOACCOUNT;
> > + error =3D security_backing_file_alloc(&ff->security, user_file)=
;> + if (unlikely(error)) {
> > + fput(&ff->file);
> > + return ERR_PTR(error);
> > + }
> > return &ff->file;
> > }
> > EXPORT_SYMBOL_GPL(alloc_empty_backing_file);
>
> Maybe, and I am not sure,
> alloc_empty_backing_file() should call ONLY
> error =3D security_backing_file_alloc(&ff->file, user_file);
>
> Instead of security_file_alloc() AND security_backing_file_alloc()
> and security_backing_file_alloc() can make use of
> backing_file_security_ptr() accessor internally?
This is another case of the code being structured so that we don't
need to expose the backing_file struct definition to the LSMs. If you
would prefer to expose the backing_file struct in include/linux I can
probably make a few additional simplifications to the code.
--=20
paul-moore.com
More information about the Linux-erofs
mailing list