c# - 读取和写入内存地址以验证不安全代码中的内存分配

标签 c# memory allocation unsafe verify

我遇到了这段 C# 代码,它应该验证在早期阶段完成的内存分配。

for (int i = 0; i < Size; i++)
{
    var b = *(BaseAddress + i); // type of BaseAddress is byte*
    *(BaseAddress + i) = b;
}

在我看来,所有代码所做的就是将一个字节从原始内存复制到临时变量 b,然后将其写回原始内存。

从写入到内存位置的读取是否使内存有效且可以安全访问? 是否存在这样的情况,这实际上会捕获内存中的损坏?

最佳答案

它实际上验证了从BaseAddress开始的内存最多 BaseAddress+(sizeof(ElementType))*(Size-1)可以读取写入

例如,假设 BaseAddress 的内存有 protection attributes PAGE_EXECUTE_READ .

在这种情况下读取不会失败,而写入它会导致访问冲突(据我所知)。


但是正如已经指出的那样,我们只能猜测为什么有人需要它。

理想情况下,它不应该是必需的,假设应用程序的托管和非托管部分都正确地实现了它们之间的契约。而且,显然,假设契约是合理的(例如模块 A promise 不分配无缘无故不可读或不可写的缓冲内存)。


是否优化为空操作?

正如@AndrewMedico 所指出的

what will happen assume the compiler doesn't just optimize it out (the loop is logically a no-op)

我怀疑所有的 C\C++ 编译器都会优化类似的代码,但这里我们谈论的是 C#,所以有两件事需要考虑

  1. C# 编译器

假设如下代码

private unsafe static void Test(Int32* ptr, Int32 size)
{
    for (int i = 0; i < size; i++)
    {
        var a = * (ptr + i);
        *(ptr + i) = a;
    }
}

...

var array = new Int32[] { 1, 2, 3 };

fixed (Int32* ptr = array)
{
    Test(ptr, array.Length);
}

测试方法将变为:

.method private hidebysig static void  Test(int32* ptr,
                                            int32 size) cil managed
{
  // Code size       29 (0x1d)
  .maxstack  3
  .locals init ([0] int32 i,
           [1] int32 a)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_0018
  IL_0004:  ldarg.0
  IL_0005:  ldloc.0
  IL_0006:  conv.i
  IL_0007:  ldc.i4.4
  IL_0008:  mul
  IL_0009:  add
  IL_000a:  ldind.i4
  IL_000b:  stloc.1
  IL_000c:  ldarg.0
  IL_000d:  ldloc.0
  IL_000e:  conv.i
  IL_000f:  ldc.i4.4
  IL_0010:  mul
  IL_0011:  add
  IL_0012:  ldloc.1
  IL_0013:  stind.i4
  IL_0014:  ldloc.0
  IL_0015:  ldc.i4.1
  IL_0016:  add
  IL_0017:  stloc.0
  IL_0018:  ldloc.0
  IL_0019:  ldarg.1
  IL_001a:  blt.s      IL_0004
  IL_001c:  ret
} // end of method Program::Test

不是空操作

  1. 但是还有一个JIT 编译器。对于那个我不能保证,所以 @AndrewMedico 可能是对的 如果不是现在,那么对于某些假设的 future JIT 编译器实现。

但现在我已经在我自己的机器上使用 simplest available method 检查了 Release模式反汇编。并且似乎打开了优化

Debug settings - Just my code = Off, Suppress JIT optimization = Off

对于下面的代码:

private unsafe static void Test(Int32* ptr, Int32 size)
{
    Console.WriteLine("test");

    for (int i = 0; i < size; i++)
    {
        var a = * (ptr + i);
        *(ptr + i) = a;
    }
}

我们有

        Console.WriteLine("test");
00742E70  push        ebp  
00742E71  mov         ebp,esp  
00742E73  push        edi  
00742E74  push        esi  
00742E75  mov         esi,ecx  
00742E77  mov         edi,edx  
00742E79  mov         ecx,dword ptr ds:[27B2310h]  
00742E7F  call        65BDD4B0  

        for (int i = 0; i < size; i++)
00742E84  xor         edx,edx  
        for (int i = 0; i < size; i++)
00742E86  test        edi,edi  
00742E88  jle         00742E95  
        {
            var a = * (ptr + i);
00742E8A  mov         ecx,dword ptr [esi+edx*4]  
            *(ptr + i) = a;
00742E8D  mov         dword ptr [esi+edx*4],ecx  
        for (int i = 0; i < size; i++)
00742E90  inc         edx  
00742E91  cmp         edx,edi  
00742E93  jl          00742E8A  
00742E95  pop         esi  
00742E96  pop         edi  
00742E97  pop         ebp  
00742E98  ret  

所以它没有优化,但如果我删除 Console.WriteLine,我将无法在 Test 中命中断点。 .起初我虽然它得到了优化当该方法只做内存杂耍时,但它实际上只在 Main 中内联。 :

        Test(ptr, array.Length);
00482DFD  mov         ecx,dword ptr [ebp-20h]  
00482E00  mov         edi,dword ptr [edx+4]  
00482E03  xor         esi,esi  
00482E05  test        edi,edi  
00482E07  jle         00482E14  
00482E09  mov         edx,dword ptr [ecx+esi*4]  
00482E0C  mov         dword ptr [ecx+esi*4],edx  
00482E0F  inc         esi  
00482E10  cmp         esi,edi  
00482E12  jl          00482E09  
00482E14  mov         dword ptr [ebp-18h],0  
00482E1B  mov         dword ptr [ebp-14h],0FCh  
00482E22  push        482E36h  
00482E27  jmp         00482E2E  
00482E29  call        66DBBDC2  
00482E2E  xor         edx,edx  
00482E30  mov         dword ptr [ebp-20h],edx  
00482E33  pop         eax  
00482E34  jmp         eax  
00482E36  mov         dword ptr [ebp-14h],0  
00482E3D  jmp         00482E4B  

所以,TLDR:它按照我说的去做。至少在 VS2015、Windows 8.1 上的 .NET 4.6.1

关于c# - 读取和写入内存地址以验证不安全代码中的内存分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36677120/

相关文章:

c# - 使用 Unity IoC 时如何实现 Ninject .InSingletonScope()

c - 如何将链表中的节点分割成 8 字节的 block ?

c - 如何释放子线程中的内存?

fortran - 使用接口(interface)和可分配的 WORK 数组的 Lapack 例程 SGELS 中出现错误

c# - 尝试向我的 WCF 服务添加 IoC 支持

C# 查找列表是否包含重复 5 次的项目

c# - IQueryable<T> 上的通用扩展方法

C++ 调用析构函数并没有真正从内存中删除对象

xcode - Xcode 的调试导航器的工作方式与 Instruments 分配不同吗?

c++ - std::swap() 是如何实现的?