Fan Wu
Fan Wu

Reputation: 169

Linux kernel socket programming:sendmsg function msg address can not access

I want to send a message with socket->ops->sendmsg() in kernel module. One of the arguments of func sendmsg struct msghdr has a pointer msg_iov to the send buffer.

But except NULL , whatever buffer address I assign to the msg_iov the sendmsg() will return a EFAULT error to me. That means the address I assigned to the pointer can not be accessed.

So please help me and thank you very much.
P.S: here is part of my code. I omitted the irrelevant codes.

struct iovec vec;
char *buff = (char *)kmalloc(7, GFP_KERNEL);  

unsigned long user_addr=0; 
size_t count = 16;

buff[0] = 'H';
buff[1] = 'e';
buff[2] = 'l';
buff[3] = 'l';
buff[4] = 'o';
buff[5] = '\n';
buff[6] = '\0';  

down_write(&current->mm->mmap_sem);
user_addr = do_mmap_pgoff(NULL, 0, count, PROT_READ|PROT_WRITE,\
                           MAP_PRIVATE|MAP_ANONYMOUS, 0);
up_write(&current->mm->mmap_sem);

__copy_to_user((void*)user_addr, (void*)buff, 7);


vec.iov_base = (void*)user_addr;
vec.iov_len = strlen( (char*)user_addr );   

msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
error = NewSock->ops->sendmsg(&kiocb,NewSock, &msg, 7);

do_munmap( &current->mm, user_addr, strlen( (char*) user_addr));

Upvotes: 1

Views: 2605

Answers (1)

sarnold
sarnold

Reputation: 104080

You can't call this function with data that lives in the kernel's address space:

int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
    size_t size)
    /* ... */
    while (--iovlen >= 0) {
    size_t seglen = iov->iov_len;
    unsigned char __user *from = iov->iov_base;
    /* ... */
                    if ((err = skb_add_data(skb, from, copy)) != 0)
                goto do_fault;


static inline int skb_add_data(struct sk_buff *skb,
               char __user *from, int copy)
    /* ... */
    __wsum csum = csum_and_copy_from_user(from, skb_put(skb, copy),
                            copy, 0, &err);


#define csum_and_copy_from_user csum_partial_copy_from_user


__wsum
csum_partial_copy_from_user(const void __user *src, void *dst,
            int len, __wsum isum, int *errp)
    /* ... */
if (!likely(access_ok(VERIFY_READ, src, len)))
    goto out_err;
/* ... */
isum = csum_partial_copy_generic((__force const void *)src,
            dst, len, isum, errp, NULL);

access_ok on x86 checks for userspace pointers:

/**
 * access_ok: - Checks if a user space pointer is valid
 * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
 *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
 *        to write to a block, it is always safe to read from it.
 * @addr: User space pointer to start of block to check
 * @size: Size of block to check
 *
 * Context: User context only.  This function may sleep.
 *
 * Checks if a pointer to a block of memory in user space is valid.
 *
 * Returns true (nonzero) if the memory block may be valid, false (zero)
 * if it is definitely invalid.
 *
 * Note that, depending on architecture, this function probably just
 * checks that the pointer is in the user space range - after calling
 * this function, memory access functions may still return -EFAULT.
 */
#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))

The comment on __range_not_ok() looks similar:

/*
 * Test whether a block of memory is a valid user space address.
 * Returns 0 if the range is valid, nonzero otherwise.
 *
 * This is equivalent to the following test:
 * (u33)addr + (u33)size >= (u33)current->addr_limit.seg (u65 for x86_64)
 *
 * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
 */

While I followed the x86-specific code paths whenever architecture-specific code was involved, I expect other architectures to enforce this behavior to the best of their respective abilities.

It looks like you cannot call sendmsg() on in-kernel struct iovec memory.

Upvotes: 2

Related Questions