【问题标题】:How to fix unaligned pointer (char**)如何修复未对齐的指针(char**)
【发布时间】:2013-09-20 05:13:32
【问题描述】:

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 */
}

这个 squawk 是在 Mac OS X 系统上产生的,我记得最近讨论过这是 Xcode 或 CFE Dev 邮件列表上的一个错误(但我现在找不到它)。

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

如何重新对齐指针以清除 Clang 问题(h_addr 给我带来了最大的麻烦)?由于苹果的内部结构取决于它,它甚至可以做到吗?

【问题讨论】:

  • 你来自 C 语言,对吧?在 C++ 中没有必要指定一个元素是一个结构:const struct hostent &s 应该是 const hostent &s
  • 另外,赋值运算符的正确行为是返回对对象的引用
  • 感谢 Manu343726。上面的代码是存在的。不知道它在那里多久了。我担心的是未对齐访问的运行时错误,而不是语言问题。
  • 如果它实际上没有触发 SIGBUS,您不必担心 SIGBUS,实际上在 x64 和类似 CPU 上它不会触发任何 SIGBUS。 IE。您确实可以忽略 UBSan 运行时警告。如果您移植到可能导致 SIGBUS 的平台,那么该平台上的相关 libc 函数可能不会生成具有此类对齐问题的 struct hostent 对象,否则访问它们的任何东西都会崩溃。

标签: c++ macos pointers alignment


【解决方案1】:

@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));

【讨论】:

    【解决方案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;
      ...
      }
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 感谢 MSalters。本质上,s.h_addr 是一个char**。但是我不能在没有总线错误的情况下取消引用它。我确实尝试过memcpy,但我仍然收到未对齐访问的错误。因此,memcpy 删除了一个未对齐的访问,但不能同时删除两者。在operator= 通话期间,剩下的似乎是尊重。 (而operator= 返回的不是我现在愿意战斗的战斗,因为它不会产生运行时错误;)
      • 对齐错误不是来自memcpy。那样不行。
      猜你喜欢
      • 2015-11-10
      • 2017-09-04
      • 1970-01-01
      • 2010-10-07
      • 1970-01-01
      • 2020-01-16
      • 2017-12-23
      • 2018-04-05
      • 1970-01-01
      相关资源
      最近更新 更多