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>