Commit 54f586a9 authored by Johannes Berg's avatar Johannes Berg Committed by Kalle Valo

rfkill: make new event layout opt-in

Again new complaints surfaced that we had broken the ABI here,
although previously all the userspace tools had agreed that it
was their mistake and fixed it. Yet now there are cases (e.g.
RHEL) that want to run old userspace with newer kernels, and
thus are broken.

Since this is a bit of a whack-a-mole thing, change the whole
extensibility scheme of rfkill to no longer just rely on the
message lengths, but instead require userspace to opt in via a
new ioctl to a given maximum event size that it is willing to
understand.

By default, set that to RFKILL_EVENT_SIZE_V1 (8), so that the
behaviour for userspace not calling the ioctl will look as if
it's just running on an older kernel.

Fixes: 14486c82 ("rfkill: add a reason to the HW rfkill state")
Cc: stable@vger.kernel.org # 5.11+
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220316212749.16491491b270.Ifcb1950998330a596f29a2a162e00b7546a1d6d0@changeid
parent a93ccb5c
...@@ -159,8 +159,16 @@ struct rfkill_event_ext { ...@@ -159,8 +159,16 @@ struct rfkill_event_ext {
* old behaviour for all userspace, unless it explicitly opts in 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. * rules outlined here by using the new &struct rfkill_event_ext.
* *
* Userspace using &struct rfkill_event_ext must adhere to the following * Additionally, some other userspace (bluez, g-s-d) was reading with a
* rules * large size but as streaming reads rather than message-based, or with
* too strict checks for the returned size. So eventually, we completely
* reverted this, and extended messages need to be opted in to by using
* an ioctl:
*
* ioctl(fd, RFKILL_IOCTL_MAX_SIZE, sizeof(struct rfkill_event_ext));
*
* Userspace using &struct rfkill_event_ext and the ioctl must adhere to
* the following rules:
* *
* 1. accept short writes, optionally using them to detect that it's * 1. accept short writes, optionally using them to detect that it's
* running on an older kernel; * running on an older kernel;
...@@ -175,6 +183,8 @@ struct rfkill_event_ext { ...@@ -175,6 +183,8 @@ struct rfkill_event_ext {
#define RFKILL_IOC_MAGIC 'R' #define RFKILL_IOC_MAGIC 'R'
#define RFKILL_IOC_NOINPUT 1 #define RFKILL_IOC_NOINPUT 1
#define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) #define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT)
#define RFKILL_IOC_MAX_SIZE 2
#define RFKILL_IOCTL_MAX_SIZE _IOW(RFKILL_IOC_MAGIC, RFKILL_IOC_EXT_SIZE, __u32)
/* and that's all userspace gets */ /* and that's all userspace gets */
......
...@@ -78,6 +78,7 @@ struct rfkill_data { ...@@ -78,6 +78,7 @@ struct rfkill_data {
struct mutex mtx; struct mutex mtx;
wait_queue_head_t read_wait; wait_queue_head_t read_wait;
bool input_handler; bool input_handler;
u8 max_size;
}; };
...@@ -1153,6 +1154,8 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) ...@@ -1153,6 +1154,8 @@ static int rfkill_fop_open(struct inode *inode, struct file *file)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->max_size = RFKILL_EVENT_SIZE_V1;
INIT_LIST_HEAD(&data->events); INIT_LIST_HEAD(&data->events);
mutex_init(&data->mtx); mutex_init(&data->mtx);
init_waitqueue_head(&data->read_wait); init_waitqueue_head(&data->read_wait);
...@@ -1235,6 +1238,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf, ...@@ -1235,6 +1238,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf,
list); list);
sz = min_t(unsigned long, sizeof(ev->ev), count); sz = min_t(unsigned long, sizeof(ev->ev), count);
sz = min_t(unsigned long, sz, data->max_size);
ret = sz; ret = sz;
if (copy_to_user(buf, &ev->ev, sz)) if (copy_to_user(buf, &ev->ev, sz))
ret = -EFAULT; ret = -EFAULT;
...@@ -1249,6 +1253,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf, ...@@ -1249,6 +1253,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf,
static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos) size_t count, loff_t *pos)
{ {
struct rfkill_data *data = file->private_data;
struct rfkill *rfkill; struct rfkill *rfkill;
struct rfkill_event_ext ev; struct rfkill_event_ext ev;
int ret; int ret;
...@@ -1263,6 +1268,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, ...@@ -1263,6 +1268,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
* our API version even in a write() call, if it cares. * our API version even in a write() call, if it cares.
*/ */
count = min(count, sizeof(ev)); count = min(count, sizeof(ev));
count = min_t(size_t, count, data->max_size);
if (copy_from_user(&ev, buf, count)) if (copy_from_user(&ev, buf, count))
return -EFAULT; return -EFAULT;
...@@ -1322,31 +1328,47 @@ static int rfkill_fop_release(struct inode *inode, struct file *file) ...@@ -1322,31 +1328,47 @@ static int rfkill_fop_release(struct inode *inode, struct file *file)
return 0; return 0;
} }
#ifdef CONFIG_RFKILL_INPUT
static long rfkill_fop_ioctl(struct file *file, unsigned int cmd, static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
struct rfkill_data *data = file->private_data; struct rfkill_data *data = file->private_data;
int ret = -ENOSYS;
u32 size;
if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC) if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC)
return -ENOSYS; return -ENOSYS;
if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT)
return -ENOSYS;
mutex_lock(&data->mtx); mutex_lock(&data->mtx);
switch (_IOC_NR(cmd)) {
if (!data->input_handler) { #ifdef CONFIG_RFKILL_INPUT
if (atomic_inc_return(&rfkill_input_disabled) == 1) case RFKILL_IOC_NOINPUT:
printk(KERN_DEBUG "rfkill: input handler disabled\n"); if (!data->input_handler) {
data->input_handler = true; if (atomic_inc_return(&rfkill_input_disabled) == 1)
printk(KERN_DEBUG "rfkill: input handler disabled\n");
data->input_handler = true;
}
ret = 0;
break;
#endif
case RFKILL_IOC_MAX_SIZE:
if (get_user(size, (__u32 __user *)arg)) {
ret = -EFAULT;
break;
}
if (size < RFKILL_EVENT_SIZE_V1 || size > U8_MAX) {
ret = -EINVAL;
break;
}
data->max_size = size;
ret = 0;
break;
default:
break;
} }
mutex_unlock(&data->mtx); mutex_unlock(&data->mtx);
return 0; return ret;
} }
#endif
static const struct file_operations rfkill_fops = { static const struct file_operations rfkill_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -1355,10 +1377,8 @@ static const struct file_operations rfkill_fops = { ...@@ -1355,10 +1377,8 @@ static const struct file_operations rfkill_fops = {
.write = rfkill_fop_write, .write = rfkill_fop_write,
.poll = rfkill_fop_poll, .poll = rfkill_fop_poll,
.release = rfkill_fop_release, .release = rfkill_fop_release,
#ifdef CONFIG_RFKILL_INPUT
.unlocked_ioctl = rfkill_fop_ioctl, .unlocked_ioctl = rfkill_fop_ioctl,
.compat_ioctl = compat_ptr_ioctl, .compat_ioctl = compat_ptr_ioctl,
#endif
.llseek = no_llseek, .llseek = no_llseek,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment