lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <f84a96d7-2df4-4f79-8c27-83a1d9574412@moroto.mountain>
Date: Sun, 28 Apr 2024 16:12:07 +0300
From: Dan Carpenter <dan.carpenter@...aro.org>
To: dm-devel@...ts.linux.dev
Cc: linux-hardening@...r.kernel.org, Kees Cook <keescook@...omium.org>
Subject: [bug report] dm ioctl: harden copy_params()'s copy_from_user() from
 malicious users

Hi DM Maintainers and kernel hardenning people,

drivers/md/dm-ioctl.c
    1931 static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
    1932                        int ioctl_flags, struct dm_ioctl **param, int *param_flags)
    1933 {
    1934         struct dm_ioctl *dmi;
    1935         int secure_data;
    1936         const size_t minimum_data_size = offsetof(struct dm_ioctl, data);
    1937 
    1938         /* check_version() already copied version from userspace, avoid TOCTOU */
    1939         if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version),
    1940                            (char __user *)user + sizeof(param_kernel->version),
    1941                            minimum_data_size - sizeof(param_kernel->version)))
    1942                 return -EFAULT;
    1943 
    1944         if (unlikely(param_kernel->data_size < minimum_data_size) ||
    1945             unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) {

So what's happening here is that struct dm_ioctl->data[] is declared as
a 7 byte array, but it's actually a variable size array which could be
more or less than 7 bytes.

    1946                 DMERR("Invalid data size in the ioctl structure: %u",
    1947                       param_kernel->data_size);
    1948                 return -EINVAL;
    1949         }
    1950 
    1951         secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
    1952 
    1953         *param_flags = secure_data ? DM_WIPE_BUFFER : 0;
    1954 
    1955         if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
    1956                 dmi = param_kernel;
    1957                 dmi->data_size = minimum_data_size;
    1958                 goto data_copied;
    1959         }
    1960 
    1961         /*
    1962          * Use __GFP_HIGH to avoid low memory issues when a device is
    1963          * suspended and the ioctl is needed to resume it.
    1964          * Use kmalloc() rather than vmalloc() when we can.
    1965          */
    1966         dmi = NULL;
    1967         dmi = kvmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH);

We allocate the correct size of the variable element array.

    1968 
    1969         if (!dmi) {
    1970                 if (secure_data && clear_user(user, param_kernel->data_size))
    1971                         return -EFAULT;
    1972                 return -ENOMEM;
    1973         }
    1974 
    1975         *param_flags |= DM_PARAMS_MALLOC;
    1976 
    1977         /* Copy from param_kernel (which was already copied from user) */
    1978         memcpy(dmi, param_kernel, minimum_data_size);
    1979 
--> 1980         if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size,
    1981                            param_kernel->data_size - minimum_data_size))

Doesn't the kernel hardenning stuff have run time checks for if we
write beyond the end of a 7 byte array?  Why not just declare it as a
zero element array?

    1982                 goto bad;
    1983 data_copied:
    1984         /* Wipe the user buffer so we do not return it to userspace */
    1985         if (secure_data && clear_user(user, param_kernel->data_size))
    1986                 goto bad;
    1987 
    1988         *param = dmi;
    1989         return 0;
    1990 
    1991 bad:
    1992         free_params(dmi, param_kernel->data_size, *param_flags);
    1993 
    1994         return -EFAULT;
    1995 }

regards,
dan carpenter

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