分配太少的空间是否安全(如果你知道你不需要它)?

Chr*_*utz 14 c malloc flexible-array-member

所以C99祝福了常用的"灵活阵列成员"黑客,让我们struct可以根据我们的尺寸要求进行过度分配.我怀疑在大多数理智的实现中这样做是完全安全的,但是如果我们知道在某些情况下我们不需要某些成员,那么它是否合法"无法定位" struct

抽象例子

说我有一个类型:

struct a {
  bool   data_is_x;
  void * data;
  size_t pos;
};
Run Code Online (Sandbox Code Playgroud)

如果data_is_x,那么类型data是需要使用该pos成员的类型.否则,使用它的函数struct将不需要pos该特定副本的成员struct.从本质上讲,它struct带有关于它是否有pos成员的信息,并且这些信息在struct一生中不会被改变(除了邪恶的恶作剧之外,无论如何都会破坏任何东西).说:安全吗

struct a *a = malloc(data_is_x ? sizeof(struct a) : offsetof(struct a, pos));
Run Code Online (Sandbox Code Playgroud)

pos只有在需要时才会为会员分配空间?或者它是否违反约束使用对struct指针而言太小的投射空间,即使您从未使用过相关成员?

具体例子

我的实际用例有点涉及; 它主要是因为你可以理解我为什么要这样做:

typedef struct {
  size_t size;
  void * data;
  size_t pos;
} mylist;
Run Code Online (Sandbox Code Playgroud)

代码mylist_create指定for size > 0,data是一个连续数据的数组,它是size项目长(无论项目可能是什么),但是size == 0它是包含项目的双向链表的当前节点.与mylists 一起使用的所有函数都将检查是否size == 0.如果是这样,他们将把数据作为链表进行处理,其中"当前"索引是data指向哪个节点.如果没有,他们将把数据作为一个存储有"当前"索引的数组来处理pos.

现在,如果size == 0我们真的不需要pos会员,但如果size > 0我们愿意的话.所以我的问题是,这样做是否合法:

mylist *list = malloc(size ? sizeof(mylist) : offsetof(mylist, pos));
Run Code Online (Sandbox Code Playgroud)

如果我们保证(对未定义行为的惩罚)那么,size == 0我们永远不会尝试(或需要)访问该pos成员?或者它是否在标准的某个地方说UB甚至想到这样做?

pax*_*blo 4

malloc它本身根本不关心为结构分配多少内存,而是对未定义的块外部的内存进行取消引用。从 C99 开始6.5.3.2 Address and indirection operators

如果为指针分配了无效值,则一元 * 运算符的行为未定义。

并且,从 中7.20.3 Memory management functions,我们发现(我的斜体):

如果分配成功则返回的指针被适当对齐,以便它可以分配给指向任何类型对象的指针,然后用于访问分配的空间中的此类对象或此类对象的数组(直到空间被显式释放) 。

因此,您可以执行以下操作:

typedef struct { char ch[100]; } ch100;
ch100 *c = malloc (1);
Run Code Online (Sandbox Code Playgroud)

而且,只要您只尝试使用 做任何事情c->ch[0],这是完全可以接受的。


对于您的具体示例,我不太确定我会那么担心,假设您担心的是存储空间。如果您出于其他原因而担心,请随意忽略这一点,特别是因为其中包含的假设不是标准所强制要求的。

根据我的理解,你有一个结构:

typedef struct {
  size_t size;
  void * data;
  size_t pos;
} mylist;
Run Code Online (Sandbox Code Playgroud)

where 您只想使用datawheresize为 0,并且dataandpossize大于 0。这排除了在并集中使用dataand的情况。pos

malloc为了缓解内存碎片问题,大量实现会将您请求的空间四舍五入为 16 字节的倍数(或 2 的更大幂)。当然,这不是标准所要求的,但它很常见。

假设(例如)32 位指针 和size_t,您的 12 个字节的结构很可能会占用 16 字节的 arena header 和 16 字节的数据块。即使您只要求 8 个字节(即没有pos),该块仍将是 16 个字节。

如果您有 64 位指针和size_t类型,则可能会有所不同 - 有 24 个字节pos,没有 16 个字节。

但即便如此,除非您分配大量这些结构,否则这可能不是问题。

  • 另外,我认为 paxdiablo 的示例(带有数组)和您的示例(带有结构)的答案可能会有所不同。当然,从“道德”的角度来看,用结构体进行这种黑客攻击似乎更具攻击性。 (2认同)