引用malloc的字节超出了struct的大小

时间:2012-01-31 02:26:58

标签: c

我正在尝试使用超出结构大小分配的内存来模仿“有效负载” 并允许该有效负载包含指向另一个结构的指针。有人能告诉我这是否可能,或者我想做的事情是不可行的。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define ptrsize sizeof(char*)

typedef struct s{
  int i;
  short j;
  long k;
}S;

S *salloc(int sz,int i,short j,long k){
  S *m=malloc(sizeof(S)+sz);
  m->i=i;m->j=j;m->k=k;
  return m;
}

char *goToData(S *m){
  char* dataloc=(char*)m+sizeof(S);
  return dataloc;
}

int main(int argc,char **argv){

  char a[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
  S *mys=salloc(26,2,3,100);              // struct * to size 26+sizeof(S) & set struct vars
  char *mydp=goToData(mys);               // get the address of the payload 
  memcpy(mydp,a,sizeof(a));               // copy a into the payload

  S *mysc=salloc(ptrsize,1,2,3);          // allocate a container struct

  char *datapw=goToData(mysc);            // go to the first byte of the payload of mysc
  (*(S**)datapw)=mys;                     // want to point at mys -- is this possible?

  printf("%d\n",ptrsize);
  printf("addr mys              %x\n",(unsigned int)mys);
  printf("addr mysc             %x\n",(unsigned int)mysc);
  printf("addr mysc | *datapw   %x\n",(unsigned int)*datapw);  // from here would like to be indirectly reference mys

  return 0;

}

2 个答案:

答案 0 :(得分:1)

goToData()功能似乎是合理的。内存分配是合理的,但是您没有记录在结构之后分配的空间大小。

这里有一个缓冲区溢出:

char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
S *mys = salloc(26,2,3,100);              // struct * to size 26+sizeof(S) & set struct vars
char *mydp = goToData(mys);               // get the address of the payload 
memcpy(mydp, a, sizeof(a));               // copy a into the payload

您分配了26个字节,但是您要复制27个字段(sizeof(a) == 27,因为sizeof()计算结尾处的NUL '\0'。这是灾难的秘诀。不要在C中使用优雅的变化;使用一致性。在两个地方都使用26或sizeof(a),但不能混合使用。

如果出现问题,请使用以下行:

(*(S**)datapw)=mys;                     // want to point at mys -- is this possible?

我甚至不确定我明白你在这里要做什么,但它看起来并不好看。

虽然datapw已对齐以用作S *,但您尚未为其分配足够的空间。在解除引用之前,我不清楚你应该将它转换为S**

如果你试图在mysc指向的结构包含指向mys所指向的结构的指针后创建空格,那么你最好有一个很好的理由不包括指针在结构中。 真的,非常很好的理由。

然而,尽管我有很强烈的疑虑,但该代码仍然准确无误。但它非常不透明。

我认为你应该得到你想要的结果:

*((S **)datapw) = mys;

我讨厌考虑严格别名的含义,尽管你可能没问题,因为对char *有某种豁免。

所以,重新审视这个陈述并找出你想要做的事情 - 因为你所写的不是这样做的,无论它是什么。

printf("addr mysc | *datapw   %x\n",(unsigned int)*datapw);

这有一些问题。由于datapwchar **datapw将成为一个角色。可能不是你的想法。我认为(unsigned int)*(S **)是您所需要的。

剩下的就是OK-ish,尽管在64位系统上,地址对%x来说太大了。您将使用32位系统,但是您应该%p使用(void *)强制转换,或"%" PRIXPTR来自<inttypes.h> (uintptr_t)铸造。

我最终得到了:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#define ptrsize sizeof(char*)

typedef struct s
{
    int   i;
    short j;
    long  k;
} S;

static S *salloc(int sz, int i, short j, long k)
{
    S *m = malloc(sizeof(S) + sz);
    m->i = i;
    m->j = j;
    m->k = k;
    return m;
}

static char *goToData(S *m)
{
    char *dataloc = (char*)m + sizeof(S);
    return dataloc;
}

int main(void)
{
    char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    S *mys = salloc(sizeof(a), 2, 3, 100);     // struct * to size 26+sizeof(S) & set struct vars
    char *mydp = goToData(mys);                // get the address of the payload 
    memcpy(mydp, a, sizeof(a));                // copy a into the payload
    S *mysc = salloc(ptrsize, 1, 2, 3);        // allocate a container struct
    char *datapw  = goToData(mysc);            // go to the first byte of the payload of mysc
    *((S **)datapw) = mys;                     // want to point at mys -- is this possible?

    printf("%zu\n", ptrsize);
    printf("addr mys              %" PRIXPTR "\n", (uintptr_t)mys);
    printf("addr mysc             %" PRIXPTR "\n", (uintptr_t)mysc);
    printf("addr mysc | *datapw   %" PRIXPTR "\n", (uintptr_t)*(S **)datapw);

    return 0;
}

(我用-Wmissing-prototypes编译;函数前面的static阻止编译器警告我这些函数。由于你没有使用main()的参数,我更换了出于同样的原因,argcargvvoid - 以避免编译器警告。)

当我在Mac上的valgrind下运行它(MacOS X 10.7.2,GCC 4.2.1,Valgrind 3.7.0)时,我得到了一个干净的运行数据输出:

8
addr mys              100005120
addr mysc             100005190
addr mysc | *datapw   100005120

答案 1 :(得分:1)

关于接受的答案,我会将结构更改为:

typedef struct s{
  int i;
  short j;
  long k;
   char data[1]; //<- start of optional data
}S;

static S *salloc(int sz, int i, short j, long k)
{
    S *m = malloc((sizeof(S)-1) + sz );
    m->i = i;
    m->j = j;
    m->k = k;
    return m;
}

然后你不需要goToData函数,因为S已经有一个指向它的指针。

int main(void)
{
  char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
  S *mys = salloc(sizeof(a), 2, 3, 100);     // struct * to size 26+sizeof(S) & set struct vars
   memcpy(&mys->data, a, sizeof(a));                // copy a into the payload
  S *mysc = salloc(ptrsize, 1, 2, 3);        // allocate a container struct
  char *datapw  = &mysc->data;            // go to the first byte of the payload of mysc
  *((S **)datapw) = mys;                     // want to point at mys -- is this possible?

  printf("%zu\n", ptrsize);
  printf("addr mys              %" PRIXPTR "\n", (uintptr_t)mys);
  printf("addr mysc             %" PRIXPTR "\n", (uintptr_t)mysc);
  printf("addr mysc | *datapw   %" PRIXPTR "\n", (uintptr_t)*(S **)datapw);

  return 0;
} 

我记得一个非常古老的Microsoft DOS编译器支持:

typedef struct s{
  int i;
  short j;
  long k;
   char data[0]; //<- start of optional data
}S;

这意味着sizeof(S)不包含可选数据,但是为您提供了指向数据开头的指针供您使用。