LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (http://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Bad address error when using the system call rename (http://www.linuxquestions.org/questions/linux-newbie-8/bad-address-error-when-using-the-system-call-rename-600511/)

Macska 11-18-2007 01:33 AM

Bad address error when using the system call rename
 
Hi,

I am trying to implement a system call in linux that calls sys_rename in namei.c. In my system call I pass in the file that already exists, and it is not a problem. For the file I want to rename it to I declare a char*
variable like this:
char *newpathname;
and I set it to: newpathname = "hhahathisworks";
then I call:
//rename pathname to newpathname
error_code = sys_rename(pathname, newpathname);
An error code of -14 (bad address) is returned. And I have managed to find that it is failing with the filename I want to rename the file to at this place:
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
retval_greater_than_zero = 0;
filename_greater_than_task_size = 0;

if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
Does anyone know why this filename could be >= TASK_SIZE ?
It makes no sense to me. Please help.

Thank you
Macska

osor 11-18-2007 02:55 PM

You seem to be confusing kernelspace with userspace. First off, sys_rename() isnít even an exported symbol on my machine (and a cursory search shows it is not exported on any other vanilla linux kernels from at least 2.0.40 to 2.6.23). The reason it is not exported is that it is not meant to be used from kernelspace. If you find yourself trying to use it, ask yourself if the problem youíre trying to solve is better addressable in userspace.

But suppose you recompiled the kernel to export the symbol. Then, what? Well, the prototype for sys_rename() is as follows:
Code:

asmlinkage long sys_rename(const char __user *oldname, const char __user *newname)
You see that the arguments are pointers holding USERSPACE addresses (not kernelspace). Userspace virtual addresses all have values strictly less than TASK_SIZE. TASK_SIZE is an architecture-dependent macro; on a default i386 setup its value is 0xC0000000 (3GB). So at least one of the paths of execution starting at sys_rename() will check to see if the addresses are bad (i.e., there is no possible way that a userspace program would be able to address such a memory location).

Almost all third party kernel code is a module that is initially executed as kernel code running in a user context (e.g., it is associated with a userspace process). So you need some way to tell the checking mechanism that you are actually in kernelspace. There are certain macros get_fs() and set_fs() that will allow you to view or change the address limit for the associated process (respectively). This allows you to temporarily give the userspace process access to kernelspace addresses.

So, you have to sandwich all code that calls such addresses by changing the address limit and resetting it to its original value. For example, suppose I write a module where I want to rename a file upon module loading. This is what it might look like:
Code:

#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>

MODULE_LICENSE("GPL");

char old_name [] = "/home/osor/oldname";
char new_name [] = "/home/osor/newname";

int init_module()
{
        int retval;
        mm_segment_t oldfs;
        oldfs = get_fs();
        set_fs (KERNEL_DS);
        retval = sys_rename(old_name, new_name);
        set_fs(oldfs);
        return retval;
}

void cleanup_module() { }

Note that this code assumes the following patch (which allows the direct calling of sys_rename()) to the 2.6.23 kernel:
Code:

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2639,6 +2639,7 @@ asmlinkage long sys_rename(const char __user *oldname, const char __user *newnam
 {
        return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
 }
+EXPORT_SYMBOL_GPL(sys_rename);
 
 int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
 {

P.S.
It seems that the desire to do userspace activities in kernelspace is systemic in the kernel-newbie community. This is the third such question I have seen in a month.


All times are GMT -5. The time now is 03:58 AM.