Re: [PATCH] fs/exec.c: Avoid a race in formats

From: Yun Levi
Date: Wed Feb 23 2022 - 22:14:20 EST


On Thu, Feb 24, 2022 at 12:00 PM Al Viro <viro@xxxxxxxxxxxxxxxxxx> wrote:
>
> On Thu, Feb 24, 2022 at 08:59:59AM +0900, Yun Levi wrote:
>
> > I think if someone wants to control their own binfmt via "ioctl" not
> > on time on LOAD.
> > For example, someone wants to control exec (notification,
> > allow/disallow and etc..)
> > and want to enable and disable own's control exec via binfmt reg / unreg
> > In that situation, While the module is loaded, binfmt is still live
> > and can be reused by
> > reg/unreg to enable/disable his exec' control.
>
> Er... So have your ->load_binary() start with
> if (I_want_it_disabled)
> return -ENOEXEC;
> and be done with that.
> The only caller of that thing is
> list_for_each_entry(fmt, &formats, lh) {
> if (!try_module_get(fmt->module))
> continue;
> read_unlock(&binfmt_lock);
>
> retval = fmt->load_binary(bprm);
>
> read_lock(&binfmt_lock);
> put_binfmt(fmt);
> if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
> read_unlock(&binfmt_lock);
> return retval;
> }
> }
> so returning -ENOEXEC is equivalent to not having it in the list.
> IDGI... Why bother unregistering/re-registering/etc.?

For example, someone wants to control exec via policy (allow or deny exec)
In that case, it just wants to confirm the policy NOT LOAD binary but
want to pass
the LOAD to the next binfmt (That's the reason __register_binfmt with insert).
So, To do this, register linux_binfmt with its own with load_binary
function like

if (this binary allow to run)
return -ENOEXEC; // pass to next binfmt to load that binary
else if (deny)
return -1;

And enable / disable is determined by registered or unregistered status.
That mean

// ioctl hook for enable
// enable by register binfmt
__register_binfmt(binfmt, 1);

// ioctl hook for disable
// disable by unreigster binfmt
__unregister_binfmt(binfmt);

Because, __unregister_binfmt isn't called int module __exit, but call
while the module is live,
it makes a problem.

It looks so strange, But in the case of the kernel without FTRACE,
BPF, KPROBE, etc
I think there's no other way to control exec running.
So I just do stupid test :)

But When I read Eric's answer,
I think __unregister_binfmt should be only called in the module __exit
function...
right?