Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 14486c82612a ("rfkill: add a reason to the HW rfkill state")
Cc: stable@vger.kernel.org # 5.11+
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220316212749.16491491b270.Ifcb1950998330a596f29a2a162e00b7546a1d6d0@changeid

authored by

Johannes Berg and committed by
Kalle Valo
54f586a9 a93ccb5c

+46 -16
+12 -2
include/uapi/linux/rfkill.h
··· 159 159 * old behaviour for all userspace, unless it explicitly opts in to the 160 160 * rules outlined here by using the new &struct rfkill_event_ext. 161 161 * 162 - * Userspace using &struct rfkill_event_ext must adhere to the following 163 - * rules 162 + * Additionally, some other userspace (bluez, g-s-d) was reading with a 163 + * large size but as streaming reads rather than message-based, or with 164 + * too strict checks for the returned size. So eventually, we completely 165 + * reverted this, and extended messages need to be opted in to by using 166 + * an ioctl: 167 + * 168 + * ioctl(fd, RFKILL_IOCTL_MAX_SIZE, sizeof(struct rfkill_event_ext)); 169 + * 170 + * Userspace using &struct rfkill_event_ext and the ioctl must adhere to 171 + * the following rules: 164 172 * 165 173 * 1. accept short writes, optionally using them to detect that it's 166 174 * running on an older kernel; ··· 183 175 #define RFKILL_IOC_MAGIC 'R' 184 176 #define RFKILL_IOC_NOINPUT 1 185 177 #define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) 178 + #define RFKILL_IOC_MAX_SIZE 2 179 + #define RFKILL_IOCTL_MAX_SIZE _IOW(RFKILL_IOC_MAGIC, RFKILL_IOC_EXT_SIZE, __u32) 186 180 187 181 /* and that's all userspace gets */ 188 182
+34 -14
net/rfkill/core.c
··· 78 78 struct mutex mtx; 79 79 wait_queue_head_t read_wait; 80 80 bool input_handler; 81 + u8 max_size; 81 82 }; 82 83 83 84 ··· 1154 1153 if (!data) 1155 1154 return -ENOMEM; 1156 1155 1156 + data->max_size = RFKILL_EVENT_SIZE_V1; 1157 + 1157 1158 INIT_LIST_HEAD(&data->events); 1158 1159 mutex_init(&data->mtx); 1159 1160 init_waitqueue_head(&data->read_wait); ··· 1238 1235 list); 1239 1236 1240 1237 sz = min_t(unsigned long, sizeof(ev->ev), count); 1238 + sz = min_t(unsigned long, sz, data->max_size); 1241 1239 ret = sz; 1242 1240 if (copy_to_user(buf, &ev->ev, sz)) 1243 1241 ret = -EFAULT; ··· 1253 1249 static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, 1254 1250 size_t count, loff_t *pos) 1255 1251 { 1252 + struct rfkill_data *data = file->private_data; 1256 1253 struct rfkill *rfkill; 1257 1254 struct rfkill_event_ext ev; 1258 1255 int ret; ··· 1268 1263 * our API version even in a write() call, if it cares. 1269 1264 */ 1270 1265 count = min(count, sizeof(ev)); 1266 + count = min_t(size_t, count, data->max_size); 1271 1267 if (copy_from_user(&ev, buf, count)) 1272 1268 return -EFAULT; 1273 1269 ··· 1328 1322 return 0; 1329 1323 } 1330 1324 1331 - #ifdef CONFIG_RFKILL_INPUT 1332 1325 static long rfkill_fop_ioctl(struct file *file, unsigned int cmd, 1333 1326 unsigned long arg) 1334 1327 { 1335 1328 struct rfkill_data *data = file->private_data; 1329 + int ret = -ENOSYS; 1330 + u32 size; 1336 1331 1337 1332 if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC) 1338 1333 return -ENOSYS; 1339 1334 1340 - if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT) 1341 - return -ENOSYS; 1342 - 1343 1335 mutex_lock(&data->mtx); 1344 - 1345 - if (!data->input_handler) { 1346 - if (atomic_inc_return(&rfkill_input_disabled) == 1) 1347 - printk(KERN_DEBUG "rfkill: input handler disabled\n"); 1348 - data->input_handler = true; 1336 + switch (_IOC_NR(cmd)) { 1337 + #ifdef CONFIG_RFKILL_INPUT 1338 + case RFKILL_IOC_NOINPUT: 1339 + if (!data->input_handler) { 1340 + if (atomic_inc_return(&rfkill_input_disabled) == 1) 1341 + printk(KERN_DEBUG "rfkill: input handler disabled\n"); 1342 + data->input_handler = true; 1343 + } 1344 + ret = 0; 1345 + break; 1346 + #endif 1347 + case RFKILL_IOC_MAX_SIZE: 1348 + if (get_user(size, (__u32 __user *)arg)) { 1349 + ret = -EFAULT; 1350 + break; 1351 + } 1352 + if (size < RFKILL_EVENT_SIZE_V1 || size > U8_MAX) { 1353 + ret = -EINVAL; 1354 + break; 1355 + } 1356 + data->max_size = size; 1357 + ret = 0; 1358 + break; 1359 + default: 1360 + break; 1349 1361 } 1350 - 1351 1362 mutex_unlock(&data->mtx); 1352 1363 1353 - return 0; 1364 + return ret; 1354 1365 } 1355 - #endif 1356 1366 1357 1367 static const struct file_operations rfkill_fops = { 1358 1368 .owner = THIS_MODULE, ··· 1377 1355 .write = rfkill_fop_write, 1378 1356 .poll = rfkill_fop_poll, 1379 1357 .release = rfkill_fop_release, 1380 - #ifdef CONFIG_RFKILL_INPUT 1381 1358 .unlocked_ioctl = rfkill_fop_ioctl, 1382 1359 .compat_ioctl = compat_ptr_ioctl, 1383 - #endif 1384 1360 .llseek = no_llseek, 1385 1361 }; 1386 1362