All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sedat Dilek <sedat.dilek@gmail.com>
To: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-wireless@vger.kernel.org,
	Johannes Berg <johannes.berg@intel.com>,
	stable@vger.kernel.org, Takashi Iwai <tiwai@suse.de>
Subject: Re: [PATCH] rfkill: revert back to old userspace API by default
Date: Tue, 6 Apr 2021 08:33:00 +0200	[thread overview]
Message-ID: <CA+icZUX0ToCQouzVaYfdPNyMphz27=sjVgnsprJWSAACVo4_jw@mail.gmail.com> (raw)
In-Reply-To: <20210319232510.f1a139cfdd9c.Ic5c7c9d1d28972059e132ea653a21a427c326678@changeid>

On Fri, Mar 19, 2021 at 11:26 PM Johannes Berg
<johannes@sipsolutions.net> wrote:
>
> From: Johannes Berg <johannes.berg@intel.com>
>
> Recompiling with the new extended version of struct rfkill_event
> broke systemd in *two* ways:
>  - It used "sizeof(struct rfkill_event)" to read the event, but
>    then complained if it actually got something != 8, this broke
>    it on new kernels (that include the updated API);
>  - It used sizeof(struct rfkill_event) to write a command, but
>    didn't implement the intended expansion protocol where the
>    kernel returns only how many bytes it accepted, and errored
>    out due to the unexpected smaller size on kernels that didn't
>    include the updated API.
>
> Even though systemd has now been fixed, that fix may not be always
> deployed, and other applications could potentially have similar
> issues.
>
> As such, in the interest of avoiding regressions, revert the
> default API "struct rfkill_event" back to the original size.
>
> Instead, add a new "struct rfkill_event_ext" that extends it by
> the new field, and even more clearly document that applications
> should be prepared for extensions in two ways:
>  * write might only accept fewer bytes on older kernels, and
>    will return how many to let userspace know which data may
>    have been ignored;
>  * read might return anything between 8 (the original size) and
>    whatever size the application sized its buffer at, indicating
>    how much event data was supported by the kernel.
>
> Perhaps that will help avoid such issues in the future and we
> won't have to come up with another version of the struct if we
> ever need to extend it again.
>
> Applications that want to take advantage of the new field will
> have to be modified to use struct rfkill_event_ext instead now,
> which comes with the danger of them having already been updated
> to use it from 'struct rfkill_event', but I found no evidence
> of that, and it's still relatively new.
>
> Cc: stable@vger.kernel.org # 5.11
> Reported-by: Takashi Iwai <tiwai@suse.de>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>

Thanks for the patch.

Tested-by: Sedat Dilek <sedat.dilek@gmail.com> # LLVM/Clang v12.0.0-r4 (x86-64)

- Sedat -

