How to replace the recently abolished first parameter of macro "access_ok"?

Viewed 1485

Trying to merge a vintage driver from kernel 2.6 to the latest kernel 5.8.

And encountered the following error:

macro "access_ok" passed 3 arguments, but takes just 2

The access_ok is a macro defined in asm/uaccess.h

Here's the code fragment:

#include <asm/uaccess.h>

if (_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;

I did some googling but they all told me to switch to a lower version kernel that supports an access_ok that takes in 3 arguments, obviously this didn't solve my problem.

So I want to know which macro could I replace access_ok with?

2 Answers

According to Torvalds' reply, The type argument was removed from access_ok() function(or macro), and the only thing you need to do to adapt the mentioned code fragment to the latest kernel(5.8 for now), is to simply remove the first argument(eg. VERIFY_WRITE or VERIFY_READ)

And here's the fragment after the amendment.

#include <asm/uaccess.h>

if (_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok((void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok((void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;

I include the following compatibility code:

#include <linux/version.h>

/* 
 * <linux/uaccess.h> was added in kernel version 2.6.18, and should be
 * included in preference to <asm/uaccess.h>.  In particular, copy_to_user()
 * and copy_from_user() were moved to <linux/uaccess.h> in kernel version
 * 4.12.
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
#include <linux/uaccess.h>
#else
#include <asm/uaccess.h>
#endif

/*
 * Kernel 5.0 removed VERIFY_READ and VERIFY_WRITE and removed the first
 * parameter of access_ok() which was set to VERIFY_READ or VERIFY_WRITE.
 * That has been redundant since kernel 2.5.70, and even then it was only
 * checked for kernels that support old 386 processors.
 *
 * Get rid of the first parameter and always pass VERIFY_WRITE for kernels
 * prior to 5.0.  This will fail for old 386 processors on pre-2.5.70
 * kernels if the memory region is not in fact writeable.
 */
#ifdef VERIFY_WRITE
/* Pre 5.0 kernel. */
static inline int _kcompat_access_ok(unsigned long addr, size_t size)
{
    /* Always use VERIFY_WRITE.  Most architectures ignore it. */
    return access_ok(VERIFY_WRITE, addr, size);
}
/* Redefine access_ok() to remove first parameter. */
#undef access_ok
#define access_ok(addr, size) _kcompat_access_ok((unsigned long)(addr), (size))
#endif

Then change all the old, three-parameter calls to access_ok in the code to omit the first parameter.

For pre-5.0 kernels that still need the removed parameter, this compatibility code always sets it to VERIFY_WRITE. It doesn't matter much because the parameter value has been redundant since kernel version 2.5.70 and even then it was only checked for kernels that support old 386 processors.

Related