1 
   2 // This was not accepted into Wine because it allows Windows applications to deadlock the system. 
   3 // However, they can do that on Windows too, and this is not a very useful behavior on a desktop, 
   4 // not even for attackers. It requires root on Linux because some servers have untrusted users.
   5 
   6 // When wineserver is suid root this patch will make games that require SetThreadPriority for audio work correctly.
   7 
   8 diff --git a/configure.ac b/configure.ac
   9 index 2c92dc9..5ce6c55 100644
  10 --- a/configure.ac
  11 +++ b/configure.ac
  12 @@ -233,6 +233,7 @@ AC_CHECK_HEADERS(\
  13     stdint.h \
  14     strings.h \
  15     sys/asoundlib.h \
  16 +    sys/capability.h \
  17     sys/cdio.h \
  18     sys/elf32.h \
  19     sys/epoll.h \
  20 @@ -1193,6 +1194,7 @@ then
  21    WINE_GET_SONAME(ungif,DGifOpen)
  22    WINE_GET_SONAME(gif,DGifOpen)
  23    WINE_GET_SONAME(capi20,capi20_isinstalled)
  24 +  WINE_GET_SONAME(cap,cap_set_proc)
  25  fi
  26  
  27  
  28 diff --git a/server/main.c b/server/main.c
  29 index 43d3cf7..d283c12 100644
  30 --- a/server/main.c
  31 +++ b/server/main.c
  32 @@ -28,6 +28,13 @@
  33  #include <stdlib.h>
  34  #include <sys/time.h>
  35  #include <unistd.h>
  36 +#include <dlfcn.h>
  37 +
  38 +#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H) && defined(SONAME_LIBCAP)
  39 +#define USE_CAPS
  40 +#include <sys/capability.h>
  41 +#include <sys/prctl.h>
  42 +#endif
  43  
  44  #include "object.h"
  45  #include "file.h"
  46 @@ -108,6 +115,51 @@ static void parse_args( int argc, char *
  47      }
  48  }
  49  
  50 +static void setup_security()
  51 +{
  52 +#ifdef USE_CAPS
  53 +    cap_t cap;
  54 +
  55 +    typeof(cap_set_proc) *cap_set_proc;
  56 +    typeof(cap_from_text) *cap_from_text;
  57 +    typeof(cap_free) *cap_free;
  58 +    void *libcap;
  59 +
  60 +    if (geteuid() != 0) return;
  61 +    if (getuid() == 0) return;
  62 +    
  63 +    /* check we have libcap */
  64 +    if ((libcap = dlopen( SONAME_LIBCAP, RTLD_LAZY )))
  65 +    {
  66 +        cap_set_proc  = dlsym( libcap, "cap_set_proc" );
  67 +        cap_from_text = dlsym( libcap, "cap_from_text" );
  68 +        cap_free      = dlsym( libcap, "cap_free" );
  69 +
  70 +        /* if these trigger, the user has a broken libcap */
  71 +        assert( cap_set_proc );
  72 +        assert( cap_from_text );
  73 +        assert( cap_free );
  74 +        
  75 +        /* ok, keep root capabilities as we transition to the regular user */
  76 +        prctl( PR_SET_KEEPCAPS, 1, 0, 0, 0 );
  77 +    }
  78 +    
  79 +    /* switch user - if no libcap, we lose all root privs here */
  80 +    setuid( getuid() );
  81 +
  82 +    if (libcap)
  83 +    {
  84 +        /* drop all privs except CAP_SYS_NICE, needed for SetThreadPriority */
  85 +        if (cap_set_proc((cap = cap_from_text( "CAP_SYS_NICE+pe" ))) < 0)
  86 +        {
  87 +            perror( "wineserver: cap_set_proc: failed to drop privs, aborting" );
  88 +            exit( 1 );
  89 +        }
  90 +        cap_free(cap);
  91 +    }
  92 +#endif    
  93 +}
  94 +
  95  static void sigterm_handler( int signum )
  96  {
  97      exit(1);  /* make sure atexit functions get called */
  98 @@ -115,6 +167,8 @@ static void sigterm_handler( int signum 
  99  
 100  int main( int argc, char *argv[] )
 101  {
 102 +    setup_security();
 103 +    
 104      parse_args( argc, argv );
 105  
 106      /* setup temporary handlers before the real signal initialization is done */
 107 diff --git a/server/thread.c b/server/thread.c
 108 index e5ef779..ef57972 100644
 109 --- a/server/thread.c
 110 +++ b/server/thread.c
 111 @@ -32,6 +32,9 @@
 112  #include <sys/types.h>
 113  #include <unistd.h>
 114  #include <time.h>
 115 +#ifdef HAVE_SCHED_H
 116 +#include <sched.h>
 117 +#endif
 118  #ifdef HAVE_POLL_H
 119  #include <poll.h>
 120  #endif
 121 @@ -313,12 +316,59 @@ struct thread *get_thread_from_pid( int 
 122      return NULL;
 123  }
 124  
 125 +static void set_thread_priority( int unix_tid, int ntprio )
 126 +{
 127 +#ifdef HAVE_SCHED_H    
 128 +    struct sched_param param;
 129 +    int result, scheduler;
 130 +
 131 +    assert( unix_tid != -1 );
 132 +
 133 +    if (ntprio == THREAD_PRIORITY_TIME_CRITICAL)
 134 +    {
 135 +        param.sched_priority = 1;
 136 +        scheduler = SCHED_FIFO;
 137 +    }
 138 +    else
 139 +    {
 140 +        param.sched_priority = 0;
 141 +        scheduler = SCHED_OTHER;
 142 +    }
 143 +    
 144 +    result = sched_setscheduler( unix_tid, scheduler, &param );
 145 +
 146 +    if (result == 0) return;
 147 +    
 148 +    if (result == -EPERM)
 149 +    {
 150 +        static BOOL warned = FALSE;
 151 +                
 152 +        if (!warned)
 153 +        {
 154 +            fprintf( stderr, "\nwineserver: Failed to promote the priority of a time critical thread.\n" );
 155 +            fprintf( stderr, "wineserver: Audio may destabilise. Try making wineserver suid root.\n" );
 156 +            warned = TRUE;
 157 +        }
 158 +
 159 +        return;
 160 +    }
 161 +
 162 +    perror( "wineserver: sched_setscheduler" );
 163 +#endif    
 164 +}
 165 +
 166  /* set all information about a thread */
 167  static void set_thread_info( struct thread *thread,
 168                               const struct set_thread_info_request *req )
 169  {
 170      if (req->mask & SET_THREAD_INFO_PRIORITY)
 171 +    {
 172 +        if ((thread->priority != req->priority) && (thread->unix_tid != -1))
 173 +            set_thread_priority( thread->unix_tid, req->priority );
 174 +        
 175          thread->priority = req->priority;
 176 +    }
 177 +
 178      if (req->mask & SET_THREAD_INFO_AFFINITY)
 179      {
 180          if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER );
 181 @@ -872,6 +922,10 @@ DECL_HANDLER(init_thread)
 182      }
 183      debug_level = max( debug_level, req->debug_level );
 184  
 185 +    /* we may have raced with a SetThreadPriority call */
 186 +    if (current->priority != THREAD_PRIORITY_NORMAL)
 187 +        set_thread_priority( current->unix_tid, current->priority );
 188 +    
 189      reply->pid     = get_process_id( process );
 190      reply->tid     = get_thread_id( current );
 191      reply->version = SERVER_PROTOCOL_VERSION;
 192 ----
 193 CategoryDevelopment
 194 

Implement SetThreadPriority (last edited 2012-07-24 20:04:55 by KyleAuble)