如何修复未对齐的指针(char **)

时间:2013-09-16 06:18:35

标签: c++ macos pointers alignment

Clang 3.3 UBSan(未定义的行为清理程序)标记以下代码以进行未对齐访问:

Address.cc:545:27: runtime error: load of misaligned address 0x60c00000a7df for type 'char *', which requires 8 byte alignment
0x60c00000a7df: note: pointer points here
 00 00 00 00 ef  a7 00 00 c0 60 00 00 00  00 00 00 00 00 00 00 c0  a8 64 0c 00 00 00 00 00  00 00 00
             ^
Address.cc:547:19: runtime error: reference binding to misaligned address 0x60c00000a7ef for type 'const struct in_addr', which requires 4 byte alignment
0x60c00000a7ef: note: pointer points here
 00 00 00 00 c0  a8 64 0c 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00
             ^

有问题的代码如下所示(忽略operator=的错误返回类型):

bool
Ip::Address::operator =(const struct hostent &s)
{
    struct in_addr* ipv4 = NULL;

    switch (s.h_addrtype) {

    case AF_INET:
        // Line 545 below
        ipv4 = (in_addr*)(s.h_addr_list[0]);
        // Line 547 below
        operator=(*ipv4);
        break;
    ...
}

hostent结构:

struct hostent {
  char    *h_name;        /* official name of host */
  char    **h_aliases;    /* alias list */
  int     h_addrtype;     /* host address type */
  int     h_length;       /* length of address */
  char    **h_addr_list;  /* list of addresses */
}

这个嘎嘎声是在Mac OS X系统上产生的,我记得最近有关于这是Xcode或CFE Dev邮件列表中的错误的讨论(但我目前无法找到它)。

编辑:Sean McBride非常友好地提供了电子邮件的链接:-fcatch-undefined-behavior false positive with readdir()?我不同意可以忽略的声明(因为它来自操作系统,而不是应用程序),尤其是因为它可能导致SIGBUS错误。

如何重新调整指针以清除Clang问题(h_addr给我带来的麻烦)?由于Apple的内部结构取决于它,它甚至可以完成吗?

3 个答案:

答案 0 :(得分:3)

如果您有一个指向数据的指针,该数据包含正确的值,但偏移量不正确,则可能需要memcpy该数据。在您的情况下,将ipv4转换为堆栈变量(将对齐),并memcpy s.h_addr_list[0]转换为ipv4

答案 1 :(得分:2)

这是我最终在Mac OS X 10.8.3上修复两个未对齐指针的方法。 (为了完整起见,Ubuntu 13(x64)上没有这些问题。

void DoSomethingWithHostent(const hostent& he)
{
  // Mac OS X work around 1. Align hostent's h_addr.
  char** ip_addr;
  const void* ptr = (void*)&s.h_addr;
  memcpy(&ip_addr, ptr, sizeof(void*));

  // Mac OS X work around 2. Align in_addr's via memcpy below.
  typedef struct {
    union {
        in_addr ipv4;
        in6_addr ipv6;
    };
  } IN_ADDR_4or6;

  IN_ADDR_4or6 ipv4or6;

  // Back to business logic
  switch (s.h_addrtype) {

  case AF_INET:
    memcpy(&ipv4or6.ipv4, ip_addr, sizeof(ipv4or6.ipv4));
    DoSomethingWithInAddr(ipv4or6.ipv4);
    break;
  ...
  }
}

答案 2 :(得分:0)

@ jww的修复对C ++很有用,但是,对于C,需要稍加修改:将'in_addr'更改为'struct in_addr'。

我必须移植类似的代码,为了比较,这是之前的(使用未对齐的内存访问):

address.sin_addr.s_addr = ((struct in_addr *)(host->h_addr_list[0]))->s_addr;

之后,使用memcpy进行正确的内存对齐:

char **ip_addr;
memcpy(&ip_addr, &(host->h_addr_list[0]), sizeof(void *));
memcpy(&address.sin_addr.s_addr, ip_addr, sizeof(struct in_addr));