【问题标题】:Why do we need an unused char array for a successfull bind?为什么我们需要一个未使用的 char 数组才能成功绑定?
【发布时间】:2018-01-31 17:36:57
【问题描述】:

我们目前正在开发一种小型工具,用于读取 CAN 消息并评估包含的数据。当 char 数组 empty 太短、太长或不存在时,bind() 命令不会返回 0(对于成功的绑定)。这个错误出现在下面的缩短程序中。

#include <sys/socket.h>     // socket, bind, PF_CAN, SOCK_RAW
#include <linux/if.h>       // ifreq
#include <linux/can.h>      // sockaddr_can, CAN_RAW
#include <stdio.h>          // printf

int main()
{
    struct sockaddr_can addr;
    struct ifreq ifr;
    int socketFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    addr.can_ifindex = ifr.ifr_ifindex;

    char empty[5];     // change length or comment out to provoke the error

    if(bind(socketFd, (struct sockaddr *) &addr, sizeof(addr)) != 0)
    {
        printf("Unable to bind the CAN-socket.\n");
    }
}

行为随着我们的 char 数组 empty 的长度而变化。 例如[5] 有效,而[24] 无效。 我们使用 GCC 5.4.0 和 7.2.0 对此进行了测试,它们都显示了这种行为。 在 GCC 5.3.0 下,empty 的存在和长度不会影响我们的 bind()

供参考

gcc main.c
./a.out

为什么我们需要 GCC 5.4.0 和 7.2.0 的未使用 char 数组才能成功绑定?

【问题讨论】:

  • addr.can_ifindex = ifr.ifr_ifindex; 是未定义的行为。你从未初始化过ifr_ifindex
  • 这种事情可能未定义行为的症状,虽然我不知道是什么。
  • 应该确保所有addr 都被正确填充。
  • 您应该以memset(&amp;addr, 0, sizeof addr); memset(&amp;ifr, 0, sizeof ifr); 开头,然后将ifr_ifr_ifindex 设置为一个有意义的值,然后再将其复制到addr.can_ifindex。 (这个精简程序似乎根本不需要ifr,但我想它在实际程序中提供了一些功能。)
  • 使用 memset 初始化 addrifr 解决了我们的问题。我们将ifr.ifr_name 设置为我们的设备名称,将addr.can_family 设置为AF_CAN,但这已被删除,因为它不会影响行为。我们的ioctl 也需要ifr,它也被剪掉了。

标签: c linux sockets bind can-bus


【解决方案1】:

由于使用了未初始化的变量,您正在导致各种未定义的行为。

你永远不会初始化ifr,然后你使用ifr.ifr_ifindex

您永远不会初始化addr.tp,然后将addr 作为sockaddr 参数传递给bind()

它曾经成功完全比失败取决于empty 数组的大小这一事实更令人惊讶。您希望套接字绑定到哪个地址?

【讨论】:

  • 因为它只是一个CAN socket,所以只需要AF_CAN的地址族和对应接口的ifindex(或者所有接口都是'0')。
  • @MarcusS 是的,但是哪个界面?
【解决方案2】:

我们遵循了kernel.org 上的文档。

在“4. 如何使用 SocketCAN”(大约第 60 行)中给出了这个例子:

int s;
struct sockaddr_can addr;
struct ifreq ifr;

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);

strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);

addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

bind(s, (struct sockaddr *)&addr, sizeof(addr));

(..)

虽然它声明没有实施错误检查,但 addrifr 都没有完全初始化(就像你们在这里提到的所有人一样)。因此,给定的示例遇到了与我们的程序相同的问题。 使用 memset 初始化它们,就像提到的 @zwol 一样,以确保在使用它们之前将所有值设置为 0,从而完全修复这种意外行为。

谢谢大家。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    相关资源
    最近更新 更多