vestigia

Detect where execution depends on secrets.
git clone git://git.ppad.tech/vestigia.git
Log | Files | Refs | README | LICENSE

memcheck-operand.patch (11714B)


      1 memcheck lets an undefined operand flow into a division's quotient
      2 without a word: it reports an undefined value only where it decides a
      3 branch or forms an address. An integer division takes a time that
      4 depends on its operands, so a division of or by a secret leaks it,
      5 and a taint trace should say so.
      6 
      7 This adds an error kind, UninitOperand ("Integer division depends on
      8 uninitialised value(s)", suppressed as "Operand"), with its origin
      9 tracked as a condition's is, and reports it for each undefined
     10 operand of every integer division the guest's code is lifted to:
     11 DivModU/S128to64, 64to32, 64to64, and 32to32 (x86_64's DIV and IDIV
     12 among them), DivU/S32 and 64 and their E forms (aarch64's UDIV and
     13 SDIV), and the 128-bit divisions and modulos. Unlike the checks
     14 before an address or a branch, it leaves the operands undefined
     15 after reporting them, so the quotient's undefinedness, and what is
     16 reported downstream of it, is as it would have been.
     17 
     18 --- a/memcheck/mc_errors.c
     19 +++ b/memcheck/mc_errors.c
     20 @@ -63,6 +63,7 @@
     21     enum { 
     22        Err_Value,
     23        Err_Cond,
     24 +      Err_Operand,
     25        Err_CoreMem,
     26        Err_Addr, 
     27        Err_Jump, 
     28 @@ -108,6 +109,14 @@
     29           ExeContext* origin_ec; // filled in later
     30        } Cond;
     31  
     32 +      // Use of an undefined value as an operand of an integer
     33 +      // division, whose time depends on its operands.
     34 +      struct {
     35 +         // Origin info
     36 +         UInt        otag;      // origin tag
     37 +         ExeContext* origin_ec; // filled in later
     38 +      } Operand;
     39 +
     40        // Addressability error in core (signal-handling) operation.
     41        // It would be good to get rid of this error kind, merge it with
     42        // another one somehow.
     43 @@ -527,6 +536,26 @@
     44           }
     45           break;
     46  
     47 +      case Err_Operand:
     48 +         MC_(any_value_errors) = True;
     49 +         if (xml) {
     50 +            emit( "  <kind>UninitOperand</kind>\n" );
     51 +            emit( "  <what>Integer division depends"
     52 +                  " on uninitialised value(s)</what>\n" );
     53 +            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
     54 +            if (extra->Err.Operand.origin_ec)
     55 +               mc_pp_origin( extra->Err.Operand.origin_ec,
     56 +                             extra->Err.Operand.otag & 3 );
     57 +         } else {
     58 +            emit( "Integer division depends"
     59 +                  " on uninitialised value(s)\n" );
     60 +            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
     61 +            if (extra->Err.Operand.origin_ec)
     62 +               mc_pp_origin( extra->Err.Operand.origin_ec,
     63 +                             extra->Err.Operand.otag & 3 );
     64 +         }
     65 +         break;
     66 +
     67        case Err_RegParam:
     68           MC_(any_value_errors) = True;
     69           if (xml) {
     70 @@ -936,6 +965,18 @@
     71     VG_(maybe_record_error)( tid, Err_Cond, /*addr*/0, /*s*/NULL, &extra );
     72  }
     73  
     74 +void MC_(record_operand_error) ( ThreadId tid, UInt otag )
     75 +{
     76 +   MC_Error extra;
     77 +   tl_assert( MC_(clo_mc_level) >= 2 );
     78 +   if (otag > 0)
     79 +      tl_assert( MC_(clo_mc_level) == 3 );
     80 +   extra.Err.Operand.otag      = otag;
     81 +   extra.Err.Operand.origin_ec = NULL;  /* Filled in later */
     82 +   VG_(maybe_record_error)( tid, Err_Operand, /*addr*/0, /*s*/NULL,
     83 +                            &extra );
     84 +}
     85 +
     86  /* --- Called from non-generated code --- */
     87  
     88  /* This is for memory errors in signal-related memory. */
     89 @@ -1214,6 +1255,7 @@
     90        case Err_IllegalMempool:
     91        case Err_Overlap:
     92        case Err_Cond:
     93 +      case Err_Operand:
     94        case Err_ReallocSizeZero:
     95        case Err_UnsafeZeroSize:
     96           return True;
     97 @@ -1423,6 +1465,10 @@
     98        update_origin( &extra->Err.Cond.origin_ec,
     99                       extra->Err.Cond.otag );
    100        return sizeof(MC_Error);
    101 +   case Err_Operand:
    102 +      update_origin( &extra->Err.Operand.origin_ec,
    103 +                     extra->Err.Operand.otag );
    104 +      return sizeof(MC_Error);
    105     case Err_RegParam:
    106        update_origin( &extra->Err.RegParam.origin_ec,
    107                       extra->Err.RegParam.otag );
    108 @@ -1557,6 +1603,9 @@
    109        // Undefined value error in conditional.
    110        CondSupp,
    111  
    112 +      // Undefined value error in an integer division's operand.
    113 +      OperandSupp,
    114 +
    115        // Unaddressable read/write attempt at given size
    116        Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp, Addr32Supp,
    117  
    118 @@ -1593,6 +1642,7 @@
    119     else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp;
    120     else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp;
    121     else if (VG_STREQ(name, "Cond"))    skind = CondSupp;
    122 +   else if (VG_STREQ(name, "Operand")) skind = OperandSupp;
    123     else if (VG_STREQ(name, "Value0"))  skind = CondSupp; /* backwards compat */
    124     else if (VG_STREQ(name, "Value1"))  skind = Value1Supp;
    125     else if (VG_STREQ(name, "Value2"))  skind = Value2Supp;
    126 @@ -1739,6 +1789,9 @@
    127        case CondSupp:
    128           return (ekind == Err_Cond);
    129  
    130 +      case OperandSupp:
    131 +         return (ekind == Err_Operand);
    132 +
    133        case Addr1Supp: su_szB = 1; goto addr_case;
    134        case Addr2Supp: su_szB = 2; goto addr_case;
    135        case Addr4Supp: su_szB = 4; goto addr_case;
    136 @@ -1822,6 +1875,7 @@
    137     case Err_Overlap:         return "Overlap";
    138     case Err_Leak:            return "Leak";
    139     case Err_Cond:            return "Cond";
    140 +   case Err_Operand:         return "Operand";
    141     case Err_FishyValue:      return "FishyValue";
    142     case Err_ReallocSizeZero: return "ReallocZero";
    143     case Err_BadAlign:        return "BadAlign";
    144 --- a/memcheck/mc_include.h
    145 +++ b/memcheck/mc_include.h
    146 @@ -551,6 +551,7 @@
    147  void MC_(record_address_error) ( ThreadId tid, Addr a, Int szB,
    148                                   Bool isWrite );
    149  void MC_(record_cond_error)    ( ThreadId tid, UInt otag );
    150 +void MC_(record_operand_error) ( ThreadId tid, UInt otag );
    151  void MC_(record_value_error)   ( ThreadId tid, Int szB, UInt otag );
    152  void MC_(record_jump_error)    ( ThreadId tid, Addr a );
    153  
    154 @@ -773,6 +774,7 @@
    155  VG_REGPARM(1) void MC_(helperc_value_check4_fail_w_o) ( UWord );
    156  VG_REGPARM(1) void MC_(helperc_value_check1_fail_w_o) ( UWord );
    157  VG_REGPARM(1) void MC_(helperc_value_check0_fail_w_o) ( UWord );
    158 +VG_REGPARM(1) void MC_(helperc_operand_check_fail_w_o) ( UWord );
    159  
    160  /* And call these ones instead to report an uninitialised value error
    161     but with no origin available. */
    162 @@ -781,6 +783,7 @@
    163  VG_REGPARM(0) void MC_(helperc_value_check4_fail_no_o) ( void );
    164  VG_REGPARM(0) void MC_(helperc_value_check1_fail_no_o) ( void );
    165  VG_REGPARM(0) void MC_(helperc_value_check0_fail_no_o) ( void );
    166 +VG_REGPARM(0) void MC_(helperc_operand_check_fail_no_o) ( void );
    167  
    168  /* V-bits load/store helpers */
    169  VG_REGPARM(1) void MC_(helperc_STOREV64be) ( Addr, ULong );
    170 --- a/memcheck/mc_main.c
    171 +++ b/memcheck/mc_main.c
    172 @@ -5751,6 +5751,11 @@
    173  }
    174  
    175  VG_REGPARM(1)
    176 +void MC_(helperc_operand_check_fail_w_o) ( UWord origin ) {
    177 +   MC_(record_operand_error) ( VG_(get_running_tid)(), (UInt)origin );
    178 +}
    179 +
    180 +VG_REGPARM(1)
    181  void MC_(helperc_value_check1_fail_w_o) ( UWord origin ) {
    182     MC_(record_value_error) ( VG_(get_running_tid)(), 1, (UInt)origin );
    183  }
    184 @@ -5775,6 +5780,11 @@
    185  VG_REGPARM(0)
    186  void MC_(helperc_value_check0_fail_no_o) ( void ) {
    187     MC_(record_cond_error) ( VG_(get_running_tid)(), 0/*origin*/ );
    188 +}
    189 +
    190 +VG_REGPARM(0)
    191 +void MC_(helperc_operand_check_fail_no_o) ( void ) {
    192 +   MC_(record_operand_error) ( VG_(get_running_tid)(), 0/*origin*/ );
    193  }
    194  
    195  VG_REGPARM(0)
    196 --- a/memcheck/mc_translate.c
    197 +++ b/memcheck/mc_translate.c
    198 @@ -1772,6 +1772,57 @@
    199  }
    200  
    201  
    202 +/* Check the supplied *original* atoms, the operands of an integer
    203 +   division, for undefinedness, and emit a complaint for each that is
    204 +   undefined.  A division takes a time that depends on its operands,
    205 +   so an undefined operand is reported as its own kind of error
    206 +   (Err_Operand), apart from a value used as an address or a
    207 +   condition.  Unlike complainIfUndefined, the atoms are not then
    208 +   marked defined: the quotient carries its operands' undefinedness
    209 +   on, as it would had nothing been reported. */
    210 +static void complainIfOperandsUndefined ( MCEnv* mce,
    211 +                                          IRAtom* atom1, IRAtom* atom2 )
    212 +{
    213 +   IRAtom* atoms[2] = { atom1, atom2 };
    214 +   Int     k;
    215 +
    216 +   if (MC_(clo_mc_level) == 1)
    217 +      return;
    218 +
    219 +   for (k = 0; k < 2; k++) {
    220 +      IRAtom*  vatom;
    221 +      IRAtom*  origin;
    222 +      IRDirty* di;
    223 +
    224 +      tl_assert(isOriginalAtom(mce, atoms[k]));
    225 +      vatom = expr2vbits( mce, atoms[k], HuOth );
    226 +      tl_assert(isShadowAtom(mce, vatom));
    227 +
    228 +      if (MC_(clo_mc_level) == 3) {
    229 +         origin = schemeE( mce, atoms[k] );
    230 +         if (mce->hWordTy == Ity_I64) {
    231 +            origin = assignNew( 'B', mce, Ity_I64,
    232 +                                unop(Iop_32Uto64, origin) );
    233 +         }
    234 +         di = unsafeIRDirty_0_N(
    235 +                 1, "MC_(helperc_operand_check_fail_w_o)",
    236 +                 VG_(fnptr_to_fnentry)(
    237 +                    &MC_(helperc_operand_check_fail_w_o) ),
    238 +                 mkIRExprVec_1(origin) );
    239 +      } else {
    240 +         di = unsafeIRDirty_0_N(
    241 +                 0, "MC_(helperc_operand_check_fail_no_o)",
    242 +                 VG_(fnptr_to_fnentry)(
    243 +                    &MC_(helperc_operand_check_fail_no_o) ),
    244 +                 mkIRExprVec_0() );
    245 +      }
    246 +      di->guard = mkPCastTo( mce, Ity_I1, vatom );
    247 +      setHelperAnns( mce, di );
    248 +      stmt( 'V', mce, IRStmt_Dirty(di) );
    249 +   }
    250 +}
    251 +
    252 +
    253  /*------------------------------------------------------------*/
    254  /*--- Shadowing PUTs/GETs, and indexed variants thereof    ---*/
    255  /*------------------------------------------------------------*/
    256 @@ -4153,6 +4204,7 @@
    257        case Iop_ModU128:
    258        case Iop_ModS128:
    259           /* I128 x I128 -> I128 */
    260 +         complainIfOperandsUndefined(mce, atom1, atom2);
    261           return mkLazy2(mce, Ity_V128, vatom1, vatom2);
    262  
    263        case Iop_QNarrowBin64Sto32Sx4:
    264 @@ -4687,10 +4739,12 @@
    265  
    266        case Iop_DivModU64to32:
    267        case Iop_DivModS64to32:
    268 +         complainIfOperandsUndefined(mce, atom1, atom2);
    269           return mkLazy2(mce, Ity_I64, vatom1, vatom2);
    270  
    271        case Iop_DivModU128to64:
    272        case Iop_DivModS128to64:
    273 +         complainIfOperandsUndefined(mce, atom1, atom2);
    274           return mkLazy2(mce, Ity_I128, vatom1, vatom2);
    275  
    276        case Iop_8HLto16:
    277 @@ -4703,6 +4757,7 @@
    278        case Iop_DivModU64to64:
    279        case Iop_DivModS64to64: {
    280           IRAtom* vTmp64 = mkLazy2(mce, Ity_I64, vatom1, vatom2);
    281 +         complainIfOperandsUndefined(mce, atom1, atom2);
    282           return assignNew('V', mce, Ity_I128,
    283                            binop(Iop_64HLto128, vTmp64, vTmp64));
    284        }
    285 @@ -4718,6 +4773,7 @@
    286        case Iop_DivModU32to32:
    287        case Iop_DivModS32to32: {
    288           IRAtom* vTmp32 = mkLazy2(mce, Ity_I32, vatom1, vatom2);
    289 +         complainIfOperandsUndefined(mce, atom1, atom2);
    290           return assignNew('V', mce, Ity_I64,
    291                            binop(Iop_32HLto64, vTmp32, vTmp32));
    292        }
    293 @@ -4745,11 +4801,14 @@
    294           return assignNew('V', mce, Ity_I16, binop(Iop_8HLto16, vHi8, vLo8));
    295        }
    296  
    297 -      case Iop_Sad8Ux4: /* maybe we could do better?  ftm, do mkLazy2. */
    298        case Iop_DivS32:
    299        case Iop_DivU32:
    300        case Iop_DivU32E:
    301        case Iop_DivS32E:
    302 +         complainIfOperandsUndefined(mce, atom1, atom2);
    303 +         return mkLazy2(mce, Ity_I32, vatom1, vatom2);
    304 +
    305 +      case Iop_Sad8Ux4: /* maybe we could do better?  ftm, do mkLazy2. */
    306        case Iop_QAdd32S: /* could probably do better */
    307        case Iop_QSub32S: /* could probably do better */
    308           return mkLazy2(mce, Ity_I32, vatom1, vatom2);
    309 @@ -4758,6 +4817,7 @@
    310        case Iop_DivU64:
    311        case Iop_DivS64E:
    312        case Iop_DivU64E:
    313 +         complainIfOperandsUndefined(mce, atom1, atom2);
    314           return mkLazy2(mce, Ity_I64, vatom1, vatom2);
    315  
    316        case Iop_Add32: