美文网首页
"WAVM: Incorrect bounds che

"WAVM: Incorrect bounds che

作者: learnforever01 | 来源:发表于2018-05-31 12:31 被阅读66次

WAVM: Incorrect bounds check when translating a reference type can results in buffer overrun

出问题的代码如下:
libraries/chain/include/eosio/chain/webassembly/wavm.hpp

/**
 * Specialization for transcribing  a reference type in the native method signature
 *    This type transcribes into an int32  pointer checks the validity of that memory
 *    range before dispatching to the native method
 *
 * @tparam Ret - the return type of the native method
 * @tparam Inputs - the remaining native parameters to transcribe
 * @tparam Translated - the list of transcribed wasm parameters
 */
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T &, Inputs...>, std::tuple<Translated...>> {
   using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
   using then_type = Ret (*)(running_instance_context &, T &, Inputs..., Translated...);

   template<then_type Then, typename U=T>
   static auto translate_one(running_instance_context& ctx, Inputs... rest, Translated... translated, I32 ptr) -> std::enable_if_t<std::is_const<U>::value, Ret> {
      // references cannot be created for null pointers
      FC_ASSERT((U32)ptr != 0);
      MemoryInstance* mem = ctx.memory;
      if(!mem || (U32)ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
         Runtime::causeException(Exception::Cause::accessViolation);
      T &base = *(T*)(getMemoryBaseAddress(mem)+(U32)ptr);
      if ( reinterpret_cast<uintptr_t>(&base) % alignof(T) != 0 ) {
         wlog( "misaligned const reference" );
         std::remove_const_t<T> copy;
         T* copy_ptr = &copy;
         memcpy( (void*)copy_ptr, (void*)&base, sizeof(T) );
         return Then(ctx, *copy_ptr, rest..., translated...);
      }
      return Then(ctx, base, rest..., translated...);
   }

   template<then_type Then, typename U=T>
   static auto translate_one(running_instance_context& ctx, Inputs... rest, Translated... translated, I32 ptr) -> std::enable_if_t<!std::is_const<U>::value, Ret> {
      // references cannot be created for null pointers
      FC_ASSERT((U32)ptr != 0);
      MemoryInstance* mem = ctx.memory;
      if(!mem || (U32)ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
         Runtime::causeException(Exception::Cause::accessViolation);
      T &base = *(T*)(getMemoryBaseAddress(mem)+(U32)ptr);
      if ( reinterpret_cast<uintptr_t>(&base) % alignof(T) != 0 ) {
         wlog( "misaligned reference" );
         std::remove_const_t<T> copy;
         T* copy_ptr = &copy;
         memcpy( (void*)copy_ptr, (void*)&base, sizeof(T) );
         Ret ret = Then(ctx, *copy_ptr, rest..., translated...);
         memcpy( (void*)&base, (void*)copy_ptr, sizeof(T) );
         return ret;
      }
      return Then(ctx, base, rest..., translated...);
   }

   template<then_type Then>
   static const auto fn() {
      return next_step::template fn<translate_one<Then>>();
   }
};

这是一个调用包含reference类型native函数的结构,例如

void printi128( const int128_t* value );

这个api就会用这个结构进行调用。

出问题的代码如下,下面是已经修复的代码:

      if(!mem || (U32)ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))

原来的代码如下:

      if(!mem || ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))

即没有加(U32)将int转换成unsigned int,而sizeof(T)是一个unsigned long,在64位平台上占用8个字节。所以当ptr和sizeof(T)相加时,编译器会将ptr扩展为8个字节,但是扩展的方式是和ptr的有signed和unsigned有关的,例如int类型的-1扩展成8字节的unsigned long时,实际上在内存中的表示就从0xffffffff变成0xffffffffffffffff,而如果是先将int类型的-1转成unsigned int,在这里是(U32)ptr,这样编译器在对其进行扩展的时候就会变成0x00000000ffffffff,即高位补0,所以(U32)ptr+sizeof(T)和ptr+sizeof(T)的结果是不一样的。

这样,当没有加U32强制转换时,一个负的ptr就有可能绕过检测,而调用下面的代码:

      T &base = *(T*)(getMemoryBaseAddress(mem)+ptr);

现己修复成

      T &base = *(T*)(getMemoryBaseAddress(mem)+(U32)ptr);

这样会造成覆盖不在wasm空间之内的内存,造成程序的数据的破坏。

相关文章

网友评论

      本文标题:"WAVM: Incorrect bounds che

      本文链接:https://www.haomeiwen.com/subject/gskqsftx.html