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: