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 )
openSUSE Build Service is sponsored by