【问题标题】:Does NULL-Pointer Assignment Partition has a bug?NULL 指针分配分区有错误吗?
【发布时间】:2014-07-13 22:56:52
【问题描述】:

从规格中我们知道

每个进程的虚拟地址空间被分割成多个分区。在 x86 32 位 Windows 上,0x00000000 - 0x0000FFFF(含)的分区称为 NULL-Pointer Assignment Partition。这个分区被留出来帮助程序员捕捉 NULL 指针赋值。如果您的进程中的线程尝试读取或写入此分区中的内存地址,则会引发访问冲突。

如果我们要创建一个具有 64*1024+1 字节字段的假设对象,例如

struct Foo {
    byte field1;
    byte field2;
    ...
    byte field65536;

    byte SomeUnexpectedData;
}* Bar = 0;

Bar->SomeUnexpectedData = 0;

所以这里我们使用了未初始化的空指针,那么环境如何避免它呢?我们不能创建大于 64K 的对象还是什么?


C# 测试

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace BigObjTest
{
    class Program
    {
        static void Main()
        {
            var type = GetObjType();
            var obj = Activator.CreateInstance(type);
            Console.WriteLine(obj);
        }

        private static Type GetObjType()
        {
            var typebuilder = GetTypeBuilder();
            for (int i = 0; i <= 65535; i++)
            {
                typebuilder.DefineField("_" + ("b" + i), typeof (byte), FieldAttributes.Private);
            }
            return typebuilder.CreateType();
        }

        private static TypeBuilder GetTypeBuilder()
        {
            const string typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , null);
            return tb;
        }
    }
}

奇怪的是,如果对象大于 64K,CLR 会抛出异常,但如果写入 65534 而不是 65535,它会正常工作。所以我猜,在具有强类型系统的托管语言中这是不可能的。

【问题讨论】:

  • 这种机制并非万无一失,只是为了在大多数情况下提供帮助。
  • 不,它只是意味着指针值本身(地址)总是大于64K。
  • 这也不是一个未初始化的指针。您显然将其初始化为0。一个真正的 indeterminate 指针将万无一失,无论您的对象有多大/多小,NULL 指针分配分区的目的都是完全不可靠的。

标签: c# c++ windows pointers memory


【解决方案1】:

访问空指针会导致未定义的行为。无法保证系统会检测到和/或处理此问题。使用受保护的内存页面来捕获大多数错误似乎仍然是一种合理的方法。

您当然可以拥有大于 64kB 的对象。不过,您可能不会在使用它们时发现一些小错误。

【讨论】:

    【解决方案2】:

    我认为你错过了分区的重点。如果一个指针被错误分配:

    int *p = NULL;
    *p = 4;
    

    然后系统可以捕获它,而不是让它做一些可怕的事情。 64K 限制意味着它可以捕获高达 64K 的值。因为:

    p = 56678455;   
    

    可能是一个“有效”的地址区域,但仍然是错误的分配。它无法检查所有内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-02
      • 1970-01-01
      • 2021-04-10
      • 1970-01-01
      • 2020-02-18
      • 1970-01-01
      • 2011-01-26
      • 2011-07-23
      相关资源
      最近更新 更多