The change is very straightforward, and takes advantage of the (very minor) efficiency improvements in copy_struct_from_user() -- that the memchr_inv() check is done on a buffer instead of one-at-at-time with get_user().
Signed-off-by: Aleksa Sarai cyphar@cyphar.com --- kernel/events/core.c | 45 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 37 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c index 0463c1151bae..fe5f58443ba6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -10498,55 +10498,26 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, u32 size; int ret;
- if (!access_ok(uattr, PERF_ATTR_SIZE_VER0)) - return -EFAULT; - - /* - * zero the full structure, so that a short copy will be nice. - */ + /* Zero the full structure, so that a short copy will be nice. */ memset(attr, 0, sizeof(*attr));
ret = get_user(size, &uattr->size); if (ret) return ret;
- if (size > PAGE_SIZE) /* silly large */ - goto err_size; - - if (!size) /* abi compat */ + /* ABI compatibility quirk: */ + if (!size) size = PERF_ATTR_SIZE_VER0; - if (size < PERF_ATTR_SIZE_VER0) goto err_size;
- /* - * If we're handed a bigger struct than we know of, - * ensure all the unknown bits are 0 - i.e. new - * user-space does not rely on any kernel feature - * extensions we dont know about yet. - */ - if (size > sizeof(*attr)) { - unsigned char __user *addr; - unsigned char __user *end; - unsigned char val; - - addr = (void __user *)uattr + sizeof(*attr); - end = (void __user *)uattr + size; - - for (; addr < end; addr++) { - ret = get_user(val, addr); - if (ret) - return ret; - if (val) - goto err_size; - } - size = sizeof(*attr); + ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size); + if (ret) { + if (ret == -E2BIG) + goto err_size; + return ret; }
- ret = copy_from_user(attr, uattr, size); - if (ret) - return -EFAULT; - attr->size = size;
if (attr->__reserved_1)