5

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?

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
Justin Lee
  • 800
  • 1
  • 11
  • 22
  • Please explain what you want to achieve with that macro. Please provide a [mre] to demonstrate the error you quote. With that the impression that `access_ok` is probably defined with only two parameters, which is the most likely explanation for the error you see. Do you have any documentation (or other idea of functionality and purpose) of the code you show the macro `access_ok` and what you want to achieve? – Yunnosch Jan 12 '21 at 06:50
  • @Yunnosch `access_ok` is a micro defined in `asm/uaccess.h` and in kernel 5 there was an attribute removed from that micro according to [this](https://github.com/torvalds/linux/commit/96d4f267e40f9509e8a66e2b39e8b95655617693) – Justin Lee Jan 12 '21 at 07:50
  • Please add that info on the history of that m**A**cro to the question. – Yunnosch Jan 12 '21 at 07:53
  • Is this a duplicate question? [What is the point of using the linux macro access_ok()](https://stackoverflow.com/questions/12357752/what-is-the-point-of-using-the-linux-macro-access-ok) The accepted answer mentions the deprecated first parameter. – Lundin Jan 12 '21 at 08:01
  • @Lundin, personally, I don't think so. Because that [the question you mentioned](https://stackoverflow.com/questions/12357752/what-is-the-point-of-using-the-linux-macro-access-ok) is discussing the necessity of using the macro I've mentioned and mine is about how to update a vintage driver to the latest kernel. – Justin Lee Jan 12 '21 at 08:04
  • I think that is not a dupe and have edited the title to highlight the aspect which is the reason for that. @Lundi Justin Lee, seeing your answer I hope you are fine with my edit. Otherwise please improve or undo and accept my apology. – Yunnosch Jan 12 '21 at 08:05
  • @Yunnosch, Thanks for the help. I do appreciate this. – Justin Lee Jan 12 '21 at 08:07

2 Answers2

7

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;

Justin Lee
  • 800
  • 1
  • 11
  • 22
5

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.

Ian Abbott
  • 15,083
  • 19
  • 33