File indexing completed on 2024-04-28 15:25:52

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include <config-kdeinit.h>
0009 
0010 #define _POSIX_C_SOURCE 200809L /* kill(2) */
0011 
0012 #include <errno.h>
0013 #include <fcntl.h>
0014 #include <signal.h>
0015 #include <stdio.h>
0016 #include <stdlib.h>
0017 #include <string.h>
0018 #include <sys/stat.h>
0019 #include <unistd.h>
0020 #if HAVE_CAPABILITIES
0021 #include <sys/capability.h>
0022 #endif
0023 
0024 #define EXECUTE KDE_INSTALL_FULL_BINDIR "/kdeinit5"
0025 
0026 #if KDEINIT_OOM_PROTECT
0027 
0028 /*
0029  Prevent getting killed by bad heuristic in Linux OOM-killer.
0030  This wrapper decreases the chance OOM killer kills it (or its children,
0031  namely kdeinit), opens a pipe and forks. Child drops privileges
0032  and launches kdeinit. Since processes started by kdeinit should
0033  not have this protection, kdeinit will after forking send the new
0034  PID using the pipe and wait for a signal. This parent will reset the protection
0035  and SIGUSR1 the process to continue.
0036  returns 1 if pid is valid
0037 */
0038 
0039 static int set_protection(pid_t pid, int enable)
0040 {
0041     char buf[ 1024 ];
0042     int procfile;
0043     struct stat st;
0044     /* Newer kernels (noticed in 2.6.36) */
0045     sprintf(buf, "/proc/%d/oom_score_adj", pid);
0046     if (lstat(buf, &st) == 0) {
0047         if (!enable) {
0048             /* Be paranoid and check that the pid we got from the pipe
0049                belongs to this user. */
0050             if (st.st_uid != getuid()) {
0051                 return 0;
0052             }
0053         }
0054         procfile = open(buf, O_WRONLY);
0055         if (enable) {
0056             write(procfile, "-300", sizeof("-300"));
0057         } else {
0058             write(procfile, "0", sizeof("0"));
0059         }
0060         close(procfile);
0061         return 1;
0062     }
0063 
0064     sprintf(buf, "/proc/%d/stat", pid);
0065     if (!enable) {
0066         /* Be paranoid and check that the pid we got from the pipe
0067            belongs to this user. */
0068         if (lstat(buf, &st) < 0 || st.st_uid != getuid()) {
0069             return 0;
0070         }
0071     }
0072     sprintf(buf, "/proc/%d/oom_adj", pid);
0073     procfile = open(buf, O_WRONLY);
0074     if (procfile >= 0) {
0075         if (enable) {
0076             write(procfile, "-5", sizeof("-5"));
0077         } else {
0078             write(procfile, "0", sizeof("0"));
0079         }
0080         close(procfile);
0081     }
0082     return 1;
0083 }
0084 
0085 int main(int argc, char **argv)
0086 {
0087     int pipes[ 2 ];
0088     int new_argc;
0089     const char **new_argv;
0090     char helper_num[ 1024 ];
0091     unsigned i;
0092     char **orig_environ = NULL;
0093     char header[ 7 ];
0094 #if HAVE_CAPABILITIES
0095     cap_t caps;
0096 #endif
0097     if (pipe(pipes) < 0) {
0098         perror("pipe()");
0099         return 1;
0100     }
0101     if (argc < 0 || argc > 1000) {
0102         abort();    /* paranoid */
0103     }
0104     set_protection(getpid(), 1);
0105     switch (fork()) {
0106     case -1:
0107         perror("fork()");
0108         return 1;
0109     default: /* parent, drop privileges and exec */
0110 #if HAVE_CAPABILITIES
0111         caps = cap_init();
0112         if (cap_set_proc(caps) < 0) {
0113             perror("cap_set_proc()");
0114             return 1;
0115         }
0116         cap_free(caps);
0117 #endif
0118         if (setgid(getgid())) {
0119             perror("setgid()");
0120             return 1;
0121         }
0122         if (setuid(getuid()) || geteuid() != getuid()) {
0123             perror("setuid()");
0124             return 1;
0125         }
0126         close(pipes[ 0 ]);
0127         /* read original environment passed by start_kdeinit_wrapper */
0128         if (read(0, header, 7) == 7 && strncmp(header, "environ", 7) == 0) {
0129             unsigned count;
0130             if (read(0, &count, sizeof(unsigned)) == sizeof(unsigned)
0131                     && count && count < (1 << 16)) {
0132                 char **env = malloc((count + 1) * sizeof(char *));
0133                 int ok = 1;
0134                 for (i = 0;
0135                         i < count && ok;
0136                         ++i) {
0137                     unsigned len;
0138                     if (read(0, &len, sizeof(unsigned)) == sizeof(unsigned)
0139                             && len && len < (1 << 12)) {
0140                         env[ i ] = malloc(len + 1);
0141                         if ((unsigned) read(0, env[ i ], len) == len) {
0142                             env[ i ][ len ] = '\0';
0143                         } else {
0144                             ok = 0;
0145                         }
0146                     }
0147                 }
0148                 if (ok) {
0149                     env[ i ] = NULL;
0150                     orig_environ = env;
0151                 }
0152             }
0153         }
0154         if (argc == 0) {
0155             return 1;
0156         }
0157         new_argc = argc + 2;
0158         new_argv = malloc(sizeof(char *) * (new_argc + 1));
0159         if (new_argv == NULL) {
0160             return 1;
0161         }
0162         new_argv[ 0 ] = EXECUTE;
0163         new_argv[ 1 ] = "--oom-pipe";
0164         sprintf(helper_num, "%d", pipes[ 1 ]);
0165         new_argv[ 2 ] = helper_num;
0166         for (i = 1;
0167                 i <= (unsigned) argc;
0168                 ++i) {
0169             new_argv[ i + 2 ] = argv[ i ];
0170         }
0171         if (orig_environ) {
0172             execve(EXECUTE, (char **)new_argv, orig_environ);
0173         } else {
0174             execv(EXECUTE, (char **)new_argv);
0175         }
0176         perror(EXECUTE);
0177         return 1;
0178     case 0: /* child, keep privileges and do the privileged work */
0179         close(pipes[ 1 ]);
0180         for (;;) {
0181             pid_t pid = 0;
0182             int ret = read(pipes[ 0 ], &pid, sizeof(pid_t));
0183             if (ret < 0 && errno == EINTR) {
0184                 continue;
0185             }
0186             if (ret <= 0) { /* pipe closed or error, exit */
0187                 _exit(0);
0188             }
0189             if (pid != 0) {
0190                 if (set_protection(pid, 0)) {
0191                     kill(pid, SIGUSR1);
0192                 }
0193             }
0194         }
0195     }
0196 }
0197 
0198 #else /* not Linux, the simple non-setuid case */
0199 
0200 int main(int argc, char **argv)
0201 {
0202     if (argc == 0) {
0203         return 1;
0204     }
0205     argv[0] = (char *)EXECUTE;
0206     execv(EXECUTE, argv);
0207     perror(EXECUTE);
0208     return 1;
0209 }
0210 #endif