C语言 从共享库公开指针时的安全注意事项

aurhwmvo  于 2023-04-19  发布在  其他
关注(0)|答案(1)|浏览(115)

假设我有一个C库,它实现了以下函数:

// Returns the number of elements in the array,
// and a pointer to the first element.
// The memory pointed to has static lifetime.
size_t MyLib_GetValues(const int** outBasePtr);

我是这样用的:

const int* values = NULL;
size_t count = MyLib_GetValues(&values);

if ( values )
{
  for ( size_t index = 0; index < count; ++index )
  {
    DoSomething(values + index);
  }
}

我对这种方法有几个问题,因为没有任何东西可以阻止库的用户遍历数组的末尾。

  • 我对系统如何处理库不太了解,不知道values[count]是否会被捕获为无效的内存访问,或者用户是否只能读取数组的末尾并从库内存的其他地方获取值。
  • 如果这会被认为是一个安全问题,那么更好的方法是什么?库是否应该将值复制到用户提供的缓冲区中?另外,这是否意味着直接将字符串暴露为const char*也会是一个安全问题,如果你想的话,你也可以读取这些字符串的结尾?

我的直觉是,这种方法是否应该被视为一个安全问题将取决于库的预期用例。鉴于库的用户可以轻松地查看十六进制编辑器中的内容,无论如何都没有什么可以阻止他们这样做。如果你试图将内容隐藏在内存中,那么在这种情况下,游戏就结束了。因此API设计模式实际上没有任何区别。然而,在更受限制的情况下,用户只能基于代码访问库(例如,通过提供一些在运行时链接到库的程序),情况可能会有所不同。
从C库设计的Angular 来看,这类事情是否有最佳实践?

dhxwm5r4

dhxwm5r41#

是的,允许库用户迭代超过数组的末尾可以被视为安全问题,因为它可能导致未定义的行为,包括内存损坏、分段错误和其他可能被攻击者利用的错误。根据平台和体系结构,此类错误可能会也可能不会被捕获为无效内存访问。
一个更好的方法是提供一个用户提供的已知大小的缓冲区,库可以安全地复制数组元素。这种方法确保库停留在缓冲区的边界内,防止用户迭代超过数组的末尾。或者,库可以返回一个动态分配的缓冲区,用户负责在使用后释放。但是,后一种方法需要用户方面更仔细的存储器管理,并且可能效率较低。
关于const char* strings,同样的问题也适用,通常建议为字符串数据提供用户提供的缓冲区或动态分配的缓冲区,而不是直接暴露内部内存。
安全风险的程度取决于库的预期用例,这一点是正确的。如果在用户对内存具有不受限制的访问权限的环境中使用库,则实施防止迭代超过数组或字符串结尾的保护措施的好处可能有限。但是,在其他环境中,例如在沙箱或受约束的环境中使用库时,或者当它被用作具有安全要求的较大系统的一部分时,这样的保护措施可能是关键的。

C库设计的最佳实践通常包括:明确定义API合约,包括函数签名、预期的输入和输出以及边缘情况下的行为。避免直接向用户暴露内部数据结构或实现细节,以保持封装性和灵活性。提供安全的默认值和合理的错误处理机制,以防止意外行为并促进调试。使用适当的内存管理技术,例如提供用户提供的缓冲区或动态分配存储器,以及清楚地指定释放存储器的所有权和责任。

相关问题