> ---
>  include/uapi/linux/rfkill.h | 80 +++++++++++++++++++++++++++++++------
>  net/rfkill/core.c           |  7 ++--
>  2 files changed, 72 insertions(+), 15 deletions(-)
>
> diff --git a/include/uapi/linux/rfkill.h b/include/uapi/linux/rfkill.h
> index 03e8af87b364..9b77cfc42efa 100644
> --- a/include/uapi/linux/rfkill.h
> +++ b/include/uapi/linux/rfkill.h
> @@ -86,34 +86,90 @@ enum rfkill_hard_block_reasons {
>   * @op: operation code
>   * @hard: hard state (0/1)
>   * @soft: soft state (0/1)
> + *
> + * Structure used for userspace communication on /dev/rfkill,
> + * used for events from the kernel and control to the kernel.
> + */
> +struct rfkill_event {
> +       __u32 idx;
> +       __u8  type;
> +       __u8  op;
> +       __u8  soft;
> +       __u8  hard;
> +} __attribute__((packed));
> +
> +/**
> + * struct rfkill_event_ext - events for userspace on /dev/rfkill
> + * @idx: index of dev rfkill
> + * @type: type of the rfkill struct
> + * @op: operation code
> + * @hard: hard state (0/1)
> + * @soft: soft state (0/1)
>   * @hard_block_reasons: valid if hard is set. One or several reasons from
>   *     &enum rfkill_hard_block_reasons.
>   *
>   * Structure used for userspace communication on /dev/rfkill,
>   * used for events from the kernel and control to the kernel.
> + *
> + * See the extensibility docs below.
>   */
> -struct rfkill_event {
> +struct rfkill_event_ext {
>         __u32 idx;
>         __u8  type;
>         __u8  op;
>         __u8  soft;
>         __u8  hard;
> +
> +       /*
> +        * older kernels will accept/send only up to this point,
> +        * and if extended further up to any chunk marked below
> +        */
> +
>         __u8  hard_block_reasons;
>  } __attribute__((packed));
>
> -/*
> - * We are planning to be backward and forward compatible with changes
> - * to the event struct, by adding new, optional, members at the end.
> - * When reading an event (whether the kernel from userspace or vice
> - * versa) we need to accept anything that's at least as large as the
> - * version 1 event size, but might be able to accept other sizes in
> - * the future.
> +/**
> + * DOC: Extensibility
> + *
> + * Originally, we had planned to allow backward and forward compatible
> + * changes by just adding fields at the end of the structure that are
> + * then not reported on older kernels on read(), and not written to by
> + * older kernels on write(), with the kernel reporting the size it did
> + * accept as the result.
> + *
> + * This would have allowed userspace to detect on read() and write()
> + * which kernel structure version it was dealing with, and if was just
> + * recompiled it would have gotten the new fields, but obviously not
> + * accessed them, but things should've continued to work.
> + *
> + * Unfortunately, while actually exercising this mechanism to add the
> + * hard block reasons field, we found that userspace (notably systemd)
> + * did all kinds of fun things not in line with this scheme:
> + *
> + * 1. treat the (expected) short writes as an error;
> + * 2. ask to read sizeof(struct rfkill_event) but then compare the
> + *    actual return value to RFKILL_EVENT_SIZE_V1 and treat any
> + *    mismatch as an error.
> + *
> + * As a consequence, just recompiling with a new struct version caused
> + * things to no longer work correctly on old and new kernels.
> + *
> + * Hence, we've rolled back &struct rfkill_event to the original version
> + * and added &struct rfkill_event_ext. This effectively reverts to the
> + * old behaviour for all userspace, unless it explicitly opts in to the
> + * rules outlined here by using the new &struct rfkill_event_ext.
> + *
> + * Userspace using &struct rfkill_event_ext must adhere to the following
> + * rules
>   *
> - * One exception is the kernel -- we already have two event sizes in
> - * that we've made the 'hard' member optional since our only option
> - * is to ignore it anyway.
> + * 1. accept short writes, optionally using them to detect that it's
> + *    running on an older kernel;
> + * 2. accept short reads, knowing that this means it's running on an
> + *    older kernel;
> + * 3. treat reads that are as long as requested as acceptable, not
> + *    checking against RFKILL_EVENT_SIZE_V1 or such.
>   */
> -#define RFKILL_EVENT_SIZE_V1   8
> +#define RFKILL_EVENT_SIZE_V1   sizeof(struct rfkill_event)
>
>  /* ioctl for turning off rfkill-input (if present) */
>  #define RFKILL_IOC_MAGIC       'R'
> diff --git a/net/rfkill/core.c b/net/rfkill/core.c
> index 68d6ef9e59fc..ac15a944573f 100644
> --- a/net/rfkill/core.c
> +++ b/net/rfkill/core.c
> @@ -69,7 +69,7 @@ struct rfkill {
>
>  struct rfkill_int_event {
>         struct list_head        list;
> -       struct rfkill_event     ev;
> +       struct rfkill_event_ext ev;
>  };
>
>  struct rfkill_data {
> @@ -253,7 +253,8 @@ static void rfkill_global_led_trigger_unregister(void)
>  }
>  #endif /* CONFIG_RFKILL_LEDS */
>
> -static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
> +static void rfkill_fill_event(struct rfkill_event_ext *ev,
> +                             struct rfkill *rfkill,
>                               enum rfkill_operation op)
>  {
>         unsigned long flags;
> @@ -1237,7 +1238,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
>                                 size_t count, loff_t *pos)
>  {
>         struct rfkill *rfkill;
> -       struct rfkill_event ev;
> +       struct rfkill_event_ext ev;
>         int ret;
>
>         /* we don't need the 'hard' variable but accept it */
> --
> 2.30.2
>

      reply	other threads:[~2021-04-06  6:33 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-19 22:25 [PATCH] rfkill: revert back to old userspace API by default Johannes Berg
2021-04-06  6:33 ` Sedat Dilek [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CA+icZUX0ToCQouzVaYfdPNyMphz27=sjVgnsprJWSAACVo4_jw@mail.gmail.com' \
    --to=sedat.dilek@gmail.com \
    --cc=johannes.berg@intel.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-wireless@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.