unix socket error 14: EFAULT (bad address)

 ̄綄美尐妖づ 提交于 2021-02-07 11:17:48

问题


I have a very simple question, but I have not managed to find any answers to it all weekend. I am using the sendto() function and it is returning error code 14: EFAULT. The man pages describe it as:

"An invalid user space address was specified for an argument."

I was convinced that this was talking about the IP address I was specifying, but now I suspect it may be the memory address of the message buffer that it is referring to - I can't find any clarification on this anywhere, can anyone clear this up?


回答1:


EFAULT It happen if the memory address of some argument passed to sendto (or more generally to any system call) is invalid. Think of it as a sort of SIGSEGV in kernel land regarding your syscall. For instance, if you pass a null or invalid buffer pointer (for reading, writing, sending, recieving...), you get that

See errno(3), sendto(2) etc... man pages.

EFAULT is not related to IP addresses at all.




回答2:


Minimal runnable example with getcpu

Just to make things more concrete, we can have a look at the getcpu system call, which is very simple to understand, and shows the same EFAULT behaviour.

From man getcpu we see that the signature is:

int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);

and the memory pointed to by the cpu will contain the ID of the current CPU the process is running on after the syscall, the only possible error being:

ERRORS
       EFAULT Arguments point outside the calling process's address space.

So we can test it out with:

main.c

#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

int main(void) {
    int err, ret;
    unsigned cpu;

    /* Correct operation. */
    assert(syscall(SYS_getcpu, &cpu, NULL, NULL) == 0);
    printf("%u\n", cpu);

    /* Bad trash address == 1. */
    ret = syscall(SYS_getcpu, 1, NULL, NULL);
    err = errno;
    assert(ret == -1);
    printf("%d\n", err);
    perror("getcpu");

    return EXIT_SUCCESS;
}

compile and run:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

Sample output:

cpu 3
errno 14
getcpu: Bad address

so we see that the bad call with a trash address of 1 returned 14, which is EFAULT as seen from kernel code: https://stackoverflow.com/a/53958705/895245

Remember that the syscall itself returns -14, and then the syscall C wrapper detects that it is an error due to being negative, returns -1, and sets errno to the actual precise error code.

And since the syscall is so simple, we can confirm this from the kernel 5.4 implementation as well at kernel/sys.c:

SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
        struct getcpu_cache __user *, unused)
{
    int err = 0;
    int cpu = raw_smp_processor_id();

    if (cpup)
        err |= put_user(cpu, cpup);
    if (nodep)
        err |= put_user(cpu_to_node(cpu), nodep);
    return err ? -EFAULT : 0;
}

so clearly we see that -EFAULT is returned if there is a problem with put_user.

It is worth mentioning that my glibc does have a getcpu wrapper as well in sched.h, but that implementation segfaults in case of bad addresses, which is a bit confusing: How do I include Linux header files like linux/getcpu.h? But it is not what the actual syscall does to the process, just whatever glibc is doing with that address.

Tested on Ubuntu 20.04, Linux 5.4.




回答3:


EFAULT is a macro defined in a file "include/uapi/asm-generic/errno-base.h"

#define EFAULT          14      /* Bad address */


来源:https://stackoverflow.com/questions/9260937/unix-socket-error-14-efault-bad-address

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!