一个类型转换引起的Bug

        最近有个bug实在让人抓狂,仅仅是由于一个简单的类型强转导致的,这里给大家讲讲。

这是pwrite系统调用的接口说明,我们关注最后一个参数:
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
我们有个接口实现是:
int storage_write_meta_info(int fd, void *data, int unit)
{
    ...
    off_t     offset;
    int       length;
    ssize_t   n;

    lenght = sizeof(struct xxx);
    offset = unit * length;
    n = pwrite(fd, data, length, offset);
    ...
}
        运行环境是CentOS x86_64,off_t的实际类型是long,8字节。程序在跑了很长一段时间后,遇到pwrite返回了-1,errno为(22: Invalid argument)。有经验的同学可能已经看出了问题:
        当两个int类型相乘(即代码中的unit * length)发生溢出时,最后的结果已经产生,并截断为int类型保存在某处。这样这个被截断的错误值就赋给了offset,两者的相乘行为完全无视左值offset的类型。怎么处理好呢?只需要对其中任何一个相乘的变量做一个强转,即:
offset = (off_t)unit * length 或者 offset = unit * (off_t)length 或者 offset = (off_t)unit * (off_t)length

        不过之前的代码pwrite就没有报错(但不一定就是正确):
        offset = uint * sizeof(struct xxx);
        因为sizeof的返回值是size_t,它的真面目是unsigned int,这里相当于换成了int * unsigned int,运算的结果被看做是一个unsigned int类型,碰巧没有越界罢了。

        所以这里告诫大家,在程序里面涉及很多不同类型的参数之间转换的时候,一点要谨慎,小心。最好的建议就是别整那么多类型!

你可能感兴趣的:(一个类型转换引起的Bug)