Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 Dec 2018 12:06:48 -0500
From:      Austin Shafer <amshafer64@gmail.com>
To:        Konstantin Belousov <kostikbel@gmail.com>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: ENOMEM when calling sysctl_handle_int from custom handler
Message-ID:  <m25zvqix1z.fsf@triplebuff.com>
In-Reply-To: <20181218055828.GA60291@kib.kiev.ua>
References:  <861s6fpyua.fsf@triplebuff.com> <20181218055828.GA60291@kib.kiev.ua>

next in thread | previous in thread | raw e-mail | index | archive | help
Konstantin Belousov <kostikbel@gmail.com> writes:

> On Mon, Dec 17, 2018 at 05:34:05PM -0500, Austin Shafer wrote:
>> My best guess:
>> I've tried to dig around in the debugger (using kgdb) but haven't been
>> able to find anything. The only difference I noticed while tracing was
>> that the "oldlenp" field in the sysctl_args struct passed in from
>> userland was 0 when calling my handler and 8 when calling the default
>> handler. I am not very familiar with the sysctl implementation, but this
>> affected the valid length in the sysctl_req which ended up returning
>> ENOMEM from sysctl_old_user. You can watch/confirm this change in sysctl_req
>> using the following dtrace one-liner. Its unclear why changing the
>> sysctl handler would affect the arguments passed to it.
> Yes, this is most likely cause for your issue.  ENOMEM does not have
> anything with the memory shortage, but with the fact that the oldlen
> is too short for int copyout.
>
> Note that for the int-typed oid, old and new len should be 4 (not 0 and
> not 8).  Why your userspace code passed such length is the question to
> you.
>

Thanks for the reply! I'm just using the sysctl(8) utility in the
examples above so the valid length passing from userspace is not done by
me. Based on what you said it seems to me that sysctl(8) is not passing
the correct values then? This would be strange since all other sysctls
work using sysctl(8). I'm more than happy to dig deep and debug
sysctl(8) to see if it is the culprit. I had assumed it was working fine
and that the error was on my end in the kernel module.

You are right about the userspace passed values being whats causing my
problems though. I wrote a short program to call sysctlbyname (with the
correct length as you specified above) and now I can set the value of the
sysctl:

// read_sysctl.c
int
main (int argc, char *argv[])
{
        int error, old, new = 3;
        size_t size;

        size = sizeof(int);
...
        error = sysctlbyname(argv[1], &old, &size, &new, size);
...
}

// dtrace -n '*::sysctl_handle_int:entry /execname == "read_sysctl" / { print(*args[3]); }'
CPU     ID                    FUNCTION:NAME
  2  27761          sysctl_handle_int:entry struct sysctl_req {
    struct thread *td = 0xfffff80003b38580
    int lock = 0x1
    void *oldptr = 0x7fffffffd71c
    size_t oldlen = 0x4
    size_t oldidx = 0
    int (*)() oldfunc = kernel`sysctl_old_user
    void *newptr = 0
    size_t newlen = 0
    size_t newidx = 0
    int (*)() newfunc = kernel`sysctl_new_user
    size_t validlen = 0x4
    int flags = 0
}
  2  27761          sysctl_handle_int:entry struct sysctl_req {
    struct thread *td = 0xfffff80003b38580
    int lock = 0x1
    void *oldptr = 0x7fffffffeb28
    size_t oldlen = 0x4
    size_t oldidx = 0
    int (*)() oldfunc = kernel`sysctl_old_user
    void *newptr = 0x7fffffffeb24
    size_t newlen = 0x4
    size_t newidx = 0
    int (*)() newfunc = kernel`sysctl_new_user
    size_t validlen = 0x4
    int flags = 0
}

Also out of curiosity, the dtrace probes show multiple sysctl_handle_int
evaluations. Is this for each root and sub-node in the sysctl tree?
Thats what it looks like to me but I'd love to know for sure.

Thanks for all the help!
       Austin Shafer



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?m25zvqix1z.fsf>