Ticket #3910: 0002-Change-what-RTS-options-are-available-by-default.patch

File 0002-Change-what-RTS-options-are-available-by-default.patch, 16.2 KB (added by duncan, 19 months ago)

Patch: Change what +RTS options are available by default

  • rts/RtsFlags.c

    From 3784bb76447e5353d467c9adf465c3bcc0f0534f Mon Sep 17 00:00:00 2001
    From: Duncan Coutts <duncan@well-typed.com>
    Date: Thu, 27 Oct 2011 13:26:15 +0100
    Subject: [PATCH 2/2] Change what +RTS options are available by default
    
    Ticket #3910 originally pointed out that the RTS options are a potential
    security problem. For example the -t -s or -S flags can be used to
    overwrite files. This would be bad in the context of CGI scripts or
    setuid binaries. So we introduced a system where +RTS processing is more
    or less disabled unless you pass the -rtsopts flag at link time.
    
    This scheme is safe enough but it also really annoies users. They have
    to use -rtsopts in many circumstances: with -threaded to use -N, with
    -eventlog to use -l, with -prof to use any of the profiling flags. Many
    users just set -rtsopts globally or in project .cabal files. Apart from
    annoying users it reduces security because it means that deployed
    binaries will have all RTS options enabled rather than just profiling
    ones.
    
    This patch relaxes the set of RTS options that are available in the
    default -rtsopts=some case. For "deployment" ways like vanilla and
    -threaded we remain quite conservative. Only --info -? --help are
    allowed for vanilla. For -threaded, -N and -N<x> are allowed with a
    check that x <= num cpus.
    
    For "developer" ways like -debug, -eventlog, -prof, we allow all the
    options that are special to that way. Some of these allow writing files,
    but the file written is not directly under the control of the attacker.
    For the setuid case (where the attacker would have control over binary
    name, current dir, local symlinks etc) we check if the process is
    running setuid/setgid and refuse all RTS option processing. Users would
    need to use -rtsopts=all in this case.
    
    We are making the assumption that developers will not deploy binaries
    built in the -debug, -eventlog, -prof ways. And even if they do, the
    damage should be limited to DOS, information disclosure and writing
    files like <progname>.eventlog, not arbitrary files.
    ---
     rts/RtsFlags.c |  124 ++++++++++++++++++++++++++++++++++++++++++++++---------
     1 files changed, 103 insertions(+), 21 deletions(-)
    
    diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
    index d6da873..d2b4cfc 100644
    a b  
    2121 
    2222#include <string.h> 
    2323 
     24#ifdef HAVE_UNISTD_H 
     25#include <unistd.h> 
     26#endif 
     27 
     28#ifdef HAVE_SYS_TYPES_H 
     29#include <sys/types.h> 
     30#endif 
     31 
    2432// Flag Structure 
    2533RTS_FLAGS RtsFlags; 
    2634 
     
    526534 * procRtsOpts: Process rts_argv between rts_argc0 and rts_argc. 
    527535 * -------------------------------------------------------------------------- */ 
    528536 
     537static void checkUnsafe(RtsOptsEnabledEnum enabled) 
     538{ 
     539    if (enabled == RtsOptsSafeOnly) { 
     540        errorBelch("Most RTS options are disabled. Link with -rtsopts to enable them."); 
     541        stg_exit(EXIT_FAILURE); 
     542    } 
     543} 
     544 
    529545static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled) 
    530546{ 
    531547    rtsBool error = rtsFalse; 
    532548    int arg; 
    533549 
     550    if (enabled == RtsOptsNone) { 
     551        errorBelch("RTS options are disabled. Link with -rtsopts to enable them."); 
     552        stg_exit(EXIT_FAILURE); 
     553    } 
     554 
     555#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) 
     556    if (enabled == RtsOptsSafeOnly) { 
     557        /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE, 
     558           we'd have to link with -lcap for that. */ 
     559        if ((getuid() != geteuid()) || (getgid() != getegid())) { 
     560            errorBelch("RTS options are disabled for setuid binaries. Link with -rtsopts to enable them."); 
     561            stg_exit(EXIT_FAILURE); 
     562        } 
     563    } 
     564#endif 
     565 
    534566    // Process RTS (rts_argv) part: mainly to determine statsfile 
    535567    for (arg = rts_argc0; arg < rts_argc; arg++) { 
     568 
     569        /* We handle RtsOptsSafeOnly mode by declaring each option as 
     570           either OPTION_SAFE or OPTION_UNSAFE. To make sure we cover 
     571           every branch we use an option_checked flag which is reset 
     572           at the start each iteration and checked at the end. */ 
     573        rtsBool option_checked = rtsFalse; 
     574 
     575#define OPTION_SAFE option_checked = rtsTrue; 
     576#define OPTION_UNSAFE checkUnsafe(enabled); option_checked = rtsTrue; 
     577 
    536578        if (rts_argv[arg][0] != '-') { 
    537579            fflush(stdout); 
    538580            errorBelch("unexpected RTS argument: %s", rts_argv[arg]); 
     
    540582 
    541583        } else { 
    542584 
    543             if (enabled == RtsOptsNone) { 
    544                 errorBelch("RTS options are disabled. Link with -rtsopts to enable them."); 
    545                 stg_exit(EXIT_FAILURE); 
    546             } 
    547  
    548             switch(rts_argv[arg][1]) { 
    549             case '-': 
    550                 if (strequal("info", &rts_argv[arg][2])) { 
    551                     printRtsInfo(); 
    552                     stg_exit(0); 
    553                 } 
    554                 break; 
    555             default: 
    556                 break; 
    557             } 
    558  
    559             if (enabled == RtsOptsSafeOnly) { 
    560                 errorBelch("Most RTS options are disabled. Link with -rtsopts to enable them."); 
    561                 stg_exit(EXIT_FAILURE); 
    562             } 
    563  
    564585            switch(rts_argv[arg][1]) { 
    565586 
    566587              /* process: general args, then PROFILING-only ones, then 
     
    612633 
    613634              /* =========== GENERAL ========================== */ 
    614635              case '?': 
     636                OPTION_SAFE; 
    615637                error = rtsTrue; 
    616638                break; 
    617639 
     
    621643              case '-': 
    622644                  if (strequal("install-signal-handlers=yes", 
    623645                               &rts_argv[arg][2])) { 
     646                      OPTION_UNSAFE; 
    624647                      RtsFlags.MiscFlags.install_signal_handlers = rtsTrue; 
    625648                  } 
    626649                  else if (strequal("install-signal-handlers=no", 
    627650                               &rts_argv[arg][2])) { 
     651                      OPTION_UNSAFE; 
    628652                      RtsFlags.MiscFlags.install_signal_handlers = rtsFalse; 
    629653                  } 
    630654                  else if (strequal("machine-readable", 
    631655                               &rts_argv[arg][2])) { 
     656                      OPTION_UNSAFE; 
    632657                      RtsFlags.MiscFlags.machineReadable = rtsTrue; 
    633658                  } 
    634659                  else if (strequal("info", 
    635660                               &rts_argv[arg][2])) { 
     661                      OPTION_SAFE; 
    636662                      printRtsInfo(); 
    637663                      stg_exit(0); 
    638664                  } 
    639665                  else { 
     666                      OPTION_SAFE; 
    640667                      errorBelch("unknown RTS option: %s",rts_argv[arg]); 
    641668                      error = rtsTrue; 
    642669                  } 
    643670                  break; 
    644671              case 'A': 
     672                  OPTION_UNSAFE; 
    645673                  RtsFlags.GcFlags.minAllocAreaSize 
    646674                      = decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_INT_MAX) 
    647675                           / BLOCK_SIZE; 
     
    649677 
    650678#ifdef USE_PAPI 
    651679              case 'a': 
     680                OPTION_UNSAFE; 
    652681                switch(rts_argv[arg][2]) { 
    653682                case '1': 
    654683                  RtsFlags.PapiFlags.eventType = PAPI_FLAG_CACHE_L1; 
     
    686715#endif 
    687716 
    688717              case 'B': 
     718                OPTION_UNSAFE; 
    689719                RtsFlags.GcFlags.ringBell = rtsTrue; 
    690720                break; 
    691721 
    692722              case 'c': 
     723                  OPTION_UNSAFE; 
    693724                  if (rts_argv[arg][2] != '\0') { 
    694725                      RtsFlags.GcFlags.compactThreshold = 
    695726                          atof(rts_argv[arg]+2); 
     
    699730                  break; 
    700731 
    701732              case 'w': 
     733                OPTION_UNSAFE; 
    702734                RtsFlags.GcFlags.sweep = rtsTrue; 
    703735                break; 
    704736 
    705737              case 'F': 
     738                OPTION_UNSAFE; 
    706739                RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2); 
    707740               
    708741                if (RtsFlags.GcFlags.oldGenFactor < 0) 
     
    710743                break; 
    711744               
    712745              case 'D': 
     746              OPTION_SAFE; 
    713747              DEBUG_BUILD_ONLY( 
    714748              {  
    715749                  char *c; 
     
    772806              break; 
    773807 
    774808              case 'K': 
     809                  OPTION_UNSAFE; 
    775810                  RtsFlags.GcFlags.maxStkSize = 
    776811                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_); 
    777812                  break; 
    778813 
    779814              case 'k': 
     815                OPTION_UNSAFE; 
    780816                switch(rts_argv[arg][2]) { 
    781817                case 'c': 
    782818                  RtsFlags.GcFlags.stkChunkSize = 
     
    798834                break; 
    799835 
    800836              case 'M': 
     837                  OPTION_UNSAFE; 
    801838                  RtsFlags.GcFlags.maxHeapSize = 
    802839                      decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX) / BLOCK_SIZE; 
    803840                  /* user give size in *bytes* but "maxHeapSize" is in *blocks* */ 
    804841                  break; 
    805842 
    806843              case 'm': 
     844                  OPTION_UNSAFE; 
    807845                  RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2); 
    808846 
    809847                  if (RtsFlags.GcFlags.pcFreeHeap < 0 || 
     
    812850                  break; 
    813851 
    814852              case 'G': 
     853                  OPTION_UNSAFE; 
    815854                  RtsFlags.GcFlags.generations = 
    816855                      decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX); 
    817856                  break; 
    818857 
    819858              case 'H': 
     859                  OPTION_UNSAFE; 
    820860                  if (rts_argv[arg][2] == '\0') { 
    821861                      RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsTrue; 
    822862                  } else { 
     
    827867 
    828868#ifdef RTS_GTK_FRONTPANEL 
    829869              case 'f': 
     870                  OPTION_UNSAFE; 
    830871                  RtsFlags.GcFlags.frontpanel = rtsTrue; 
    831872                  break; 
    832873#endif 
    833874 
    834875              case 'I': /* idle GC delay */ 
     876                OPTION_UNSAFE; 
    835877                if (rts_argv[arg][2] == '\0') { 
    836878                  /* use default */ 
    837879                } else { 
     
    844886                break; 
    845887 
    846888              case 'T': 
     889                  OPTION_UNSAFE; 
    847890                  RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS; 
    848891                  break; /* Don't initialize statistics file. */ 
    849892 
    850893              case 'S': 
     894                  OPTION_UNSAFE; 
    851895                  RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS; 
    852896                  goto stats; 
    853897 
    854898              case 's': 
     899                  OPTION_UNSAFE; 
    855900                  RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS; 
    856901                  goto stats; 
    857902 
    858903              case 't': 
     904                  OPTION_UNSAFE; 
    859905                  RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS; 
    860906                  goto stats; 
    861907 
     
    869915                break; 
    870916 
    871917              case 'Z': 
     918                OPTION_UNSAFE; 
    872919                RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse; 
    873920                break; 
    874921 
     
    876923 
    877924              case 'P': /* detailed cost centre profiling (time/alloc) */ 
    878925              case 'p': /* cost centre profiling (time/alloc) */ 
     926                OPTION_SAFE; 
    879927                PROFILING_BUILD_ONLY( 
    880928                switch (rts_argv[arg][2]) { 
    881929                  case 'x': 
     
    897945                ) break; 
    898946 
    899947              case 'R': 
     948                  OPTION_SAFE; 
    900949                  PROFILING_BUILD_ONLY( 
    901950                      RtsFlags.ProfFlags.maxRetainerSetSize = atof(rts_argv[arg]+2); 
    902951                  ) break; 
    903952              case 'L': 
     953                  OPTION_SAFE; 
    904954                  PROFILING_BUILD_ONLY( 
    905955                      RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2); 
    906956                      if(RtsFlags.ProfFlags.ccsLength <= 0) { 
     
    909959                  ) break; 
    910960              case 'h': /* serial heap profile */ 
    911961#if !defined(PROFILING) 
     962                OPTION_UNSAFE; 
    912963                switch (rts_argv[arg][2]) { 
    913964                  case '\0': 
    914965                  case 'T': 
     
    919970                    error = rtsTrue; 
    920971                } 
    921972#else 
     973                OPTION_SAFE; 
    922974                PROFILING_BUILD_ONLY( 
    923975                switch (rts_argv[arg][2]) { 
    924976                case '\0': 
     
    10271079                break; 
    10281080 
    10291081              case 'i': /* heap sample interval */ 
     1082                OPTION_UNSAFE; 
    10301083                if (rts_argv[arg][2] == '\0') { 
    10311084                  /* use default */ 
    10321085                } else { 
     
    10401093 
    10411094              /* =========== CONCURRENT ========================= */ 
    10421095              case 'C': /* context switch interval */ 
     1096                OPTION_UNSAFE; 
    10431097                if (rts_argv[arg][2] == '\0') 
    10441098                    RtsFlags.ConcFlags.ctxtSwitchTime = 0; 
    10451099                else { 
     
    10521106                break; 
    10531107 
    10541108              case 'V': /* master tick interval */ 
     1109                OPTION_UNSAFE; 
    10551110                if (rts_argv[arg][2] == '\0') { 
    10561111                    // turns off ticks completely 
    10571112                    RtsFlags.MiscFlags.tickInterval = 0; 
     
    10661121 
    10671122#if !defined(NOSMP) 
    10681123              case 'N': 
     1124                OPTION_SAFE; 
    10691125                THREADED_BUILD_ONLY( 
    10701126                if (rts_argv[arg][2] == '\0') { 
    10711127#if defined(PROFILING) 
     
    10751131#endif 
    10761132                } else { 
    10771133                    int nNodes; 
     1134                    OPTION_SAFE; /* but see extra checks below... */ 
    10781135                    nNodes = strtol(rts_argv[arg]+2, (char **) NULL, 10); 
    10791136                    if (nNodes <= 0) { 
    10801137                      errorBelch("bad value for -N"); 
    10811138                      error = rtsTrue; 
    10821139                    } 
     1140                    if (enabled == RtsOptsSafeOnly && 
     1141                        nNodes > (int)getNumberOfProcessors()) { 
     1142                      errorBelch("Using large values for -N is not allowed by default. Link with -rtsopts to allow full control."); 
     1143                      stg_exit(EXIT_FAILURE); 
     1144                    } 
    10831145#if defined(PROFILING) 
    10841146                    if (nNodes > 1) { 
    10851147                        errorBelch("bad option %s: only -N1 is supported with profiling", rts_argv[arg]); 
     
    10911153                ) break; 
    10921154 
    10931155              case 'g': 
     1156                OPTION_UNSAFE; 
    10941157                THREADED_BUILD_ONLY( 
    10951158                    switch (rts_argv[arg][2]) { 
    10961159                    case '1': 
     
    11051168                    ) break; 
    11061169 
    11071170              case 'q': 
     1171                OPTION_UNSAFE; 
    11081172                THREADED_BUILD_ONLY( 
    11091173                    switch (rts_argv[arg][2]) { 
    11101174                    case '\0': 
     
    11481212#endif 
    11491213              /* =========== PARALLEL =========================== */ 
    11501214              case 'e': 
     1215                OPTION_UNSAFE; 
    11511216                THREADED_BUILD_ONLY( 
    11521217                if (rts_argv[arg][2] != '\0') { 
    11531218                    RtsFlags.ParFlags.maxLocalSparks 
     
    11621227              /* =========== TICKY ============================== */ 
    11631228 
    11641229              case 'r': /* Basic profiling stats */ 
     1230                OPTION_SAFE; 
    11651231                TICKY_BUILD_ONLY( 
    11661232 
    11671233                RtsFlags.TickyFlags.showTickyStats = rtsTrue; 
     
    11781244              /* =========== TRACING ---------=================== */ 
    11791245 
    11801246              case 'l': 
     1247                  OPTION_SAFE; 
    11811248                  TRACING_BUILD_ONLY( 
    11821249                      RtsFlags.TraceFlags.tracing = TRACE_EVENTLOG; 
    11831250                      read_trace_flags(&rts_argv[arg][2]); 
     
    11851252                  break; 
    11861253 
    11871254              case 'v': 
     1255                  OPTION_SAFE; 
    11881256                  DEBUG_BUILD_ONLY( 
    11891257                      RtsFlags.TraceFlags.tracing = TRACE_STDERR; 
    11901258                      read_trace_flags(&rts_argv[arg][2]); 
     
    11961264              case 'x': /* Extend the argument space */ 
    11971265                switch(rts_argv[arg][2]) { 
    11981266                  case '\0': 
     1267                    OPTION_SAFE; 
    11991268                    errorBelch("incomplete RTS option: %s",rts_argv[arg]); 
    12001269                    error = rtsTrue; 
    12011270                    break; 
    12021271 
    12031272                case 'b': /* heapBase in hex; undocumented */ 
     1273                    OPTION_UNSAFE; 
    12041274                    if (rts_argv[arg][3] != '\0') { 
    12051275                        RtsFlags.GcFlags.heapBase 
    12061276                            = strtol(rts_argv[arg]+3, (char **) NULL, 16); 
     
    12121282 
    12131283#if defined(x86_64_HOST_ARCH) 
    12141284                case 'm': /* linkerMemBase */ 
     1285                    OPTION_UNSAFE; 
    12151286                    if (rts_argv[arg][3] != '\0') { 
    12161287                        RtsFlags.MiscFlags.linkerMemBase 
    12171288                            = strtol(rts_argv[arg]+3, (char **) NULL, 16); 
     
    12261297#endif 
    12271298 
    12281299                case 'c': /* Debugging tool: show current cost centre on an exception */ 
     1300                    OPTION_SAFE; 
    12291301                    PROFILING_BUILD_ONLY( 
    12301302                        RtsFlags.ProfFlags.showCCSOnException = rtsTrue; 
    12311303                        ); 
    12321304                    break; 
    12331305 
    12341306                case 't':  /* Include memory used by TSOs in a heap profile */ 
     1307                    OPTION_SAFE; 
    12351308                    PROFILING_BUILD_ONLY( 
    12361309                        RtsFlags.ProfFlags.includeTSOs = rtsTrue; 
    12371310                        ); 
     
    12401313                  /* The option prefix '-xx' is reserved for future extension.  KSW 1999-11. */ 
    12411314 
    12421315                  default: 
     1316                    OPTION_SAFE; 
    12431317                    errorBelch("unknown RTS option: %s",rts_argv[arg]); 
    12441318                    error = rtsTrue; 
    12451319                    break; 
     
    12481322 
    12491323              /* =========== OH DEAR ============================ */ 
    12501324              default: 
     1325                OPTION_SAFE; 
    12511326                errorBelch("unknown RTS option: %s",rts_argv[arg]); 
    12521327                error = rtsTrue; 
    12531328                break; 
    12541329            } 
     1330 
     1331            if (!option_checked) { 
     1332                /* Naughty! Someone didn't use OPTION_UNSAFE / OPTION_SAFE for 
     1333                   an option above */ 
     1334                errorBelch("Internal error in the RTS options parser"); 
     1335                stg_exit(EXIT_FAILURE); 
     1336            } 
    12551337        } 
    12561338    } 
    12571339