File oom-protect.diff of Package kdelibs4
Index: kinit/start_kdeinit.c
===================================================================
--- kinit/start_kdeinit.c (revision 0)
+++ kinit/start_kdeinit.c (revision 1001887)
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2006 Lubos Lunak <l.lunak@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <config-prefix.h>
+#include <config-kdeinit.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define EXECUTE BIN_INSTALL_DIR "/kdeinit4"
+
+#ifdef KDEINIT_OOM_PROTECT
+
+/*
+ Prevent getting killed by bad heuristic in Linux OOM-killer.
+ This wrapper decreases the chance OOM killer kills it (or its children,
+ namely kdeinit), opens a pipe and forks. Child drops privileges
+ and launches kdeinit. Since processes started by kdeinit should
+ not have this protection, kdeinit will after forking send the new
+ PID using the pipe and wait for a signal. This parent will reset the protection
+ and SIGUSR1 the process to continue.
+ returns 1 if pid is valid
+*/
+
+static int set_protection( pid_t pid, int enable )
+{
+ char buf[ 1024 ];
+ int procfile;
+ sprintf( buf, "/proc/%d/stat", pid );
+ if( !enable ) {
+ /* Be paranoid and check that the pid we got from the pipe
+ belongs to this user. */
+ struct stat st;
+ if( lstat( buf, &st ) < 0 || st.st_uid != getuid())
+ return 0;
+ }
+ sprintf( buf, "/proc/%d/oom_adj", pid );
+ procfile = open( buf, O_WRONLY );
+ if( procfile >= 0 ) {
+ if( enable )
+ write( procfile, "-5", sizeof( "-5" ));
+ else
+ write( procfile, "0", sizeof( "0" ));
+ close( procfile );
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int pipes[ 2 ];
+ int new_argc;
+ const char** new_argv;
+ char helper_num[ 1024 ];
+ unsigned i;
+ char** orig_environ = NULL;
+ char header[ 7 ];
+ if( pipe( pipes ) < 0 ) {
+ perror( "pipe()" );
+ return 1;
+ }
+ if( argc < 0 || argc > 1000 )
+ abort(); /* paranoid */
+ set_protection( getpid(), 1 );
+ switch( fork()) {
+ case -1:
+ perror( "fork()" );
+ return 1;
+ default: /* parent, drop privileges and exec */
+ if (setgid(getgid())) {
+ perror("setgid()");
+ return 1;
+ }
+ if (setuid(getuid()) || geteuid() != getuid()) {
+ perror("setuid()");
+ return 1;
+ }
+ close( pipes[ 0 ] );
+ /* read original environment passed by start_kdeinit_wrapper */
+ if( read( 0, header, 7 ) == 7 && strncmp( header, "environ", 7 ) == 0 ) {
+ unsigned count;
+ if( read( 0, &count, sizeof( unsigned )) == sizeof( unsigned )
+ && count && count < (1<<16)) {
+ char** env = malloc(( count + 1 ) * sizeof( char* ));
+ int ok = 1;
+ for( i = 0;
+ i < count && ok;
+ ++i ) {
+ unsigned len;
+ if( read( 0, &len, sizeof( unsigned )) == sizeof( unsigned )
+ && len && len < (1<<12)) {
+ env[ i ] = malloc( len + 1 );
+ if( (unsigned) read( 0, env[ i ], len ) == len ) {
+ env[ i ][ len ] = '\0';
+ } else {
+ ok = 0;
+ }
+ }
+ }
+ if( ok ) {
+ env[ i ] = NULL;
+ orig_environ = env;
+ }
+ }
+ }
+ if(argc == 0)
+ return 1;
+ new_argc = argc + 2;
+ new_argv = malloc( sizeof( char* ) * ( new_argc + 1 ));
+ if( new_argv == NULL )
+ return 1;
+ new_argv[ 0 ] = EXECUTE;
+ new_argv[ 1 ] = "--oom-pipe";
+ sprintf( helper_num, "%d", pipes[ 1 ] );
+ new_argv[ 2 ] = helper_num;
+ for( i = 1;
+ i <= (unsigned) argc;
+ ++i )
+ new_argv[ i + 2 ] = argv[ i ];
+ if( orig_environ )
+ execve(EXECUTE, (char**)new_argv, orig_environ);
+ else
+ execv(EXECUTE, (char**)new_argv);
+ perror(EXECUTE);
+ return 1;
+ case 0: /* child, keep privileges and do the privileged work */
+ close( pipes[ 1 ] );
+ for(;;) {
+ pid_t pid = 0;
+ int ret = read( pipes[ 0 ], &pid, sizeof( pid_t ));
+ if( ret < 0 && errno == EINTR )
+ continue;
+ if( ret <= 0 ) /* pipe closed or error, exit */
+ _exit(0);
+ if( pid != 0 ) {
+ if (set_protection( pid, 0 ))
+ kill( pid, SIGUSR1 );
+ }
+ }
+ }
+}
+
+#else /* not Linux, the simple non-setuid case */
+
+int main(int argc, char **argv)
+{
+ if(argc == 0)
+ return 1;
+ argv[0] = (char*)EXECUTE;
+ execv(EXECUTE,argv);
+ perror(EXECUTE);
+ return 1;
+}
+#endif
Index: kinit/start_kdeinit_wrapper.c
===================================================================
--- kinit/start_kdeinit_wrapper.c (revision 0)
+++ kinit/start_kdeinit_wrapper.c (revision 1001887)
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2007 Lubos Lunak <l.lunak@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <config-prefix.h>
+#include <config-kdeinit.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define EXECUTE LIBEXEC_INSTALL_DIR "/start_kdeinit"
+
+#ifdef KDEINIT_OOM_PROTECT
+
+/*
+ The start_kdeinit wrapper is setuid, which means some shell variables like LD_LIBRARY_PATH
+ get unset before it's launched. However kdeinit is used to launch most of KDE, so such variables
+ should not be dropped. Therefore this wrapper for the setuid wrapper read the environment
+ and writes it to start_kdeinit's stdin, which after dropping priviledges reads it and uses it
+ for launching the real kdeinit.
+*/
+int main(int argc, char **argv)
+{
+ int pipes[ 2 ];
+ if(argc == 0)
+ return 1;
+ if( pipe( pipes ) < 0 ) {
+ perror( "pipe()" );
+ return 1;
+ }
+ switch( fork()) {
+ case -1:
+ perror( "fork()" );
+ return 1;
+ default: /* parent, exec */
+ close( pipes[ 1 ] );
+ close( 0 ); /* stdin */
+ dup2( pipes[ 0 ], 0 );
+ close( pipes[ 0 ] );
+ argv[ 0 ] = (char*)EXECUTE;
+ execvp(EXECUTE, argv);
+ perror("start_kdeinit");
+ return 1;
+ case 0: { /* child, pass env and exit */
+ extern char** environ;
+ int i;
+ close( pipes[ 0 ] );
+ write( pipes[ 1 ], "environ", 7 ); /* header, just in case */
+ for( i = 0;
+ environ[ i ] != NULL;
+ ++i )
+ {}
+ write( pipes[ 1 ], &i, sizeof( int )); /* write count */
+ for( i = 0;
+ environ[ i ] != NULL;
+ ++i )
+ {
+ int len = strlen( environ[ i ] );
+ write( pipes[ 1 ], &len, sizeof( int )); /* write length */
+ write( pipes[ 1 ], environ[ i ], strlen( environ[ i ] ));
+ }
+ close( pipes[ 1 ] );
+ }
+ }
+ return 0;
+}
+
+#else /* not Linux, the simple non-setuid case */
+
+int main(int argc, char **argv)
+{
+ if(argc == 0)
+ return 1;
+ argv[0] = (char*)EXECUTE;
+ execvp(EXECUTE,argv);
+ perror("start_kdeinit");
+ return 1;
+}
+#endif
Index: kinit/proctitle.cpp
===================================================================
--- kinit/proctitle.cpp (revision 1000152)
+++ kinit/proctitle.cpp (revision 1001887)
@@ -80,6 +80,7 @@
#if PF_ARGV_TYPE == PF_ARGV_WRITEABLE /* Only this mode uses LastArgv */
static char *LastArgv = NULL;
+static char *cleanUpTo = NULL;
#endif
/**
@@ -114,6 +115,7 @@
LastArgv = argv[i] + strlen(argv[i]);
}
}
+ cleanUpTo = LastArgv;
for (i = 0; envp[i] != NULL; i++) {
/* must not overwrite XDG_SESSION_COOKIE */
@@ -201,17 +203,15 @@
/* We can overwrite individual argv[] arguments. Semi-nice. */
snprintf(Argv[0], maxlen, "%s", statbuf);
p = &Argv[0][i];
-
- /* null terminate it, but don't clear the rest of the
- memory that is usually used for environment variables. Some
+ /* Clear the rest used by arguments, but don't clear the memory
+ that is usually used for environment variables. Some
tools, like ConsoleKit must have access to the process'es initial
environment (more exact, the XDG_SESSION_COOKIE variable stored there).
If this code causes another side effect, we have to specifically
always append those variables to our environment. */
+ while (p < cleanUpTo)
+ *p++ = '\0';
- if (p < LastArgv)
- *p = '\0';
-
Argv[1] = NULL;
# endif /* PF_ARGV_WRITEABLE */
Index: kinit/kinit.cpp
===================================================================
--- kinit/kinit.cpp (revision 1000152)
+++ kinit/kinit.cpp (revision 1001887)
@@ -22,6 +22,7 @@
#define QT_NO_CAST_FROM_ASCII
#include <config.h>
+#include <config-kdeinit.h>
#include <sys/types.h>
#include <sys/time.h>
@@ -34,6 +35,7 @@
#include <sys/select.h> // Needed on some systems.
#endif
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include "proctitle.h"
@@ -84,12 +86,6 @@
#include <kstartupinfo.h>
#endif
-#if KDE_IS_VERSION( 3, 90, 0 )
-#ifdef __GNUC__
-#warning Check if Linux OOM-killer still sucks and if yes, forwardport revision 579164 and following fixes.
-#endif
-#endif
-
// #define SKIP_PROCTITLE 1
extern char **environ;
@@ -421,6 +417,38 @@
return QFile::encodeName(execpath);
}
+#ifdef KDEINIT_OOM_PROTECT
+static int oom_pipe = -1;
+
+static void oom_protect_sighandler( int ) {
+}
+
+static void reset_oom_protect() {
+ if( oom_pipe <= 0 )
+ return;
+ struct sigaction act, oldact;
+ act.sa_handler = oom_protect_sighandler;
+ act.sa_flags = 0;
+ sigemptyset( &act.sa_mask );
+ sigaction( SIGUSR1, &act, &oldact );
+ sigset_t sigs, oldsigs;
+ sigemptyset( &sigs );
+ sigaddset( &sigs, SIGUSR1 );
+ sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
+ pid_t pid = getpid();
+ if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) {
+ sigsuspend( &oldsigs ); // wait for the signal to come
+ }
+ sigprocmask( SIG_SETMASK, &oldsigs, NULL );
+ sigaction( SIGUSR1, &oldact, NULL );
+ close( oom_pipe );
+ oom_pipe = -1;
+}
+#else
+static void reset_oom_protect() {
+}
+#endif
+
static pid_t launch(int argc, const char *_name, const char *args,
const char *cwd=0, int envc=0, const char *envs=0,
bool reset_env = false,
@@ -505,6 +533,7 @@
/** Child **/
close(d.fd[0]);
close_fds();
+ reset_oom_protect();
// Try to chdir, either to the requested directory or to the user's document path by default.
// We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist,
@@ -1601,6 +1630,10 @@
printf("KDE: %s\n", KDE_VERSION_STRING);
exit(0);
}
+#ifdef KDEINIT_OOM_PROTECT
+ if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc)
+ oom_pipe = atol(argv[i+1]);
+#endif
if (strcmp(safe_argv[i], "--help") == 0)
{
printf("Usage: kdeinit4 [options]\n");
@@ -1750,7 +1783,11 @@
#endif
handle_requests(pid);
}
- else if (safe_argv[i][0] == '-')
+ else if (safe_argv[i][0] == '-'
+#ifdef KDEINIT_OOM_PROTECT
+ || isdigit(safe_argv[i][0])
+#endif
+ )
{
// Ignore
}
Index: kinit/config-kdeinit.h.cmake
===================================================================
--- kinit/config-kdeinit.h.cmake (revision 1000152)
+++ kinit/config-kdeinit.h.cmake (revision 1001887)
@@ -1,8 +1,10 @@
-// These are for proctitle.cpp:
+/* These are for proctitle.cpp: */
#cmakedefine HAVE___PROGNAME 1
#cmakedefine HAVE___PROGNAME_FULL 1
#cmakedefine HAVE_SYS_PSTAT_H 1
#cmakedefine HAVE_PSTAT 1
#cmakedefine HAVE_SETPROCTITLE 1
+/* for start_kdeinit */
+#cmakedefine KDEINIT_OOM_PROTECT 1
Index: kinit/CMakeLists.txt
===================================================================
--- kinit/CMakeLists.txt (revision 1000152)
+++ kinit/CMakeLists.txt (revision 1001887)
@@ -17,8 +17,6 @@
check_include_files(unistd.h HAVE_UNISTD_H)
check_function_exists(pstat HAVE_PSTAT)
check_function_exists(setproctitle HAVE_SETPROCTITLE)
-configure_file(config-kdeinit.h.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/config-kdeinit.h )
# used by 4 executables in this file
@@ -149,7 +147,35 @@
target_link_libraries( klauncher kdeinit_klauncher )
install(TARGETS klauncher DESTINATION ${LIBEXEC_INSTALL_DIR} )
+########### start_kdeinit ###############
+if (NOT WIN32)
+ set(start_kdeinit_SRCS start_kdeinit.c)
+ kde4_add_executable(start_kdeinit ${start_kdeinit_SRCS})
+ if(KDE4_ENABLE_FPIE)
+ macro_add_compile_flags(start_kdeinit ${KDE4_CXX_FPIE_FLAGS})
+ macro_add_link_flags(start_kdeinit ${KDE4_PIE_LDFLAGS})
+ endif(KDE4_ENABLE_FPIE)
+
+ set(start_kdeinit_wrapper_SRCS start_kdeinit_wrapper.c)
+ kde4_add_executable(start_kdeinit_wrapper ${start_kdeinit_wrapper_SRCS})
+
+ install(TARGETS start_kdeinit DESTINATION ${LIBEXEC_INSTALL_DIR})
+ install(TARGETS start_kdeinit_wrapper DESTINATION ${LIBEXEC_INSTALL_DIR})
+endif (NOT WIN32)
+
+if (CMAKE_SYSTEM_NAME MATCHES Linux)
+ MESSAGE(STATUS "Using setuid root kdeinit wrapper in order to protect it from bad Linux OOM-killer")
+ set(KDEINIT_OOM_PROTECT 1)
+ install(CODE "
+ set(START_KDEINIT_PATH \"\$ENV{DESTDIR}${LIBEXEC_INSTALL_DIR}/start_kdeinit\")
+ EXECUTE_PROCESS(COMMAND sh -c \"chown root '\${START_KDEINIT_PATH}' && chmod u+s '\${START_KDEINIT_PATH}'\")
+ ")
+endif (CMAKE_SYSTEM_NAME MATCHES Linux)
+
########### install files ###############
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KLauncher.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} )
+########### config-kdeinit.h ############
+configure_file(config-kdeinit.h.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/config-kdeinit.h )