Error: Cannot update core dump limit
Hi, FreeRADIUS 3.0.11 on Linux x64: root@radiusdev01:~# ulimit -Sc 0 root@radiusdev01:~# ulimit -Hc unlimited root@radiusdev01:~# ui-freeradius -f
Tue Jul 5 13:16:42 2016 : Error: Cannot update core dump limit: Operation not permitted Tue Jul 5 13:16:42 2016 : Info: Core dumps are enabled Tue Jul 5 13:16:42 2016 : Info: Debugger not attached
Any idea what's going wrong here? Regards Jakob
On Jul 5, 2016, at 10:43 AM, Jakob Hirsch <jh@plonk.de> wrote:
Tue Jul 5 13:16:42 2016 : Error: Cannot update core dump limit: Operation not permitted Tue Jul 5 13:16:42 2016 : Info: Core dumps are enabled Tue Jul 5 13:16:42 2016 : Info: Debugger not attached
Any idea what's going wrong here?
The server is probably changing UID, and trying to update the core limit after that. Alan DeKok.
Alan DeKok wrote on 2016-07-06 15:13:
Tue Jul 5 13:16:42 2016 : Error: Cannot update core dump limit: Operation not permitted Tue Jul 5 13:16:42 2016 : Info: Core dumps are enabled Tue Jul 5 13:16:42 2016 : Info: Debugger not attached Any idea what's going wrong here? The server is probably changing UID, and trying to update the core limit after that.
I guess that's not a problem (since FR does a prctl(PR_SET_DUMPABLE, 1) after the UID/GID changes):
setgroups(1, [106]) = 0 setgid(106) = 0 mkdir("/var/log/freeradius", 0700) = -1 EEXIST (File exists) geteuid() = 0 setresuid(4294967295, 104, 0) = 0 geteuid() = 104 setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) = 0 open("/var/log/freeradius/radius.log", O_WRONLY|O_CREAT|O_APPEND, 0640) = 3 fchown(3, 104, 106) = 0 prctl(PR_SET_DUMPABLE, 1) = 0 setrlimit(RLIMIT_CORE, {rlim_cur=RLIM_INFINITY, rlim_max=RLIM_INFINITY}) = -1 EPERM (Operation not permitted)
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). I don't know what the call setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) is good for...
On Jul 7, 2016, at 4:20 AM, Jakob Hirsch <jh@plonk.de> wrote:
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). I don't know what the call setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) is good for...
If you have given it the capability, you can get core dumps. Alan DeKok.
Alan DeKok wrote on 2016-07-07 14:38:
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). I don't know what the call setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) is good for... If you have given it the capability, you can get core dumps.
And how would you suggest I should do that? freeradius is started as root, so the capability is there at start time. As I wrote, the code explicitely sets the hard limit for the core size to zero, so there is no chance for the unprivileged freeradius user to raise them again. Why is the server doing that??
On Jul 13, 2016, at 8:06 AM, Jakob Hirsch <jh@plonk.de> wrote: dumps.
And how would you suggest I should do that? freeradius is started as root, so the capability is there at start time.
If you set "allow_core_dumps = yes" it should work.
As I wrote, the code explicitely sets the hard limit for the core size to zero, so there is no chance for the unprivileged freeradius user to raise them again. Why is the server doing that??
Honestly... no idea. All of that debugger / core dump code was re-written by Arran. It *should* work, but I've had all kinds of problems with it. Alan DeKok.
On 13 Jul 2016, at 08:58, Alan DeKok <aland@deployingradius.com> wrote:
On Jul 13, 2016, at 8:06 AM, Jakob Hirsch <jh@plonk.de> wrote: dumps.
And how would you suggest I should do that? freeradius is started as root, so the capability is there at start time.
If you set "allow_core_dumps = yes" it should work.
As I wrote, the code explicitely sets the hard limit for the core size to zero, so there is no chance for the unprivileged freeradius user to raise them again. Why is the server doing that??
Honestly... no idea.
All of that debugger / core dump code was re-written by Arran. It *should* work, but I've had all kinds of problems with it.
Even split between code issues, PEBCAK, poor communication. -Arran Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2
On 7 Jul 2016, at 04:20, Jakob Hirsch <jh@plonk.de> wrote:
Alan DeKok wrote on 2016-07-06 15:13:
Tue Jul 5 13:16:42 2016 : Error: Cannot update core dump limit: Operation not permitted Tue Jul 5 13:16:42 2016 : Info: Core dumps are enabled Tue Jul 5 13:16:42 2016 : Info: Debugger not attached Any idea what's going wrong here? The server is probably changing UID, and trying to update the core limit after that.
I guess that's not a problem (since FR does a prctl(PR_SET_DUMPABLE, 1) after the UID/GID changes):
setgroups(1, [106]) = 0 setgid(106) = 0 mkdir("/var/log/freeradius", 0700) = -1 EEXIST (File exists) geteuid() = 0 setresuid(4294967295, 104, 0) = 0 geteuid() = 104 setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) = 0 open("/var/log/freeradius/radius.log", O_WRONLY|O_CREAT|O_APPEND, 0640) = 3 fchown(3, 104, 106) = 0 prctl(PR_SET_DUMPABLE, 1) = 0 setrlimit(RLIMIT_CORE, {rlim_cur=RLIM_INFINITY, rlim_max=RLIM_INFINITY}) = -1 EPERM (Operation not permitted)
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability).
OK... So there's probably an issue there. Could we get the stack trace so it's clear what logic is being executed. There's only two calls to setrlimit in the server...
I don't know what the call setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) is good for...
It disables core dumping. -Arran Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2
Arran Cudbard-Bell wrote on 2016-07-13 15:17:
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). OK... So there's probably an issue there. Could we get the stack trace so it's clear what logic is being executed.
Sure:
#0 fr_set_dumpable (allow_core_dumps=false) at src/lib/debug.c:526 #1 0x00007ffff7bc75a2 in rad_suid_down () at src/main/util.c:1560 #2 0x0000000000420bf0 in switch_users (cs=0x81fbf0) at src/main/mainconfig.c:651 #3 main_config_init () at src/main/mainconfig.c:933 #4 0x0000000000410cc7 in main (argc=2, argv=0x7fffffffe748) at src/main/radiusd.c:347
I tried to track this down myself. I think the cause is this calls chain: switch_users() rad_suid_down() fr_reset_dumpable() fr_set_dumpable(dump_core) <= Note that dump_core is not set at this point (only initialized to 0 since it's a static bool), so the core dump limit is set to 0 by fr_set_dumpable(). ... fr_set_dumpable(allow_core_dumps) <= allow_core_dumps is true here, but since fr_set_dumpable() was already called with 0=false and therefore setrlimit(RLIMIT_CORE, &no_core), it's not possible to re-raise the core dump limit. An easy fix could be to simply use the hard limit that was already fetched by fr_set_dumpable_init(): diff --git a/src/lib/debug.c b/src/lib/debug.c index 797b4b6..eb39148 100644 --- a/src/lib/debug.c +++ b/src/lib/debug.c @@ -533,7 +533,7 @@ int fr_set_dumpable(bool allow_core_dumps) struct rlimit no_core; no_core.rlim_cur = 0; - no_core.rlim_max = 0; + no_core.rlim_max = core_limits.rlim_max; if (setrlimit(RLIMIT_CORE, &no_core) < 0) { fr_strerror_printf("Failed disabling core dumps: %s", fr_syserror(errno)); Or we could add a flag to fr_set_dumpable_init() which sets dump_core to the correct value. OTOH, I don't see why we need to setrlimit() twice, but maybe there is some good reason for the fr_reset_dumpable() call in rad_suid_down()...
There's only two calls to setrlimit in the server...
I don't know what the call setrlimit(RLIMIT_CORE, {rlim_cur=0, rlim_max=0}) is good for... It disables core dumping.
I know. I just wondered why the server is doing that...
On Jul 13, 2016, at 10:28 AM, Jakob Hirsch <jh@plonk.de> wrote:
Arran Cudbard-Bell wrote on 2016-07-13 15:17:
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). OK... So there's probably an issue there. Could we get the stack trace so it's clear what logic is being executed.
Sure:
This code is run before the configuration is read: { char const *panic_action = NULL; panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action; if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("Failed configuring panic action: %s", main_config.name); fr_exit(EXIT_FAILURE); } } Note that main_config.panic_action will always be NULL here. Because it hasn't read the configuration files yet. The solution is to have an "initialization" function which is separate from the "do work" function. Alan DeKok.
On Jul 13, 2016, at 10:46 AM, Alan DeKok <aland@deployingradius.com> wrote:
On Jul 13, 2016, at 10:28 AM, Jakob Hirsch <jh@plonk.de> wrote:
Arran Cudbard-Bell wrote on 2016-07-13 15:17:
but FR uses setrlimit() with rlim_max=0, and you can only lower rlim_max, but not raise it (unless you have the CAP_SYS_RESOURCE capability). OK... So there's probably an issue there. Could we get the stack trace so it's clear what logic is being executed.
Sure:
This code is run before the configuration is read:
{ char const *panic_action = NULL;
panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action;
if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("Failed configuring panic action: %s", main_config.name); fr_exit(EXIT_FAILURE); } }
Note that main_config.panic_action will always be NULL here. Because it hasn't read the configuration files yet.
The solution is to have an "initialization" function which is separate from the "do work" function.
That code doesn't touch the dumpable flag or rlim_cur or rlim_max, or initialize anything to do with core dumps, the core dump code just happens to exist in the same src file as fr_fault_setup. OP's idea of not setting rlim_max will work. We need to be able to change rlim_cur, because IIRC, the process needs to be temporarily 'dumpable' in order for PATTACH to work in some cases. A big part of why the code in debug.c is so complex, is because the Linux/BSD/POSIX APIs for controlling this stuff are awful. Whether the process can have its core dumped or not should affect whether you can attach a debugger to it, but it does. There's three different facilities all controlling a slightly different part of this functionality: - Linux CAP API - Controls user/process permissions like PATTACH. - get/set rlimit - Controls the size of cores and transitively whether core dumps are allowed. - PRCTRL - Bool - Again whether core dumps are allowed. So get/set rlimit do essentially the same thing as PR_SET_DUMPABLE, I guess PR_SET_DUMPABLE must have some interaction with CAP, that get/set rlimit doesn't... There's no good test for whether a process is currently being debugged either. We attempt a PATTACH to try and figure it out, but that doesn't always work, because we might not have CAP permissions to do that, even though the process is trying to attach to itself. It makes absolutely no sense. fr_fault_setup needs to be run once before we read the config (to set the panic action from the environment), and once after (if the config specifies a panic action), to set the new panic action. Its position in radiusd.c was changed recently, because the debug callbacks weren't being installed early enough to catch issues in the dictionary code. The code needs to be changed so it's called a second time in mainconfig.c with the new panic_action (if one was provided). I'm going to split the debug code and the core dump control code into separate files to make it easier to see what's going on. They're not really related. -Arran
participants (3)
-
Alan DeKok -
Arran Cudbard-Bell -
Jakob Hirsch