Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 17 Dec 2018 17:34:05 -0500
From:      Austin Shafer <amshafer64@gmail.com>
To:        freebsd-hackers@freebsd.org
Subject:   ENOMEM when calling sysctl_handle_int from custom handler
Message-ID:  <861s6fpyua.fsf@triplebuff.com>

next in thread | raw e-mail | index | archive | help
Hi freebsd-hackers,

I've had an issue with creating a custom sysctl handler that I can't
seem to find an answer for. Using sysctl_handle_int as a handler in
SYSCTL_ADD_PROC works fine, but calling sysctl_handle_int from a custom
sysctl handler results in an error.

(sorry for the long message, I tried to be verbose)

Creating the nodes works fine, but SYSCTL_OUT or sysctl_handle_int
return ENOMEM despite there being plenty of available memory. I based my
approach on other areas of the source where those sysctls work without
error. It seems to only happen to custom sysctls that I add. This
happens on multiple machines, but the output in this email is from the
bhyve VM I test in. Any insight as to where my implementation went wrong
would be extremely helpful.

Full Source: ("make" should compile on a bsd system)
https://github.com/ashaferian/sysctls

echo_modevent ()
{
...
        /* working sysctl using default handler sysctl_handle_int */
	SYSCTL_ADD_PROC(&ctx, SYSCTL_CHILDREN(poid), OID_AUTO, "default", CTLTYPE_INT|CTLFLAG_RW, &ret, -1, sysctl_handle_int, "I", "working sysctl");

	/* broken sysctl using my handler */
	SYSCTL_ADD_PROC(&ctx, SYSCTL_CHILDREN(poid), OID_AUTO, "custom",
	CTLTYPE_INT|CTLFLAG_RW, &ret, -1, sysctl_handle, "I", "working
	sysctl");
}

static int
sysctl_handle(SYSCTL_HANDLER_ARGS)
{
	int error;

        /* returns ENOMEM */
	error = sysctl_handle_int(oidp, arg1, arg2, req);
...

In the code above I use the "sysctl_handle" function to handle requests
to the echo.* sysctls I have created. I create two nodes echo.default
which uses the default handler "sysctl_handle_int", and echo.custom
which uses my "sysctl_handle" function. What confuses me so greatly is
that sysctl_handle is just a wrapper that calls sysctl_handle_int and
passes its arguments without changing anything. There should be no
difference because all I do is call the default handler. echo.default works,
while echo.custom does not. Both functions operate on the same
variables and have pretty much the same SYSCLT_ADD_PROC declaration.

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.

---------------------------
dtrace -n '*::sysctl_handle_int:entry /execname == "sysctl" / {
print(*args[3]); }'


// sysctl echo.default
  0  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0x7fffffffd69c
    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
}
  0  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0
    size_t oldlen = 0
    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 = 0
    int flags = 0
}
  0  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0x800668000
    size_t oldlen = 0x8
    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 = 0x8
    int flags = 0
}

// sysctl echo.custom
Custom Sysctl: Error 0
Custom Sysctl: Error 12
CPU     ID                    FUNCTION:NAME
  1  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0x7fffffffd69c
    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
}
  1  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0
    size_t oldlen = 0
    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 = 0
    int flags = 0
}
  1  27761          sysctl_handle_int:entry struct sysctl_req {                          
    struct thread *td = 0xfffff8000392f580
    int lock = 0x1
    void *oldptr = 0x800667008
    size_t oldlen = 0
    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 = 0
    int flags = 0
}
---------------------------

I'm really not sure what I've done wrong so any help is greatly
appreciated. Most of the online resources/books that show sysctl
creation seem to do the same thing I did. If there is tricky or
non-obvious behavior that I have missed it would be helpful to note it
for others trying to learn.

If there is anything else I can provide or do please let me know.

Thank you so much for your time!
      Austin Shafer

_________________________________________________________________________
uname -a:
FreeBSD punk-vm 13.0-CURRENT FreeBSD 13.0-CURRENT GENERIC-NODEBUG  amd64
        * uname doesn't list revision but it should be r342141

top memory usage:
...
Mem: 20M Active, 2672K Inact, 143M Wired, 98M Buf, 1789M Free




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