Re: [PATCH v3] sysctl: handle table->maxlen robustly for proc_dobool

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jun 9, 2022 at 11:42 PM Luis Chamberlain <mcgrof@xxxxxxxxxx> wrote:
>
> Please Cc the original authors of code if sending some follow up
> possible enhancements.

Will do. +Jia He

>
> On Wed, May 25, 2022 at 02:50:50PM +0800, Muchun Song wrote:
> > Setting ->proc_handler to proc_dobool at the same time setting ->maxlen
> > to sizeof(int) is counter-intuitive, it is easy to make mistakes in the
> > future (When I first use proc_dobool() in my driver, I assign
> > sizeof(variable) to table->maxlen.  Then I found it was wrong, it should
> > be sizeof(int) which was very counter-intuitive).
>
> How did you find this out? If I change fs/lockd/svc.c's use I get
> compile warnings on at least x86_64.

I am writing a code like:

static bool optimize_vmemmap_enabled;

static struct ctl_table hugetlb_vmemmap_sysctls[] = {
        {
                .procname = "hugetlb_optimize_vmemmap",
                .data = &optimize_vmemmap_enabled,
                .maxlen = sizeof(optimize_vmemmap_enabled),
                .mode = 0644,
                .proc_handler = proc_dobool,
        },
        { }
};

At least I don't see any warnings from compiler. And I found
the assignment of ".data" should be "sizeof(int)", otherwise,
it does not work properly. It is a little weird to me.

>
> > For robustness,
> > rework proc_dobool() robustly.
>
> You mention robustness twice. Just say something like:
>
> To help make things clear, make the logic used by proc_dobool() very
> clear with regards to its requirement with working with bools.

Clearer! Thanks.

