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