验证内存是否已在C中初始化

时间:2009-01-24 07:23:42

标签: c memory-management

我编写了一个API,需要初始化上下文,然后传递给每个API调用。调用者为上下文分配内存,然后使用其他参数将其传递给init函数,这些参数描述了他们希望以后的API调用的行为方式。上下文是不透明的,所以客户端不能真正在那里捣乱;它仅用于API函数的内部使用。

我遇到的问题是调用者正在分配上下文,但没有初始化它。因此,后续的API函数将无意义的垃圾称为真实的上下文。

我正在寻找一种方法来验证传递给API函数的上下文是否已实际初始化。我不确定这是否可行。我想到的两个想法是:

  1. 使用预定义的常量并将其存储在上下文的“魔术”字段中,以便在API调用时进行验证。
  2. 使用上下文内容的校验和,将其存储在“magic”字段中并在调用时进行验证。
  3. 不幸的是,我知道这些选项中的任何一个都可能导致误报验证,因为内存中的随机垃圾与“魔术”数字相匹配,或者因为上下文恰好占用了与之前初始化的上下文相同的空间。我认为后一种情况更有可能。

    这简单归结为概率问题吗?在大多数情况下,我可以避免误报,但不是全部?是否值得使用一个只给我一个合理的准确概率的系统,或者这只会让调试其他问题变得更加困难?

6 个答案:

答案 0 :(得分:6)

我认为,最佳解决方案是将create()/ delete()函数添加到API中,并使用create来分配和初始化结构。您可以在结构的开头放置一个签名,以验证您传递的指针指向使用create()分配的内存,并在释放内存之前使用delete()覆盖签名(或整个缓冲区)。

你实际上无法避免C中的误报,因为调用者malloc'd内存“发生”从你的签名开始;但让你的签名合理地长(比如8个字节)并且赔率很低。通过提供create()函数从调用者手中分配将会有很长的路要走。

而且,是的,你最大的风险是没有使用delete()就可以释放初始化的缓冲区,并且后续的malloc会重复使用该内存块。

答案 1 :(得分:3)

您的上下文变量可能是某种指向已分配内存的指针。而不是这个,使它成为可以明确验证的标记或句柄。每次初始化上下文时,都会返回一个新标记(而不是实际的上下文对象)并将该标记存储在内部列表中。然后,当客户端稍后为您提供上下文时,您可以通过查看列表来检查它是否有效。如果是,则可以将令牌转换为实际上下文并使用,否则返回错误。

typedef Context long;

typedef std::map<Context, InternalContext> Contexts;
Contexts _contexts;

Context nextContext()
{
  static Context next=0;
  return next++;
}

Context initialise()
{
  Context c=nextContext();
  _contexts.insert(make_pair(c, new InternalContext));
  return c;
}

void doSomethingWithContext(Context c)
{
  Contexts::iterator it=_ _contexts.find(c);
  if (it==_contexts.end())
    throw "invalid context";
  // otherwise do stuff with the valid context variable
  InternalContext *internalContext=*it.second;
}

使用此方法,不存在内存访问无效的风险,因为您只能正确使用有效的上下文引用。

答案 2 :(得分:3)

请看马特·毕晓普关于Robust Programming的论文。使用票证或令牌(在某些方面类似于文件句柄,但也包括一次使用的随机数)允许库代码确保它使用的令牌有效。实际上,您代表用户分配数据结构,并将每个调用您必须提供的票据传回给用户。

我有一些密切关注该系统的代码。标题包含注释:

/*
** Based on the tickets in qlib.c by Matt Bishop (bishop@ucdavis.edu) in
** Robust Programming.  Google terms: Bishop Robust Nonce.
** http://nob.cs.ucdavis.edu/~bishop/secprog/robust.pdf
** http://nob.cs.ucdavis.edu/classes/ecs153-1998-04/robust.html
*/

我还建立了一个基于竞技场的内存分配系统,使用门票来识别不同的竞技场。

答案 3 :(得分:1)

您可以定义一个新的API调用,该调用将获取未初始化的内存并以您需要的任何方式对其进行初始化。然后,客户端API的一部分是客户端必须调用上下文初始化函数,否则将导致未定义的行为。

答案 4 :(得分:0)

为了回避重用上一个上下文的内存位置的问题,除了释放上下文之外,您还可以重置它并删除“魔术”数字,当然假设用户使用您的API释放上下文。这样,当系统为下一个上下文请求返回相同的内存块时,幻数检查将失败。

答案 5 :(得分:0)

查看您的系统对未初始化的内存执行的操作。 m $:Uninitialized memory blocks in VC++