柔性数组越界导致的启动失败

为了在系统启动的时候能够自动加载framebuffer的驱动,我选择将驱动编译进内核里面,但编译进内核后系统停在了加载驱动之后,驱动看起来可以正常工作但是系统启动却失败了。

查找原因

查找原因过程非常的没有技术含量==纯粹是删减代码之后定位到了语句,下次还是用sysrq key的方法试试看。
不过总算还是找到了问题所在。
在初始化DMA的时候用到了一个结构体dma_interleaved_template,主要对他的成员sgl赋值,系统就没法正常启动。
那么来看一下这个结构体的定义:

struct dma_interleaved_template {
    dma_addr_t src_start;
    dma_addr_t dst_start;
    enum dma_transfer_direction dir;
    bool src_inc;
    bool dst_inc;
    bool src_sgl;
    bool dst_sgl;
    size_t numf;
    size_t frame_size;
    struct data_chunk sgl[0];
};

这里很容易发现,sgl其实是个柔性数组(也叫零长度数组、灵活数组)。

柔性数组

柔性数组是C99中引入的一个语言特性,结构体的最后一个成员可以是一个未指定长度的数组类型(根据编译器的不同也可以是长度为0的数组)。这里所谓的柔性就是由这个数组来表现的,由于长度未定,所以可以根据自己的需要调整整个结构体的长度,以改变最后数组成员的长度。和直接定义一个指针相比,当不需要使用最后一个成员的时候,直接申请长度等于结构体长度的内存,又不会导致内存的浪费,并且不需要申请两次内存(结构体一次,数组一次),减小了开发人员的负担。
这里引用wikipedia上的一个例子:

struct vectord {
    size_t len;
    double arr[]; // the flexible array member must be last
};
struct vectord *allocate_vectord (size_t len) {
   struct vectord *vec = malloc(offsetof(struct vectord, arr) + len * sizeof(vec->arr[0]));

   if (!vec) {
       perror("malloc vectord failed");
       exit(EXIT_FAILURE);
   }

   vec->len = len;

   for (size_t i = 0; i < len; i++)
       vec->arr[i] = 0;

   return vec;
}

函数的参数len表明了所需的数组长度,通过改变这个参数就可以实现所谓的柔性了。

解决方法

了解完这个特性之后来看看我们自己的用法,首先是这个结构体变量的定义。

struct dma_interleaved_template dma_template;

这么看来问题就很明显了,直接定义这样一个变量的话,他的内存空间是不包含最后一个数组的,然而我又对最后一个数组进行了操作,那么内存就越界了啊!!
既然内存都越界了==那么出现什么问题大概都不觉得奇怪了。修改方法也很简单,参照上面的例程,使用kmalloc申请包含所需数组长度的内存空间即可,这里就不贴上代码了。
顺便吐槽一下xilinx官方提供的vdmatest驱动也是这么写的,所以也有越界的问题,路径是linux-xlnx/drivers/dma/xilinx/vdmatest.c,贴上部分代码:

定义:

static struct dma_interleaved_template xt;

赋值:

xt.dst_start = dma_dsts[i];
            xt.dir = DMA_DEV_TO_MEM;
            xt.numf = vsize;
            xt.sgl[0].size = hsize;
            xt.sgl[0].icg = 0;
            xt.frame_size = 1;
            rxd = rx_dev->device_prep_interleaved_dma(rx_chan,
                                  &xt, flags);
            rx_cookie = rxd->tx_submit(rxd);

总结时间

这次主要是了解一下柔性数组的使用,另外内存越界真的是非常难以查找的问题,问题表现没有太多的规律,还是需要想办法借助一些更有效的工具。

点赞
  1. liangzr说道:

    :exclaim: cgnb(森森破音)

发表评论

电子邮件地址不会被公开。 必填项已用*标注