[pulseaudio-discuss] priority logic in main seems broken
David Kågedal
davidk at lysator.liu.se
Wed Jan 16 03:48:04 PST 2008
There are a multitude of ways in which pulseaudio tries to acquire
high/realtime privileges when starting. Some of them probably even
work, but not the ones it tries for me.
My main attempt was to make pulseaudio suid root, but it seems that
that code path hasn't been used in a while. I suspect that Lennart
uses PolicyKit or something.
Anyway, I went over the code, and I see some strangeness that I will
note below. I'm running Ubuntu linux 7.10 x86_64 with the
2.6.22-14-generic kernek. My config.h includes this:
#define HAVE_GETUID 1
/* #undef HAVE_POLKIT */
Let's look at the code
> int main(int argc, char *argv[]) {
> ... variable declarations omitted ...
>
> #ifdef OS_IS_WIN32
> ...
> #endif
>
>
> #if defined(__linux__) && defined(__OPTIMIZE__)
> ... unrelated stuff ...
> #endif
>
> #ifdef HAVE_GETUID
> real_root = getuid() == 0;
> suid_root = !real_root && geteuid() == 0;
> #else
> ...
> #endif
>
> if (suid_root) {
> /* Drop all capabilities except CAP_SYS_NICE */
> pa_limit_caps();
But setrlimit(2) says that it wants CAP_SYS_RESOURCE.
> /* Drop priviliges, but keep CAP_SYS_NICE */
> pa_drop_root();
>
> /* After dropping root, the effective set is reset, hence,
> * let's raise it again */
> pa_limit_caps();
>
> /* When capabilities are not supported we will not be able to
> * aquire RT sched anymore. But yes, that's the way it is. It
> * is just too risky tun let PA run as root all the time. */
> }
>
> /* At this point, we are a normal user, possibly with CAP_NICE if
> * we were started SUID. If we are started as normal root, than we
> * still are normal root. */
>
> setlocale(LC_ALL, "");
> pa_log_set_maximal_level(PA_LOG_INFO);
> pa_log_set_ident("pulseaudio");
Here is a different problem. The logging isn't initialized until now,
which means that any logging that e.g. the pa_limit_caps call above
did will be lost. This didn't help my debugging :-)
> conf = pa_daemon_conf_new();
>
> if (pa_daemon_conf_load(conf, NULL) < 0)
> goto finish;
>
> if (pa_daemon_conf_env(conf) < 0)
> goto finish;
>
> if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
> pa_log("Failed to parse command line.");
> goto finish;
> }
>
> pa_log_set_maximal_level(conf->log_level);
> pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
>
> if (suid_root) {
> /* Ok, we're suid root, so let's better not enable high prio
> * or RT by default */
So now the commented-out default value in daemon.conf is no longer
correct? So I give --high-priority=yes just to be sure.
> allow_high_priority = allow_realtime = FALSE;
>
> #ifdef HAVE_POLKIT
> ... not me ...
> #endif
>
> if ((conf->high_priority || conf->realtime_scheduling) && pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
> pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
> allow_realtime = conf->realtime_scheduling;
> allow_high_priority = conf->high_priority;
> }
I'm in pulse-rt, so I do get this log message.
> if (!allow_high_priority && !allow_realtime) {
This condition should be false for me.
> /* OK, there's no further need to keep CAP_NICE. Hence
> * let's give it up early */
>
> pa_drop_caps();
> pa_drop_root();
> suid_root = real_root = FALSE;
>
> if (conf->high_priority || conf->realtime_scheduling)
> pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
> "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
> "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
> }
>
> } else {
This is the non-suid case.
> /* OK, we're a normal user, so let's allow the user evrything
> * he asks for, it's now the kernel's job to enforce limits,
> * not ours anymore */
> allow_high_priority = allow_realtime = TRUE;
> }
>
> if (conf->high_priority && !allow_high_priority) {
> pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
> conf->high_priority = FALSE;
> }
>
> if (conf->realtime_scheduling && !allow_realtime) {
> pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
> conf->realtime_scheduling = FALSE;
> }
>
> if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
> pa_raise_priority(conf->nice_level);
At this point, I get the following log message:
W: core-util.c: setpriority(): Permission denied
I suspect that since the -11 nice value breaks a resource limit, we
should have called setrlimit to change it before dropping CAP_SYS_RESOURCE.
> if (suid_root) {
> pa_bool_t drop;
>
> drop = conf->cmd != PA_CMD_DAEMON || !conf->realtime_scheduling;
>
> #ifdef RLIMIT_RTPRIO
> if (!drop) {
>
> /* At this point we still have CAP_NICE if we were loaded
> * SUID root. If possible let's acquire RLIMIT_RTPRIO
> * instead and give CAP_NICE up. */
>
> const pa_rlimit rl = { 9, TRUE };
>
> if (set_one_rlimit(&rl, RLIMIT_RTPRIO, "RLIMIT_RTPRIO") >= 0) {
> pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE.");
> drop = TRUE;
> } else
> pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
> }
> #endif
>
> if (drop) {
> pa_drop_caps();
> pa_drop_root();
> suid_root = real_root = FALSE;
> }
> }
--
David Kågedal
More information about the pulseaudio-discuss
mailing list