>
> > So it is an improvement not a real bug
> > fix.
> >
> > Signed-off-by: Muchun Song <songmuchun@xxxxxxxxxxxxx>
> > Cc: Luis Chamberlain <mcgrof@xxxxxxxxxx>
> > Cc: Kees Cook <keescook@xxxxxxxxxxxx>
> > Cc: Iurii Zaikin <yzaikin@xxxxxxxxxx>
> > ---
> > v3:
> >  - Update commit log.
> >
> > v2:
> >  - Reimplementing proc_dobool().
> >
> >  fs/lockd/svc.c  |  2 +-
> >  kernel/sysctl.c | 38 +++++++++++++++++++-------------------
> >  2 files changed, 20 insertions(+), 20 deletions(-)
> >
> > diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
> > index 59ef8a1f843f..6e48ee787f49 100644
> > --- a/fs/lockd/svc.c
> > +++ b/fs/lockd/svc.c
> > @@ -496,7 +496,7 @@ static struct ctl_table nlm_sysctls[] = {
> >       {
> >               .procname       = "nsm_use_hostnames",
> >               .data           = &nsm_use_hostnames,
> > -             .maxlen         = sizeof(int),
> > +             .maxlen         = sizeof(nsm_use_hostnames),
> >               .mode           = 0644,
> >               .proc_handler   = proc_dobool,
> >       },
>
> Should this be a separate patch? What about the rest of the kernel?

I afraid not. Since this change of proc_dobool will break the
"nsm_use_hostnames". It should be changed to
sizeof(nsm_use_hostnames) at the same time.

> I see it is only used once so the one commit should mention that also.

Well, will do.

>
> Or did chaning this as you have it now alter the way the kernel
> treats this sysctl? All these things would be useful to clarify
> in the commit log.

Make sense. I'll mention those things into commit log.

>
> > diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> > index e52b6e372c60..50a2c29efc94 100644
> > --- a/kernel/sysctl.c
> > +++ b/kernel/sysctl.c
> > @@ -423,21 +423,6 @@ static void proc_put_char(void **buf, size_t *size, char c)
> >       }
> >  }
> >
> > -static int do_proc_dobool_conv(bool *negp, unsigned long *lvalp,
> > -                             int *valp,
> > -                             int write, void *data)
> > -{
> > -     if (write) {
> > -             *(bool *)valp = *lvalp;
> > -     } else {
> > -             int val = *(bool *)valp;
> > -
> > -             *lvalp = (unsigned long)val;
> > -             *negp = false;
> > -     }
> > -     return 0;
> > -}
> > -
> >  static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
> >                                int *valp,
> >                                int write, void *data)
> > @@ -708,16 +693,31 @@ int do_proc_douintvec(struct ctl_table *table, int write,
> >   * @lenp: the size of the user buffer
> >   * @ppos: file position
> >   *
> > - * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
> > - * values from/to the user buffer, treated as an ASCII string.
> > + * Reads/writes up to table->maxlen/sizeof(bool) bool values from/to
> > + * the user buffer, treated as an ASCII string.
> >   *
> >   * Returns 0 on success.
> >   */
> >  int proc_dobool(struct ctl_table *table, int write, void *buffer,
> >               size_t *lenp, loff_t *ppos)
> >  {
> > -     return do_proc_dointvec(table, write, buffer, lenp, ppos,
> > -                             do_proc_dobool_conv, NULL);
> > +     struct ctl_table tmp = *table;
> > +     bool *data = table->data;
>
> Previously do_proc_douintvec() is called, and that checks if table->data
> is NULL previously before reading it and if so bails on
> __do_proc_dointvec() as follows:
>
>         if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
>                 *lenp = 0;
>                 return 0;
>         }
>
> Is it possible to have table->data be NULL? I think that's where the
> above check comes from.

At least now it cannot be NULL (no users do this now).

>
> And, so if it was false but not NULL, would it never do anything?

I think we can add the check of NULL in the future if it could be
happened, just like proc_dou8vec_minmax and proc_do_static_key
do (they do not check ->data as well).

>
> You can use lib/test_sysctl.c for this to proove / disprove correct
> functionality.

I didn't see the test for proc_bool in lib/test_sysctl.c. I think we can
add a separate patch later to add a test for proc_bool.

>
> > +     unsigned int val = READ_ONCE(*data);
> > +     int ret;
> > +
> > +     /* Do not support arrays yet. */
> > +     if (table->maxlen != sizeof(bool))
> > +             return -EINVAL;
>
> This is a separate change, and while I agree with it, as it simplifies
> our implementation and we don't want to add more array crap support,
> this should *alone* should be a separate commit.

If you agree reusing do_proc_douintvec to implement proc_dobool(),
I think a separate commit may be not suitable since do_proc_douintvec()
only support non-array. Mentioning this in commit log makes sense to me.

>
> > +
> > +     tmp.maxlen = sizeof(val);
>
> Why even set this as you do when we know it must be sizeof(bool)?
> Or would this break things given do_proc_douintvec() is used?

Since we reuse do_proc_douintvec(), which requires a uint type, to
get/set the value from/to the users. I think you can refer to the implementation
of proc_dou8vec_minmax().

>
> > +     tmp.data = &val;
> > +     ret = do_proc_douintvec(&tmp, write, buffer, lenp, ppos, NULL, NULL);
>
> Ugh, since we are avoiding arrays and we are only dealing with bools
> I'm inclined to just ask we simpify this a bool implementation which
> does something like do_proc_do_bool() but without array and is optimized
> just for bools.

The current implementation of __do_proc_douintvec() is already only deal
with non-array. Maybe it is better to reuse __do_proc_douintvec()? Otherwise,
we need to implement a similar functionality (do_proc_do_bool) like it but just
process bool type. I suspect the changes will be not small. I am wondering is it
value to do this? If yes, should we also rework proc_dou8vec_minmax() as well?

Thanks.

>
> The current hoops to read this code is simplly irritating
>
>   Luis
>
> > +     if (ret)
> > +             return ret;
> > +     if (write)
> > +             WRITE_ONCE(*data, val ? true : false);
> > +     return 0;
> >  }
> >
> >  /**
> > --
> > 2.11.0
> >



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux