Let caller specify allocation.
Keep the existing calls with GFP_USER, and enforce the existing
defense against log spam from userspace with __GFP_NOWARN.

Signed-off-by: Pascal Bouchareine <ka...@tfz.net>
---
 drivers/dma-buf/dma-buf.c                  |  2 +-
 drivers/gpu/drm/i915/i915_debugfs_params.c |  2 +-
 drivers/gpu/drm/vc4/vc4_bo.c               |  3 +-
 drivers/input/misc/uinput.c                |  2 +-
 drivers/s390/char/keyboard.c               |  3 +-
 drivers/vfio/vfio.c                        |  3 +-
 drivers/virt/fsl_hypervisor.c              |  4 +--
 fs/f2fs/file.c                             |  3 +-
 fs/fsopen.c                                |  6 ++--
 fs/namespace.c                             |  2 +-
 fs/nfs/fs_context.c                        |  8 +++--
 fs/xfs/xfs_ioctl.c                         |  2 +-
 include/linux/string.h                     |  2 +-
 kernel/events/core.c                       |  2 +-
 kernel/module.c                            |  2 +-
 kernel/trace/trace_event_perf.c            |  2 +-
 mm/util.c                                  | 34 +++++++++++++---------
 net/core/pktgen.c                          |  2 +-
 security/keys/dh.c                         |  3 +-
 security/keys/keyctl.c                     | 17 +++++++----
 security/keys/keyctl_pkey.c                |  2 +-
 21 files changed, 63 insertions(+), 43 deletions(-)

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 1ca609f66fdf..3d94ba811f4b 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -326,7 +326,7 @@ static __poll_t dma_buf_poll(struct file *file, poll_table 
*poll)
  */
 static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
 {
-       char *name = strndup_user(buf, DMA_BUF_NAME_LEN);
+       char *name = strndup_user(buf, DMA_BUF_NAME_LEN, GFP_USER);
        long ret = 0;
 
        if (IS_ERR(name))
diff --git a/drivers/gpu/drm/i915/i915_debugfs_params.c 
b/drivers/gpu/drm/i915/i915_debugfs_params.c
index 62b2c5f0495d..4c0a77e15c09 100644
--- a/drivers/gpu/drm/i915/i915_debugfs_params.c
+++ b/drivers/gpu/drm/i915/i915_debugfs_params.c
@@ -142,7 +142,7 @@ static ssize_t i915_param_charp_write(struct file *file,
        kernel_param_lock(THIS_MODULE);
 
        old = *s;
-       new = strndup_user(ubuf, PAGE_SIZE);
+       new = strndup_user(ubuf, PAGE_SIZE, GFP_USER);
        if (IS_ERR(new)) {
                len = PTR_ERR(new);
                goto out;
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 72d30d90b856..deb2c4957a6f 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -1072,7 +1072,8 @@ int vc4_label_bo_ioctl(struct drm_device *dev, void *data,
        if (!args->len)
                return -EINVAL;
 
-       name = strndup_user(u64_to_user_ptr(args->name), args->len + 1);
+       name = strndup_user(u64_to_user_ptr(args->name), args->len + 1,
+                               GFP_USER);
        if (IS_ERR(name))
                return PTR_ERR(name);
 
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index f2593133e524..11627a4b4d87 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -926,7 +926,7 @@ static long uinput_ioctl_handler(struct file *file, 
unsigned int cmd,
                        goto out;
                }
 
-               phys = strndup_user(p, 1024);
+               phys = strndup_user(p, 1024, GFP_USER);
                if (IS_ERR(phys)) {
                        retval = PTR_ERR(phys);
                        goto out;
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index 567aedc03c76..8e58921d5db4 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -464,7 +464,8 @@ do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user 
*u_kbs,
        case KDSKBSENT:
                if (!perm)
                        return -EPERM;
-               p = strndup_user(u_kbs->kb_string, sizeof(u_kbs->kb_string));
+               p = strndup_user(u_kbs->kb_string,
+                       sizeof(u_kbs->kb_string), GFP_USER);
                if (IS_ERR(p))
                        return PTR_ERR(p);
                kfree(kbd->func_table[kb_func]);
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 580099afeaff..d55aae6661eb 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -1547,7 +1547,8 @@ static long vfio_group_fops_unl_ioctl(struct file *filep,
        {
                char *buf;
 
-               buf = strndup_user((const char __user *)arg, PAGE_SIZE);
+               buf = strndup_user((const char __user *)arg, PAGE_SIZE,
+                                       GFP_USER);
                if (IS_ERR(buf))
                        return PTR_ERR(buf);
 
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c
index 1b0b11b55d2a..142c74aab2b0 100644
--- a/drivers/virt/fsl_hypervisor.c
+++ b/drivers/virt/fsl_hypervisor.c
@@ -346,11 +346,11 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user 
*p, int set)
        upropname = (char __user *)(uintptr_t)param.propname;
        upropval = (void __user *)(uintptr_t)param.propval;
 
-       path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
+       path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN, GFP_USER);
        if (IS_ERR(path))
                return PTR_ERR(path);
 
-       propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
+       propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN, GFP_USER);
        if (IS_ERR(propname)) {
                ret = PTR_ERR(propname);
                goto err_free_path;
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 3268f8dd59bb..1d61dcee510d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3395,7 +3395,8 @@ static int f2fs_set_volume_name(struct file *filp, 
unsigned long arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       vbuf = strndup_user((const char __user *)arg, FSLABEL_MAX);
+       vbuf = strndup_user((const char __user *)arg, FSLABEL_MAX,
+               GFP_USER);
        if (IS_ERR(vbuf))
                return PTR_ERR(vbuf);
 
diff --git a/fs/fsopen.c b/fs/fsopen.c
index 2fa3f241b762..c17ef9ee455c 100644
--- a/fs/fsopen.c
+++ b/fs/fsopen.c
@@ -125,7 +125,7 @@ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, 
unsigned int, flags)
        if (flags & ~FSOPEN_CLOEXEC)
                return -EINVAL;
 
-       fs_name = strndup_user(_fs_name, PAGE_SIZE);
+       fs_name = strndup_user(_fs_name, PAGE_SIZE, GFP_USER);
        if (IS_ERR(fs_name))
                return PTR_ERR(fs_name);
 
@@ -381,7 +381,7 @@ SYSCALL_DEFINE5(fsconfig,
        }
 
        if (_key) {
-               param.key = strndup_user(_key, 256);
+               param.key = strndup_user(_key, 256, GFP_USER);
                if (IS_ERR(param.key)) {
                        ret = PTR_ERR(param.key);
                        goto out_f;
@@ -394,7 +394,7 @@ SYSCALL_DEFINE5(fsconfig,
                break;
        case FSCONFIG_SET_STRING:
                param.type = fs_value_is_string;
-               param.string = strndup_user(_value, 256);
+               param.string = strndup_user(_value, 256, GFP_USER);
                if (IS_ERR(param.string)) {
                        ret = PTR_ERR(param.string);
                        goto out_key;
diff --git a/fs/namespace.c b/fs/namespace.c
index 4a0f600a3328..1d9da91fbd2e 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3099,7 +3099,7 @@ void *copy_mount_options(const void __user * data)
 
 char *copy_mount_string(const void __user *data)
 {
-       return data ? strndup_user(data, PATH_MAX) : NULL;
+       return data ? strndup_user(data, PATH_MAX, GFP_USER) : NULL;
 }
 
 /*
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index ccc88be88d6a..fcdaeca51ca9 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -1077,18 +1077,20 @@ static int nfs4_parse_monolithic(struct fs_context *fc,
                } else
                        ctx->selected_flavor = RPC_AUTH_UNIX;
 
-               c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
+               c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN,
+                                       GFP_USER);
                if (IS_ERR(c))
                        return PTR_ERR(c);
                ctx->nfs_server.hostname = c;
 
-               c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
+               c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN,
+                                       GFP_USER);
                if (IS_ERR(c))
                        return PTR_ERR(c);
                ctx->nfs_server.export_path = c;
                dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c);
 
-               c = strndup_user(data->client_addr.data, 16);
+               c = strndup_user(data->client_addr.data, 16, GFP_USER);
                if (IS_ERR(c))
                        return PTR_ERR(c);
                ctx->client_address = c;
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index a190212ca85d..216ab920c6b3 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -546,7 +546,7 @@ xfs_ioc_attrmulti_one(
        if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE))
                return -EINVAL;
 
-       name = strndup_user(uname, MAXNAMELEN);
+       name = strndup_user(uname, MAXNAMELEN, GFP_USER);
        if (IS_ERR(name))
                return PTR_ERR(name);
 
diff --git a/include/linux/string.h b/include/linux/string.h
index 9b7a0632e87a..3eb69aee484d 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -9,7 +9,7 @@
 #include <stdarg.h>
 #include <uapi/linux/string.h>
 
-extern char *strndup_user(const char __user *, long);
+extern char *strndup_user(const char __user *, long, gfp_t);
 extern void *memdup_user(const void __user *, size_t);
 extern void *vmemdup_user(const void __user *, size_t);
 extern void *memdup_user_nul(const void __user *, size_t);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 856d98c36f56..3b0621563d7f 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -10072,7 +10072,7 @@ static int perf_event_set_filter(struct perf_event 
*event, void __user *arg)
        int ret = -EINVAL;
        char *filter_str;
 
-       filter_str = strndup_user(arg, PAGE_SIZE);
+       filter_str = strndup_user(arg, PAGE_SIZE, GFP_USER);
        if (IS_ERR(filter_str))
                return PTR_ERR(filter_str);
 
diff --git a/kernel/module.c b/kernel/module.c
index aa183c9ac0a2..566c9ddb86d3 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3872,7 +3872,7 @@ static int load_module(struct load_info *info, const char 
__user *uargs,
        flush_module_icache(mod);
 
        /* Now copy in args */
-       mod->args = strndup_user(uargs, ~0UL >> 1);
+       mod->args = strndup_user(uargs, ~0UL >> 1, GFP_USER);
        if (IS_ERR(mod->args)) {
                err = PTR_ERR(mod->args);
                goto free_arch_cleanup;
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 643e0b19920d..48569b39d1f2 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -310,7 +310,7 @@ int perf_uprobe_init(struct perf_event *p_event,
                return -EINVAL;
 
        path = strndup_user(u64_to_user_ptr(p_event->attr.uprobe_path),
-                           PATH_MAX);
+                           PATH_MAX, GFP_USER);
        if (IS_ERR(path)) {
                ret = PTR_ERR(path);
                return (ret == -EINVAL) ? -E2BIG : ret;
diff --git a/mm/util.c b/mm/util.c
index c63c8e47be57..cec32cc6d434 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -156,20 +156,13 @@ char *kmemdup_nul(const char *s, size_t len, gfp_t gfp)
 }
 EXPORT_SYMBOL(kmemdup_nul);
 
-/**
- * memdup_user - duplicate memory region from user space
- *
- * @src: source address in user space
- * @len: number of bytes to copy
- *
- * Return: an ERR_PTR() on failure.  Result is physically
- * contiguous, to be freed by kfree().
- */
-void *memdup_user(const void __user *src, size_t len)
+static inline void *__memdup_user_flags(const void __user *src, size_t len,
+       gfp_t gfp)
 {
        void *p;
 
-       p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
+       /* Don't let users spam with big allocs and use GFP_NOWARN */
+       p = kmalloc_track_caller(len, __GFP_NOWARN | gfp);
        if (!p)
                return ERR_PTR(-ENOMEM);
 
@@ -180,6 +173,20 @@ void *memdup_user(const void __user *src, size_t len)
 
        return p;
 }
+
+/**
+ * memdup_user - duplicate memory region from user space
+ *
+ * @src: source address in user space
+ * @len: number of bytes to copy
+ *
+ * Return: an ERR_PTR() on failure.  Result is physically
+ * contiguous, to be freed by kfree().
+ */
+void *memdup_user(const void __user *src, size_t len)
+{
+       return __memdup_user_flags(src, len, GFP_USER);
+}
 EXPORT_SYMBOL(memdup_user);
 
 /**
@@ -212,10 +219,11 @@ EXPORT_SYMBOL(vmemdup_user);
  * strndup_user - duplicate an existing string from user space
  * @s: The string to duplicate
  * @n: Maximum number of bytes to copy, including the trailing NUL.
+ * @gfp: the GFP mask used in the kmalloc() call when allocating memory
  *
  * Return: newly allocated copy of @s or an ERR_PTR() in case of error
  */
-char *strndup_user(const char __user *s, long n)
+char *strndup_user(const char __user *s, long n, gfp_t gfp)
 {
        char *p;
        long length;
@@ -228,7 +236,7 @@ char *strndup_user(const char __user *s, long n)
        if (length > n)
                return ERR_PTR(-EINVAL);
 
-       p = memdup_user(s, length);
+       p = __memdup_user_flags(s, length, gfp);
 
        if (IS_ERR(p))
                return p;
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index b53b6d38c4df..ed12433e194a 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -901,7 +901,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
        if (debug) {
                size_t copy = min_t(size_t, count + 1, 1024);
-               char *tp = strndup_user(user_buffer, copy);
+               char *tp = strndup_user(user_buffer, copy, GFP_USER);
 
                if (IS_ERR(tp))
                        return PTR_ERR(tp);
diff --git a/security/keys/dh.c b/security/keys/dh.c
index c4c629bb1c03..fada6015b25b 100644
--- a/security/keys/dh.c
+++ b/security/keys/dh.c
@@ -266,7 +266,8 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user 
*params,
                }
 
                /* get KDF name string */
-               hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
+               hashname = strndup_user(kdfcopy->hashname,
+                               CRYPTO_MAX_ALG_NAME, GFP_USER);
                if (IS_ERR(hashname)) {
                        ret = PTR_ERR(hashname);
                        goto out1;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 9febd37a168f..0f74097cdcd1 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -93,7 +93,8 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
 
        description = NULL;
        if (_description) {
-               description = strndup_user(_description, KEY_MAX_DESC_SIZE);
+               description = strndup_user(_description,
+                               KEY_MAX_DESC_SIZE, GFP_USER);
                if (IS_ERR(description)) {
                        ret = PTR_ERR(description);
                        goto error;
@@ -182,7 +183,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
                goto error;
 
        /* pull the description into kernel space */
-       description = strndup_user(_description, KEY_MAX_DESC_SIZE);
+       description = strndup_user(_description, KEY_MAX_DESC_SIZE,
+                               GFP_USER);
        if (IS_ERR(description)) {
                ret = PTR_ERR(description);
                goto error;
@@ -192,7 +194,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
        callout_info = NULL;
        callout_len = 0;
        if (_callout_info) {
-               callout_info = strndup_user(_callout_info, PAGE_SIZE);
+               callout_info = strndup_user(_callout_info, PAGE_SIZE,
+                                       GFP_USER);
                if (IS_ERR(callout_info)) {
                        ret = PTR_ERR(callout_info);
                        goto error2;
@@ -293,7 +296,7 @@ long keyctl_join_session_keyring(const char __user *_name)
        /* fetch the name from userspace */
        name = NULL;
        if (_name) {
-               name = strndup_user(_name, KEY_MAX_DESC_SIZE);
+               name = strndup_user(_name, KEY_MAX_DESC_SIZE, GFP_USER);
                if (IS_ERR(name)) {
                        ret = PTR_ERR(name);
                        goto error;
@@ -728,7 +731,8 @@ long keyctl_keyring_search(key_serial_t ringid,
        if (ret < 0)
                goto error;
 
-       description = strndup_user(_description, KEY_MAX_DESC_SIZE);
+       description = strndup_user(_description, KEY_MAX_DESC_SIZE,
+                       GFP_USER);
        if (IS_ERR(description)) {
                ret = PTR_ERR(description);
                goto error;
@@ -1742,7 +1746,8 @@ long keyctl_restrict_keyring(key_serial_t id, const char 
__user *_type,
                if (ret < 0)
                        goto error;
 
-               restriction = strndup_user(_restriction, PAGE_SIZE);
+               restriction = strndup_user(_restriction, PAGE_SIZE,
+                               GFP_USER);
                if (IS_ERR(restriction)) {
                        ret = PTR_ERR(restriction);
                        goto error;
diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
index 931d8dfb4a7f..903792123a85 100644
--- a/security/keys/keyctl_pkey.c
+++ b/security/keys/keyctl_pkey.c
@@ -86,7 +86,7 @@ static int keyctl_pkey_params_get(key_serial_t id,
        memset(params, 0, sizeof(*params));
        params->encoding = "raw";
 
-       p = strndup_user(_info, PAGE_SIZE);
+       p = strndup_user(_info, PAGE_SIZE, GFP_USER);
        if (IS_ERR(p))
                return PTR_ERR(p);
        params->info = p;
-- 
2.25.1

Reply via email to